aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorPaul Eggert2011-02-14 21:41:07 -0800
committerPaul Eggert2011-02-14 21:41:07 -0800
commitfae95934b8edae3f538063e756ac799ed94313b2 (patch)
tree3bb814c43cd50db54591bf685e5cb72b863b5833 /lisp
parent6d302144c218f12bd380344ae2d3ed87a6ea9327 (diff)
parentbb55f713d2e4ea089a861a257d7d000432642ce9 (diff)
downloademacs-fae95934b8edae3f538063e756ac799ed94313b2.tar.gz
emacs-fae95934b8edae3f538063e756ac799ed94313b2.zip
Merge from mainline.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/ChangeLog153
-rw-r--r--lisp/Makefile.in8
-rw-r--r--lisp/dired-aux.el4
-rw-r--r--lisp/dired-x.el168
-rw-r--r--lisp/dired.el4
-rw-r--r--lisp/emacs-lisp/checkdoc.el2
-rw-r--r--lisp/emacs-lisp/cl-loaddefs.el18
-rw-r--r--lisp/emacs-lisp/cl-seq.el16
-rw-r--r--lisp/emulation/cua-base.el2
-rw-r--r--lisp/faces.el20
-rw-r--r--lisp/gnus/ChangeLog141
-rw-r--r--lisp/gnus/ChangeLog.22
-rw-r--r--lisp/gnus/auth-source.el1234
-rw-r--r--lisp/gnus/gnus-art.el7
-rw-r--r--lisp/gnus/gnus-delay.el4
-rw-r--r--lisp/gnus/mail-source.el88
-rw-r--r--lisp/gnus/nnimap.el66
-rw-r--r--lisp/gnus/nntp.el16
-rw-r--r--lisp/gnus/sieve-manage.el18
-rw-r--r--lisp/makefile.w32-in6
-rw-r--r--lisp/mh-e/ChangeLog.12
-rw-r--r--lisp/net/imap-hash.el374
-rw-r--r--lisp/net/tramp-imap.el850
-rw-r--r--lisp/net/tramp.el15
-rw-r--r--lisp/password-cache.el7
-rw-r--r--lisp/progmodes/cc-engine.el88
-rw-r--r--lisp/progmodes/cc-fonts.el11
-rw-r--r--lisp/simple.el4
-rw-r--r--lisp/textmodes/reftex-index.el2
-rw-r--r--lisp/vc/ediff-mult.el2
-rw-r--r--lisp/vc/ediff-ptch.el2
-rw-r--r--lisp/vc/log-view.el198
-rw-r--r--lisp/vc/vc-bzr.el16
-rw-r--r--lisp/vc/vc-git.el101
-rw-r--r--lisp/vc/vc-hg.el80
-rw-r--r--lisp/vc/vc.el30
36 files changed, 1748 insertions, 2011 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 7cd39ae6d4b..742cbfc9267 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,111 @@
12011-02-14 Chong Yidong <cyd@stupidchicken.com>
2
3 * vc/vc-git.el (vc-git-root-log-format): New option for
4 customizing log format.
5 (vc-git-print-log, vc-git-log-outgoing, vc-git-log-incoming)
6 (vc-git-log-view-mode): Use it.
7 (vc-git-expanded-log-entry): New function.
8 (vc-git-log-view-mode): Use it. Truncate lines in root log.
9
10 * vc/vc-hg.el (vc-hg-root-log-template): New option for
11 customizing log format.
12 (vc-hg-print-log): Use it.
13 (vc-hg-expanded-log-entry): New function.
14 (vc-hg-log-view-mode): Use vc-hg-root-log-template and
15 vc-hg-expanded-log-entry. Truncate lines in root log.
16
17 * vc/vc-bzr.el (vc-bzr-log-view-mode): Truncate lines in root log.
18
19 * vc/log-view.el (log-view-mode-menu): Add
20 log-view-toggle-entry-display.
21
222011-02-14 Glenn Morris <rgm@gnu.org>
23
24 * dired-x.el: Don't require man when compiling.
25 (dired-omit-extensions, dired-local-variables-file)
26 (dired-x-hands-off-my-keys): Make them defcustoms.
27 (Man-support-local-filenames, Man-getpage-in-background): Declare.
28 (vm-visit-folder): Declare rather than defining.
29 (dired-x-help-address, dired-x-variable-list): Remove.
30 (dired-x-submit-report): Make it an obsolete alias.
31
322011-02-14 Juanma Barranquero <lekktu@gmail.com>
33
34 * makefile.w32-in (TRAMP_SRC): Remove tramp-imap.el.
35
362011-02-13 Teodor Zlatanov <tzz@lifelogs.com>
37
38 * net/imap.el: Bring it back.
39
402011-02-13 Alan Mackenzie <acm@muc.de>
41
42 * progmodes/cc-fonts.el (c-font-lock-declarations): Remove a
43 narrow-to-region call that cuts context off the end (Bug#7722).
44
45 * progmodes/cc-engine.el (c-forward-<>-arglist-recur): Refactor
46 nested if-forms with a simple cond.
47 (c-forward-<>-arglist): Revert 2011-01-31 change.
48
492011-02-13 Chong Yidong <cyd@stupidchicken.com>
50
51 * vc/log-view.el: New command log-view-toggle-entry-display for
52 toggling log entries between concise and detailed forms.
53 (log-view-toggle-entry-display): New command.
54 (log-view-mode-map): Bind RET to it.
55 (log-view-expanded-log-entry-function): New variable.
56 (log-view-current-entry, log-view-inside-comment-p)
57 (log-view-current-tag): New functions.
58 (log-view-toggle-mark-entry): Use log-view-current-entry and
59 log-view-end-of-defun instead of searching directly with
60 log-view-message-re.
61 (log-view-end-of-defun): Likewise. Add optional ARG for
62 compatibility with end-of-defun.
63 (log-view-end-of-defun): Ignore comments and VC buttons.
64
65 * vc/vc-bzr.el (vc-bzr-expanded-log-entry): New function.
66 (vc-bzr-log-view-mode): Use log-view-expanded-log-entry-function.
67
682011-02-13 Teodor Zlatanov <tzz@lifelogs.com>
69
70 * net/imap.el: Remove file. All the functionality is in nnimap.el.
71
72 * net/imap-hash.el: Remove file.
73
742011-02-13 Michael Albinus <michael.albinus@gmx.de>
75
76 * Makefile.in (TRAMP_SRC): Remove tramp-imap.el.
77
78 * net/tramp.el (tramp-read-passwd): Simplify `auth-source-search'
79 call.
80
81 * net/tramp-imap.el: Remove file.
82
832011-02-13 Chong Yidong <cyd@stupidchicken.com>
84
85 * vc/vc.el (vc-print-log-setup-buttons): Instead of using the
86 widget library for buttons, just use button.el.
87
88 * vc/log-view.el (log-view-mode-map): Don't inherit from
89 widget-keymap.
90
912011-02-12 Glenn Morris <rgm@gnu.org>
92
93 * emacs-lisp/cl-seq.el (union, nunion, intersection)
94 (nintersection, set-difference, nset-difference)
95 (set-exclusive-or, nset-exclusive-or): Doc fix.
96
97 * ediff-ptch.el (ediff-fixup-patch-map): Doc fix.
98
99 * faces.el (face-attr-match-p): Handle the obsolete :bold and
100 :italic props, so that frame-set-background-mode works. (Bug#7966)
101
102 * simple.el (next-error): Doc fix.
103
1042011-02-12 Thierry Volpiatto <thierry.volpiatto@gmail.com>
105
106 * dired-aux.el (dired-create-files): Adapt destination name to
107 match the new behavior of copy-directory.
108
12011-02-12 Chong Yidong <cyd@stupidchicken.com> 1092011-02-12 Chong Yidong <cyd@stupidchicken.com>
2 110
3 * mail/mail-utils.el (mail-dont-reply-to-names): New variable, 111 * mail/mail-utils.el (mail-dont-reply-to-names): New variable,
@@ -169,6 +277,11 @@
169 (allout-after-copy-or-kill-hook): No arguments - hook implementers 277 (allout-after-copy-or-kill-hook): No arguments - hook implementers
170 should concentrate on the kill ring. 278 should concentrate on the kill ring.
171 279
2802011-02-09 Teodor Zlatanov <tzz@lifelogs.com>
281
282 * password-cache.el (password-cache-remove): Accept secrets that are
283 not strings.
284
1722011-02-09 Stefan Monnier <monnier@iro.umontreal.ca> 2852011-02-09 Stefan Monnier <monnier@iro.umontreal.ca>
173 286
174 * progmodes/sh-script.el (sh-font-lock-open-heredoc): Fix case 287 * progmodes/sh-script.el (sh-font-lock-open-heredoc): Fix case
@@ -507,7 +620,7 @@
507 620
5082011-01-31 Deniz Dogan <deniz.a.m.dogan@gmail.com> 6212011-01-31 Deniz Dogan <deniz.a.m.dogan@gmail.com>
509 622
510 * net/rcirc.el: New customizable nick completion format. (Bug#6314) 623 * net/rcirc.el: New customizable nick completion format. (Bug#6314)
511 (rcirc-nick-completion-format): New defcustom. 624 (rcirc-nick-completion-format): New defcustom.
512 (rcirc-complete): Use it. 625 (rcirc-complete): Use it.
513 626
@@ -812,7 +925,7 @@
812 925
813 * calc/calc.el (calc-default-power-reference-level) 926 * calc/calc.el (calc-default-power-reference-level)
814 (calc-default-field-reference-level): New variables. 927 (calc-default-field-reference-level): New variables.
815 * calc/calc-units.el (math-standard-units): Add dB and Np. 928 * calc/calc-units.el (math-standard-units): Add dB and Np.
816 (math-logunits): New variable. 929 (math-logunits): New variable.
817 (math-extract-logunits, math-logcombine, calcFunc-luplus) 930 (math-extract-logunits, math-logcombine, calcFunc-luplus)
818 (calcFunc-luminus, calc-luplus, calc-luminus, math-logunit-level) 931 (calcFunc-luminus, calc-luplus, calc-luminus, math-logunit-level)
@@ -822,7 +935,7 @@
822 * calc/calc-help.el (calc-u-prefix-help): Add logarithmic help. 935 * calc/calc-help.el (calc-u-prefix-help): Add logarithmic help.
823 (calc-ul-prefix-help): New function. 936 (calc-ul-prefix-help): New function.
824 * calc/calc-ext.el (calc-init-extensions): Autoload new units 937 * calc/calc-ext.el (calc-init-extensions): Autoload new units
825 functions. Add keybindings for new units functions. 938 functions. Add keybindings for new units functions.
826 939
8272011-01-22 Giorgos Keramidas <keramida@ceid.upatras.gr> (tiny change) 9402011-01-22 Giorgos Keramidas <keramida@ceid.upatras.gr> (tiny change)
828 941
@@ -928,7 +1041,7 @@
928 1041
9292011-01-20 Ken Manheimer <ken.manheimer@gmail.com> 10422011-01-20 Ken Manheimer <ken.manheimer@gmail.com>
930 1043
931 * allout.el: (allout-institute-keymap): Use fset instead of 1044 * allout.el (allout-institute-keymap): Use fset instead of
932 reapplying defalias. 1045 reapplying defalias.
933 1046
934 (allout-hotspot-key-handler): Check for non-control-modified 1047 (allout-hotspot-key-handler): Check for non-control-modified
@@ -1056,7 +1169,7 @@
1056 (info-xref-output-heading): Rename from info-xref-filename-heading. 1169 (info-xref-output-heading): Rename from info-xref-filename-heading.
1057 (info-xref-good, info-xref-bad, info-xref-xfile-alist) 1170 (info-xref-good, info-xref-bad, info-xref-xfile-alist)
1058 (info-xref-filename-heading): Move to output managing section. 1171 (info-xref-filename-heading): Move to output managing section.
1059 (info-xref-docstrings): New command checking "Info node `(foo)Bar'" 1172 (info-xref-docstrings): New command checking "Info node `(foo)Bar'"
1060 (info-xref-lock-file-p, info-xref-with-file): New helpers for it. 1173 (info-xref-lock-file-p, info-xref-with-file): New helpers for it.
1061 (info-xref-subfile-p): Move to generic section with those two. 1174 (info-xref-subfile-p): Move to generic section with those two.
1062 (info-xref-check-node): New function split from 1175 (info-xref-check-node): New function split from
@@ -1066,7 +1179,7 @@
1066 (info-xref-check-node): Use it. 1179 (info-xref-check-node): Use it.
1067 (info-xref-with-output): Show count of unavailables at end of output. 1180 (info-xref-with-output): Show count of unavailables at end of output.
1068 (info-xref-all-info-files): Exclude ".*" dotfiles. Ignore broken 1181 (info-xref-all-info-files): Exclude ".*" dotfiles. Ignore broken
1069 symlinks. Exclude .texi files. Exclude Emacs backup files. 1182 symlinks. Exclude .texi files. Exclude Emacs backup files.
1070 (info-xref-check-all-custom): Fix quietening viper-mode and 1183 (info-xref-check-all-custom): Fix quietening viper-mode and
1071 gnus-registry-install -- use setq not let so as not to unbind 1184 gnus-registry-install -- use setq not let so as not to unbind
1072 after load. 1185 after load.
@@ -1646,7 +1759,7 @@
1646 (rmail-mime-insert-text): Call rmail-mime-insert-decoded-text. 1759 (rmail-mime-insert-text): Call rmail-mime-insert-decoded-text.
1647 (rmail-mime-insert-image): Argument changed. Caller changed. 1760 (rmail-mime-insert-image): Argument changed. Caller changed.
1648 (rmail-mime-image): Call rmail-mime-toggle-hidden. 1761 (rmail-mime-image): Call rmail-mime-toggle-hidden.
1649 (rmail-mime-set-bulk-data): New funciton. 1762 (rmail-mime-set-bulk-data): New function.
1650 (rmail-mime-insert-bulk): Argument changed. 1763 (rmail-mime-insert-bulk): Argument changed.
1651 (rmail-mime-multipart-handler): Return t. 1764 (rmail-mime-multipart-handler): Return t.
1652 (rmail-mime-process-multipart): Argument changed. 1765 (rmail-mime-process-multipart): Argument changed.
@@ -1911,7 +2024,7 @@
1911 (allout-toggle-subtree-encryption): Adjust docstrings to reflect 2024 (allout-toggle-subtree-encryption): Adjust docstrings to reflect
1912 defaulting policy and other changes. Change fetch-pass to keymode-cue, 2025 defaulting policy and other changes. Change fetch-pass to keymode-cue,
1913 for simpler universal argument interpretation. 2026 for simpler universal argument interpretation.
1914 (allout-toggle-subtree-encryption): Adjust docstring to describe 2027 (allout-toggle-subtree-encryption): Adjust docstring to describe
1915 changed encryption provisions. Change fetch-pass to keymode-cue, for 2028 changed encryption provisions. Change fetch-pass to keymode-cue, for
1916 simpler universal argument interpretation. Remove provisions for 2029 simpler universal argument interpretation. Remove provisions for
1917 handling key type and identity - they'll all be within 2030 handling key type and identity - they'll all be within
@@ -2527,8 +2640,8 @@
2527 and "psftp". Exchange "%k" marker with options. 2640 and "psftp". Exchange "%k" marker with options.
2528 (tramp-do-copy-or-rename-file, tramp-sh-handle-file-local-copy): 2641 (tramp-do-copy-or-rename-file, tramp-sh-handle-file-local-copy):
2529 Compute size of link target. 2642 Compute size of link target.
2530 (tramp-do-copy-or-rename-file-out-of-band). Move setting of 2643 (tramp-do-copy-or-rename-file-out-of-band): Move setting of
2531 `tramp-current-*' up due to gateway methods. Optimze computing of 2644 `tramp-current-*' up due to gateway methods. Optimize computing of
2532 copy arguments. Use `tramp-get-connection-name' and 2645 copy arguments. Use `tramp-get-connection-name' and
2533 `tramp-get-connection-buffer'. Improve debug messages. 2646 `tramp-get-connection-buffer'. Improve debug messages.
2534 (tramp-compute-multi-hops): Remove port determination. 2647 (tramp-compute-multi-hops): Remove port determination.
@@ -3780,7 +3893,7 @@
3780 3893
3781 * international/characters.el (char-acronym-table): New variable. 3894 * international/characters.el (char-acronym-table): New variable.
3782 (glyphless-char-control): New variable. 3895 (glyphless-char-control): New variable.
3783 (update-glyphless-char-display): New funciton. 3896 (update-glyphless-char-display): New function.
3784 3897
3785 * faces.el (glyphless-char): New face. 3898 * faces.el (glyphless-char): New face.
3786 3899
@@ -3851,7 +3964,7 @@
38512010-10-31 Jan Djärv <jan.h.d@swipnet.se> 39642010-10-31 Jan Djärv <jan.h.d@swipnet.se>
3852 3965
3853 * term/x-win.el (x-get-selection-value): New function that gets 3966 * term/x-win.el (x-get-selection-value): New function that gets
3854 PRIMARY with type as specified in x-select-request-type. (Bug#6802). 3967 PRIMARY with type as specified in x-select-request-type. (Bug#6802)
3855 3968
38562010-10-31 Michael Albinus <michael.albinus@gmx.de> 39692010-10-31 Michael Albinus <michael.albinus@gmx.de>
3857 3970
@@ -4140,7 +4253,7 @@
4140 is indented differently if it is after a begin..end clock. 4253 is indented differently if it is after a begin..end clock.
4141 (verilog-in-attribute-p, verilog-skip-backward-comments) 4254 (verilog-in-attribute-p, verilog-skip-backward-comments)
4142 (verilog-skip-forward-comment-p): Support proper treatment of 4255 (verilog-skip-forward-comment-p): Support proper treatment of
4143 attributes by indent code. Reported by Jeff Steele. 4256 attributes by indent code. Reported by Jeff Steele.
4144 (verilog-in-directive-p): Fix comment to correctly describe function. 4257 (verilog-in-directive-p): Fix comment to correctly describe function.
4145 (verilog-backward-up-list, verilog-in-struct-region-p) 4258 (verilog-backward-up-list, verilog-in-struct-region-p)
4146 (verilog-backward-token, verilog-in-struct-p) 4259 (verilog-backward-token, verilog-in-struct-p)
@@ -4151,9 +4264,9 @@
4151 (verilog-property-re, verilog-endcomment-reason-re) 4264 (verilog-property-re, verilog-endcomment-reason-re)
4152 (verilog-beg-of-statement, verilog-set-auto-endcomments) 4265 (verilog-beg-of-statement, verilog-set-auto-endcomments)
4153 (verilog-calc-1 ): Fix for assert a; else b; indentation (new form 4266 (verilog-calc-1 ): Fix for assert a; else b; indentation (new form
4154 of if). Reported by Max Bjurling and 4267 of if). Reported by Max Bjurling and
4155 (verilog-calc-1): Fix for clocking block in modport 4268 (verilog-calc-1): Fix for clocking block in modport
4156 declaration. Reported by Brian Hunter. 4269 declaration. Reported by Brian Hunter.
4157 4270
41582010-10-24 Wilson Snyder <wsnyder@wsnyder.org> 42712010-10-24 Wilson Snyder <wsnyder@wsnyder.org>
4159 4272
@@ -4169,7 +4282,7 @@
4169 (verilog-read-always-signals-recurse, verilog-read-decls): Fix not 4282 (verilog-read-always-signals-recurse, verilog-read-decls): Fix not
4170 treating `elsif similar to `endif inside AUTOSENSE. 4283 treating `elsif similar to `endif inside AUTOSENSE.
4171 (verilog-do-indent): Implement correct automatic or static task or 4284 (verilog-do-indent): Implement correct automatic or static task or
4172 function end comment highlight. Reported by Steve Pearlmutter. 4285 function end comment highlight. Reported by Steve Pearlmutter.
4173 (verilog-font-lock-keywords-2): Fix highlighting of single 4286 (verilog-font-lock-keywords-2): Fix highlighting of single
4174 character pins, bug264. Reported by Michael Laajanen. 4287 character pins, bug264. Reported by Michael Laajanen.
4175 (verilog-auto-inst, verilog-read-decls, verilog-read-sub-decls) 4288 (verilog-auto-inst, verilog-read-decls, verilog-read-sub-decls)
@@ -4180,7 +4293,7 @@
4180 Reported by Mark Johnson. 4293 Reported by Mark Johnson.
4181 (verilog-auto-tieoff, verilog-auto-tieoff-ignore-regexp): 4294 (verilog-auto-tieoff, verilog-auto-tieoff-ignore-regexp):
4182 Add 'verilog-auto-tieoff-ignore-regexp' for AUTOTIEOFF, 4295 Add 'verilog-auto-tieoff-ignore-regexp' for AUTOTIEOFF,
4183 bug269. Suggested by Gary Delp. 4296 bug269. Suggested by Gary Delp.
4184 (verilog-mode-map, verilog-preprocess, verilog-preprocess-history) 4297 (verilog-mode-map, verilog-preprocess, verilog-preprocess-history)
4185 (verilog-preprocessor, verilog-set-compile-command): 4298 (verilog-preprocessor, verilog-set-compile-command):
4186 Create verilog-preprocess and verilog-preprocessor to show 4299 Create verilog-preprocess and verilog-preprocessor to show
@@ -4188,7 +4301,7 @@
4188 (verilog-get-beg-of-line, verilog-get-end-of-line) 4301 (verilog-get-beg-of-line, verilog-get-end-of-line)
4189 (verilog-modi-file-or-buffer, verilog-modi-name) 4302 (verilog-modi-file-or-buffer, verilog-modi-name)
4190 (verilog-modi-point, verilog-within-string): Move defmacro's 4303 (verilog-modi-point, verilog-within-string): Move defmacro's
4191 before first use to avoid warning. Reported by Steve Pearlmutter. 4304 before first use to avoid warning. Reported by Steve Pearlmutter.
4192 (verilog-colorize-buffer, verilog-colorize-include-files-buffer) 4305 (verilog-colorize-buffer, verilog-colorize-include-files-buffer)
4193 (verilog-colorize-region, verilog-highlight-buffer) 4306 (verilog-colorize-region, verilog-highlight-buffer)
4194 (verilog-highlight-includes, verilog-highlight-modules) 4307 (verilog-highlight-includes, verilog-highlight-modules)
@@ -4220,7 +4333,7 @@
4220 (verilog-alw-get-temps, verilog-auto-reset) 4333 (verilog-alw-get-temps, verilog-auto-reset)
4221 (verilog-auto-sense-sigs, verilog-read-always-signals) 4334 (verilog-auto-sense-sigs, verilog-read-always-signals)
4222 (verilog-read-always-signals-recurse): Fix loop indexes being 4335 (verilog-read-always-signals-recurse): Fix loop indexes being
4223 AUTORESET. AUTORESET now assumes any variables in the 4336 AUTORESET. AUTORESET now assumes any variables in the
4224 initialization section of a for() should be ignored. 4337 initialization section of a for() should be ignored.
4225 Reported by Dan Dever. 4338 Reported by Dan Dever.
4226 (verilog-error-font-lock-keywords) 4339 (verilog-error-font-lock-keywords)
@@ -5633,7 +5746,7 @@
5633 (sql-postgres-login-params): Add user and database defaults. 5746 (sql-postgres-login-params): Add user and database defaults.
5634 (sql-buffer-live-p): Bug fix. 5747 (sql-buffer-live-p): Bug fix.
5635 (sql-product-history): New variable. 5748 (sql-product-history): New variable.
5636 (sql-read-product): New function. Use it. 5749 (sql-read-product): New function. Use it.
5637 (sql-set-product, sql-product-interactive): Use it. 5750 (sql-set-product, sql-product-interactive): Use it.
5638 (sql-connection-history): New variable. 5751 (sql-connection-history): New variable.
5639 (sql-read-connection): New function. Use it. 5752 (sql-read-connection): New function. Use it.
diff --git a/lisp/Makefile.in b/lisp/Makefile.in
index 2f92578b516..d99622944a3 100644
--- a/lisp/Makefile.in
+++ b/lisp/Makefile.in
@@ -329,16 +329,16 @@ $(MH_E_DIR)/mh-loaddefs.el: $(MH_E_SRC)
329 --eval "(setq make-backup-files nil)" \ 329 --eval "(setq make-backup-files nil)" \
330 -f batch-update-autoloads $(MH_E_DIR) 330 -f batch-update-autoloads $(MH_E_DIR)
331 331
332# Update TRAMP internal autoloads. Maybe we could move trmp*.el into 332# Update TRAMP internal autoloads. Maybe we could move tramp*.el into
333# an own subdirectory. OTOH, it does not hurt to keep them in 333# an own subdirectory. OTOH, it does not hurt to keep them in
334# lisp/net. 334# lisp/net.
335TRAMP_DIR = $(lisp)/net 335TRAMP_DIR = $(lisp)/net
336TRAMP_SRC = $(TRAMP_DIR)/tramp.el $(TRAMP_DIR)/tramp-cache.el \ 336TRAMP_SRC = $(TRAMP_DIR)/tramp.el $(TRAMP_DIR)/tramp-cache.el \
337 $(TRAMP_DIR)/tramp-cmds.el $(TRAMP_DIR)/tramp-compat.el \ 337 $(TRAMP_DIR)/tramp-cmds.el $(TRAMP_DIR)/tramp-compat.el \
338 $(TRAMP_DIR)/tramp-ftp.el $(TRAMP_DIR)/tramp-gvfs.el \ 338 $(TRAMP_DIR)/tramp-ftp.el $(TRAMP_DIR)/tramp-gvfs.el \
339 $(TRAMP_DIR)/tramp-gw.el $(TRAMP_DIR)/tramp-imap.el \ 339 $(TRAMP_DIR)/tramp-gw.el $(TRAMP_DIR)/tramp-sh.el \
340 $(TRAMP_DIR)/tramp-sh.el $(TRAMP_DIR)/tramp-smb.el \ 340 $(TRAMP_DIR)/tramp-smb.el $(TRAMP_DIR)/tramp-uu.el \
341 $(TRAMP_DIR)/tramp-uu.el $(TRAMP_DIR)/trampver.el 341 $(TRAMP_DIR)/trampver.el
342 342
343$(TRAMP_DIR)/tramp-loaddefs.el: $(TRAMP_SRC) 343$(TRAMP_DIR)/tramp-loaddefs.el: $(TRAMP_SRC)
344 $(emacs) -l autoload \ 344 $(emacs) -l autoload \
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 6f33831eb38..cb1324051a7 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -1383,6 +1383,10 @@ ESC or `q' to not overwrite any of the remaining files,
1383 (cond ((integerp marker-char) marker-char) 1383 (cond ((integerp marker-char) marker-char)
1384 (marker-char (dired-file-marker from)) ; slow 1384 (marker-char (dired-file-marker from)) ; slow
1385 (t nil)))) 1385 (t nil))))
1386 (when (and (file-directory-p from)
1387 (file-directory-p to)
1388 (eq file-creator 'dired-copy-file))
1389 (setq to (file-name-directory to)))
1386 (condition-case err 1390 (condition-case err
1387 (progn 1391 (progn
1388 (funcall file-creator from to dired-overwrite-confirmed) 1392 (funcall file-creator from to dired-overwrite-confirmed)
diff --git a/lisp/dired-x.el b/lisp/dired-x.el
index e0caae059b4..8011b4d32a4 100644
--- a/lisp/dired-x.el
+++ b/lisp/dired-x.el
@@ -25,91 +25,47 @@
25 25
26;;; Commentary: 26;;; Commentary:
27 27
28;; This is Sebastian Kremer's excellent dired-x.el (Dired Extra), version 28;; This is based on Sebastian Kremer's excellent dired-x.el (Dired Extra),
29;; 1.191, hacked up for GNU Emacs. Redundant or conflicting material has 29;; version 1.191, adapted for GNU Emacs. See the `dired-x' info pages.
30;; been removed or renamed in order to work properly with dired of GNU
31;; Emacs. All suggestions or comments are most welcomed.
32 30
33;; 31;; USAGE: In your ~/.emacs,
34;; Please, PLEASE, *PLEASE* see the info pages.
35;;
36
37;; BUGS: Type M-x dired-x-submit-report and a report will be generated.
38
39;; INSTALLATION: In your ~/.emacs,
40;; 32;;
41;; (add-hook 'dired-load-hook 33;; (add-hook 'dired-load-hook
42;; (function (lambda () 34;; (lambda ()
43;; (load "dired-x") 35;; (require 'dired-x)
44;; ;; Set global variables here. For example: 36;; ;; Set global variables here. For example:
45;; ;; (setq dired-guess-shell-gnutar "gtar") 37;; ;; (setq dired-guess-shell-gnutar "gtar")
46;; ))) 38;; ))
47;; (add-hook 'dired-mode-hook 39;; (add-hook 'dired-mode-hook
48;; (function (lambda () 40;; (lambda ()
49;; ;; Set buffer-local variables here. For example: 41;; ;; Set buffer-local variables here. For example:
50;; ;; (dired-omit-mode 1) 42;; ;; (dired-omit-mode 1)
51;; ))) 43;; ))
52;; 44;;
53;; At load time dired-x.el will install itself, redefine some functions, and 45;; At load time dired-x.el will install itself, redefine some functions, and
54;; bind some dired keys. *Please* see the info pages for more details. 46;; bind some dired keys.
55 47
56;; *Please* see the info pages for more details. 48;; User customization: M-x customize-group RET dired-x RET.
57 49
58;; User defined variables: 50;; When loaded this code redefines the following functions of GNU Emacs:
59;; 51;; From dired.el: dired-clean-up-after-deletion, dired-find-buffer-nocreate,
60;; dired-bind-vm 52;; and dired-initial-position.
61;; dired-vm-read-only-folders 53;; From dired-aux.el: dired-add-entry and dired-read-shell-command.
62;; dired-bind-jump
63;; dired-bind-info
64;; dired-bind-man
65;; dired-x-hands-off-my-keys
66;; dired-find-subdir
67;; dired-enable-local-variables
68;; dired-local-variables-file
69;; dired-guess-shell-gnutar
70;; dired-guess-shell-gzip-quiet
71;; dired-guess-shell-znew-switches
72;; dired-guess-shell-alist-user
73;; dired-clean-up-buffers-too
74;; dired-omit-mode
75;; dired-omit-files
76;; dired-omit-extensions
77;; dired-omit-size-limit
78;;
79;; To find out more about these variables, load this file, put your cursor at
80;; the end of any of the variable names, and hit C-h v [RET]. *Please* see
81;; the info pages for more details.
82 54
83;; When loaded this code redefines the following functions of GNU Emacs 55;; *Please* see the `dired-x' info pages for more details.
84;;
85;; Function Found in this file of GNU Emacs
86;; -------- -------------------------------
87;; dired-clean-up-after-deletion ../lisp/dired.el
88;; dired-find-buffer-nocreate ../lisp/dired.el
89;; dired-initial-position ../lisp/dired.el
90;;
91;; dired-add-entry ../lisp/dired-aux.el
92;; dired-read-shell-command ../lisp/dired-aux.el
93 56
94 57
95;;; Code: 58;;; Code:
96 59
97;; LOAD. 60;; LOAD.
98 61
99;; This is a no-op if dired-x is being loaded via `dired-load-hook'. It is 62;; This is a no-op if dired-x is being loaded via `dired-load-hook',
100;; here in case the user has autoloaded dired-x via the dired-jump key binding 63;; but maybe not if a dired-x function is being autoloaded.
101;; (instead of autoloading to dired as is suggested in the info-pages).
102
103(require 'dired) 64(require 'dired)
104 65
105;; We will redefine some functions and also need some macros so we need to 66;; We will redefine some functions and also need some macros.
106;; load dired stuff of GNU Emacs.
107
108(require 'dired-aux) 67(require 'dired-aux)
109 68
110(defvar vm-folder-directory)
111(eval-when-compile (require 'man))
112
113;;; User-defined variables. 69;;; User-defined variables.
114 70
115(defgroup dired-x nil 71(defgroup dired-x nil
@@ -340,10 +296,9 @@ to nil: a pipe using `zcat' or `gunzip -c' will be used."
340 'dashes))) 296 'dashes)))
341 297
342;;; GLOBAL BINDING. 298;;; GLOBAL BINDING.
343(if dired-bind-jump 299(when dired-bind-jump
344 (progn 300 (define-key global-map "\C-x\C-j" 'dired-jump)
345 (define-key global-map "\C-x\C-j" 'dired-jump) 301 (define-key global-map "\C-x4\C-j" 'dired-jump-other-window))
346 (define-key global-map "\C-x4\C-j" 'dired-jump-other-window)))
347 302
348 303
349;; Install into appropriate hooks. 304;; Install into appropriate hooks.
@@ -589,7 +544,7 @@ Should never be used as marker by the user or other packages.")
589 (let ((dired-omit-mode nil)) (revert-buffer)) ;; Show omitted files 544 (let ((dired-omit-mode nil)) (revert-buffer)) ;; Show omitted files
590 (dired-mark-unmarked-files (dired-omit-regexp) nil nil dired-omit-localp)) 545 (dired-mark-unmarked-files (dired-omit-regexp) nil nil dired-omit-localp))
591 546
592(defvar dired-omit-extensions 547(defcustom dired-omit-extensions
593 (append completion-ignored-extensions 548 (append completion-ignored-extensions
594 dired-latex-unclean-extensions 549 dired-latex-unclean-extensions
595 dired-bibtex-unclean-extensions 550 dired-bibtex-unclean-extensions
@@ -600,7 +555,9 @@ Defaults to elements of `completion-ignored-extensions',
600`dired-texinfo-unclean-extensions'. 555`dired-texinfo-unclean-extensions'.
601 556
602See interactive function `dired-omit-mode' \(\\[dired-omit-mode]\) and 557See interactive function `dired-omit-mode' \(\\[dired-omit-mode]\) and
603variables `dired-omit-mode' and `dired-omit-files'.") 558variables `dired-omit-mode' and `dired-omit-files'."
559 :type '(repeat string)
560 :group 'dired-x)
604 561
605(defun dired-omit-expunge (&optional regexp) 562(defun dired-omit-expunge (&optional regexp)
606 "Erases all unmarked files matching REGEXP. 563 "Erases all unmarked files matching REGEXP.
@@ -896,12 +853,15 @@ Knows about the special cases in variable `default-directory-alist'."
896;;; `dired-enable-local-variables' and run `hack-local-variables' on the 853;;; `dired-enable-local-variables' and run `hack-local-variables' on the
897;;; Dired Buffer. 854;;; Dired Buffer.
898 855
899(defvar dired-local-variables-file (convert-standard-filename ".dired") 856;; FIXME do standard dir-locals obsolete this?
857(defcustom dired-local-variables-file (convert-standard-filename ".dired")
900 "Filename, as string, containing local dired buffer variables to be hacked. 858 "Filename, as string, containing local dired buffer variables to be hacked.
901If this file found in current directory, then it will be inserted into dired 859If this file found in current directory, then it will be inserted into dired
902buffer and `hack-local-variables' will be run. See Info node 860buffer and `hack-local-variables' will be run. See Info node
903`(emacs)File Variables' for more information on local variables. 861`(emacs)File Variables' for more information on local variables.
904See also `dired-enable-local-variables'.") 862See also `dired-enable-local-variables'."
863 :type 'file
864 :group 'dired)
905 865
906(defun dired-hack-local-variables () 866(defun dired-hack-local-variables ()
907 "Evaluate local variables in `dired-local-variables-file' for dired buffer." 867 "Evaluate local variables in `dired-local-variables-file' for dired buffer."
@@ -980,6 +940,8 @@ dired."
980;; NOTE: Use `gunzip -c' instead of `zcat' on `.gz' files. Some do not 940;; NOTE: Use `gunzip -c' instead of `zcat' on `.gz' files. Some do not
981;; install GNU zip's version of zcat. 941;; install GNU zip's version of zcat.
982 942
943(declare-function Man-support-local-filenames "man" ())
944
983(defvar dired-guess-shell-alist-default 945(defvar dired-guess-shell-alist-default
984 (list 946 (list
985 (list "\\.tar$" 947 (list "\\.tar$"
@@ -1429,6 +1391,8 @@ NOSELECT the files are merely found but not selected."
1429 1391
1430;; Run man on files. 1392;; Run man on files.
1431 1393
1394(declare-function Man-getpage-in-background "man" (topic))
1395
1432(defun dired-man () 1396(defun dired-man ()
1433 "Run man on this file. Display old buffer if buffer name matches filename. 1397 "Run man on this file. Display old buffer if buffer name matches filename.
1434Uses `man.el' of \\[manual-entry] fame." 1398Uses `man.el' of \\[manual-entry] fame."
@@ -1449,11 +1413,8 @@ Uses `man.el' of \\[manual-entry] fame."
1449 1413
1450;; Run mail on mail folders. 1414;; Run mail on mail folders.
1451 1415
1452;; Avoid compiler warning. 1416(declare-function vm-visit-folder "ext:vm" (folder &optional read-only))
1453(eval-when-compile 1417(defvar vm-folder-directory)
1454 (when (not (fboundp 'vm-visit-folder))
1455 (defun vm-visit-folder (file &optional arg)
1456 nil)))
1457 1418
1458(defun dired-vm (&optional read-only) 1419(defun dired-vm (&optional read-only)
1459 "Run VM on this file. 1420 "Run VM on this file.
@@ -1659,11 +1620,17 @@ to mark all zero length files."
1659 1620
1660;;; FIND FILE AT POINT. 1621;;; FIND FILE AT POINT.
1661 1622
1662(defvar dired-x-hands-off-my-keys t 1623(defcustom dired-x-hands-off-my-keys t
1663 "*Non-nil means don't bind `dired-x-find-file' over `find-file' on keyboard. 1624 "Non-nil means don't bind `dired-x-find-file' over `find-file' on keyboard.
1664Similarly for `dired-x-find-file-other-window' over `find-file-other-window'. 1625Similarly for `dired-x-find-file-other-window' over `find-file-other-window'.
1665If you change this variable after `dired-x.el' is loaded then do 1626If you change this variable without using \\[customize] after `dired-x.el'
1666\\[dired-x-bind-find-file].") 1627is loaded then call \\[dired-x-bind-find-file]."
1628 :type 'boolean
1629 :initialize 'custom-initialize-default
1630 :set (lambda (sym val)
1631 (set sym val)
1632 (dired-x-bind-find-file))
1633 :group 'dired-x)
1667 1634
1668;; Bind `dired-x-find-file{-other-window}' over wherever 1635;; Bind `dired-x-find-file{-other-window}' over wherever
1669;; `find-file{-other-window}' is bound? 1636;; `find-file{-other-window}' is bound?
@@ -1777,48 +1744,7 @@ If `current-prefix-arg' is non-nil, uses name at point as guess."
1777 1744
1778;;; BUG REPORTS 1745;;; BUG REPORTS
1779 1746
1780;; Fixme: get rid of this later. 1747(define-obsolete-function-alias 'dired-x-submit-report 'report-emacs-bug "24.1")
1781
1782;; This section is provided for reports. It uses Barry A. Warsaw's
1783;; reporter.el which is bundled with GNU Emacs v19.
1784
1785(defconst dired-x-help-address "bug-gnu-emacs@gnu.org"
1786 "Address(es) accepting submission of reports on dired-x.el.")
1787
1788(defconst dired-x-variable-list
1789 (list
1790 'dired-bind-vm
1791 'dired-vm-read-only-folders
1792 'dired-bind-jump
1793 'dired-bind-info
1794 'dired-bind-man
1795 'dired-find-subdir
1796 'dired-enable-local-variables
1797 'dired-local-variables-file
1798 'dired-guess-shell-gnutar
1799 'dired-guess-shell-gzip-quiet
1800 'dired-guess-shell-znew-switches
1801 'dired-guess-shell-alist-user
1802 'dired-clean-up-buffers-too
1803 'dired-omit-mode
1804 'dired-omit-files
1805 'dired-omit-extensions
1806 )
1807 "List of variables to be appended to reports sent by `dired-x-submit-report'.")
1808
1809(defun dired-x-submit-report ()
1810 "Submit via `reporter.el' a bug report on program.
1811Send report on `dired-x-file' version `dired-x-version', to
1812`dired-x-maintainer' at address `dired-x-help-address' listing
1813variables `dired-x-variable-list' in the message."
1814 (interactive)
1815
1816 (reporter-submit-bug-report
1817 dired-x-help-address ; address
1818 "dired-x" ; pkgname
1819 dired-x-variable-list ; varlist
1820 nil nil ; pre-/post-hooks
1821 ""))
1822 1748
1823 1749
1824;; As Barry Warsaw would say: "This might be useful..." 1750;; As Barry Warsaw would say: "This might be useful..."
diff --git a/lisp/dired.el b/lisp/dired.el
index 3a76398e956..058dbdc548a 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -3570,7 +3570,7 @@ Ask means pop up a menu for the user to select one of copy, move or link."
3570;;;;;; dired-run-shell-command dired-do-shell-command dired-do-async-shell-command 3570;;;;;; dired-run-shell-command dired-do-shell-command dired-do-async-shell-command
3571;;;;;; dired-clean-directory dired-do-print dired-do-touch dired-do-chown 3571;;;;;; dired-clean-directory dired-do-print dired-do-touch dired-do-chown
3572;;;;;; dired-do-chgrp dired-do-chmod dired-compare-directories dired-backup-diff 3572;;;;;; dired-do-chgrp dired-do-chmod dired-compare-directories dired-backup-diff
3573;;;;;; dired-diff) "dired-aux" "dired-aux.el" "9f5fc434fa6c2607b6e66060862c9caf") 3573;;;;;; dired-diff) "dired-aux" "dired-aux.el" "e66465bcd1687d66cfb1202c9963d567")
3574;;; Generated autoloads from dired-aux.el 3574;;; Generated autoloads from dired-aux.el
3575 3575
3576(autoload 'dired-diff "dired-aux" "\ 3576(autoload 'dired-diff "dired-aux" "\
@@ -4029,7 +4029,7 @@ true then the type of the file linked to by FILE is printed instead.
4029;;;*** 4029;;;***
4030 4030
4031;;;### (autoloads (dired-do-relsymlink dired-jump) "dired-x" "dired-x.el" 4031;;;### (autoloads (dired-do-relsymlink dired-jump) "dired-x" "dired-x.el"
4032;;;;;; "fbac6ae123aaa2b2e9df8bb2cde61ceb") 4032;;;;;; "d35468f85920d324895b0c04bb703328")
4033;;; Generated autoloads from dired-x.el 4033;;; Generated autoloads from dired-x.el
4034 4034
4035(autoload 'dired-jump "dired-x" "\ 4035(autoload 'dired-jump "dired-x" "\
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el
index 2d3b228cbd4..9880e2918b0 100644
--- a/lisp/emacs-lisp/checkdoc.el
+++ b/lisp/emacs-lisp/checkdoc.el
@@ -1797,7 +1797,7 @@ Replace with \"%s\"? " original replace)
1797 (let ((found nil) (start (point)) (msg nil) (ms nil)) 1797 (let ((found nil) (start (point)) (msg nil) (ms nil))
1798 (while (and (not msg) 1798 (while (and (not msg)
1799 (re-search-forward 1799 (re-search-forward
1800 ;; Ignore manual page refereces like 1800 ;; Ignore manual page references like
1801 ;; git-config(1). 1801 ;; git-config(1).
1802 "[^-([`':a-zA-Z]\\(\\w+[:-]\\(\\w\\|\\s_\\)+\\)[^](']" 1802 "[^-([`':a-zA-Z]\\(\\w+[:-]\\(\\w\\|\\s_\\)+\\)[^](']"
1803 e t)) 1803 e t))
diff --git a/lisp/emacs-lisp/cl-loaddefs.el b/lisp/emacs-lisp/cl-loaddefs.el
index e10dc10447c..8e192a18459 100644
--- a/lisp/emacs-lisp/cl-loaddefs.el
+++ b/lisp/emacs-lisp/cl-loaddefs.el
@@ -754,7 +754,7 @@ surrounded by (block NAME ...).
754;;;;;; find nsubstitute-if-not nsubstitute-if nsubstitute substitute-if-not 754;;;;;; find nsubstitute-if-not nsubstitute-if nsubstitute substitute-if-not
755;;;;;; substitute-if substitute delete-duplicates remove-duplicates 755;;;;;; substitute-if substitute delete-duplicates remove-duplicates
756;;;;;; delete-if-not delete-if delete* remove-if-not remove-if remove* 756;;;;;; delete-if-not delete-if delete* remove-if-not remove-if remove*
757;;;;;; replace fill reduce) "cl-seq" "cl-seq.el" "43e0c1183e738e1e1038cdd84fde8366") 757;;;;;; replace fill reduce) "cl-seq" "cl-seq.el" "df375ddc313f0c1c262cacab5cffd3e4")
758;;; Generated autoloads from cl-seq.el 758;;; Generated autoloads from cl-seq.el
759 759
760(autoload 'reduce "cl-seq" "\ 760(autoload 'reduce "cl-seq" "\
@@ -1080,7 +1080,7 @@ Keywords supported: :key
1080 1080
1081(autoload 'union "cl-seq" "\ 1081(autoload 'union "cl-seq" "\
1082Combine LIST1 and LIST2 using a set-union operation. 1082Combine LIST1 and LIST2 using a set-union operation.
1083The result list contains all items that appear in either LIST1 or LIST2. 1083The resulting list contains all items that appear in either LIST1 or LIST2.
1084This is a non-destructive function; it makes a copy of the data if necessary 1084This is a non-destructive function; it makes a copy of the data if necessary
1085to avoid corrupting the original LIST1 and LIST2. 1085to avoid corrupting the original LIST1 and LIST2.
1086 1086
@@ -1090,7 +1090,7 @@ Keywords supported: :test :test-not :key
1090 1090
1091(autoload 'nunion "cl-seq" "\ 1091(autoload 'nunion "cl-seq" "\
1092Combine LIST1 and LIST2 using a set-union operation. 1092Combine LIST1 and LIST2 using a set-union operation.
1093The result list contains all items that appear in either LIST1 or LIST2. 1093The resulting list contains all items that appear in either LIST1 or LIST2.
1094This is a destructive function; it reuses the storage of LIST1 and LIST2 1094This is a destructive function; it reuses the storage of LIST1 and LIST2
1095whenever possible. 1095whenever possible.
1096 1096
@@ -1100,7 +1100,7 @@ Keywords supported: :test :test-not :key
1100 1100
1101(autoload 'intersection "cl-seq" "\ 1101(autoload 'intersection "cl-seq" "\
1102Combine LIST1 and LIST2 using a set-intersection operation. 1102Combine LIST1 and LIST2 using a set-intersection operation.
1103The result list contains all items that appear in both LIST1 and LIST2. 1103The resulting list contains all items that appear in both LIST1 and LIST2.
1104This is a non-destructive function; it makes a copy of the data if necessary 1104This is a non-destructive function; it makes a copy of the data if necessary
1105to avoid corrupting the original LIST1 and LIST2. 1105to avoid corrupting the original LIST1 and LIST2.
1106 1106
@@ -1110,7 +1110,7 @@ Keywords supported: :test :test-not :key
1110 1110
1111(autoload 'nintersection "cl-seq" "\ 1111(autoload 'nintersection "cl-seq" "\
1112Combine LIST1 and LIST2 using a set-intersection operation. 1112Combine LIST1 and LIST2 using a set-intersection operation.
1113The result list contains all items that appear in both LIST1 and LIST2. 1113The resulting list contains all items that appear in both LIST1 and LIST2.
1114This is a destructive function; it reuses the storage of LIST1 and LIST2 1114This is a destructive function; it reuses the storage of LIST1 and LIST2
1115whenever possible. 1115whenever possible.
1116 1116
@@ -1120,7 +1120,7 @@ Keywords supported: :test :test-not :key
1120 1120
1121(autoload 'set-difference "cl-seq" "\ 1121(autoload 'set-difference "cl-seq" "\
1122Combine LIST1 and LIST2 using a set-difference operation. 1122Combine LIST1 and LIST2 using a set-difference operation.
1123The result list contains all items that appear in LIST1 but not LIST2. 1123The resulting list contains all items that appear in LIST1 but not LIST2.
1124This is a non-destructive function; it makes a copy of the data if necessary 1124This is a non-destructive function; it makes a copy of the data if necessary
1125to avoid corrupting the original LIST1 and LIST2. 1125to avoid corrupting the original LIST1 and LIST2.
1126 1126
@@ -1130,7 +1130,7 @@ Keywords supported: :test :test-not :key
1130 1130
1131(autoload 'nset-difference "cl-seq" "\ 1131(autoload 'nset-difference "cl-seq" "\
1132Combine LIST1 and LIST2 using a set-difference operation. 1132Combine LIST1 and LIST2 using a set-difference operation.
1133The result list contains all items that appear in LIST1 but not LIST2. 1133The resulting list contains all items that appear in LIST1 but not LIST2.
1134This is a destructive function; it reuses the storage of LIST1 and LIST2 1134This is a destructive function; it reuses the storage of LIST1 and LIST2
1135whenever possible. 1135whenever possible.
1136 1136
@@ -1140,7 +1140,7 @@ Keywords supported: :test :test-not :key
1140 1140
1141(autoload 'set-exclusive-or "cl-seq" "\ 1141(autoload 'set-exclusive-or "cl-seq" "\
1142Combine LIST1 and LIST2 using a set-exclusive-or operation. 1142Combine LIST1 and LIST2 using a set-exclusive-or operation.
1143The result list contains all items that appear in exactly one of LIST1, LIST2. 1143The resulting list contains all items appearing in exactly one of LIST1, LIST2.
1144This is a non-destructive function; it makes a copy of the data if necessary 1144This is a non-destructive function; it makes a copy of the data if necessary
1145to avoid corrupting the original LIST1 and LIST2. 1145to avoid corrupting the original LIST1 and LIST2.
1146 1146
@@ -1150,7 +1150,7 @@ Keywords supported: :test :test-not :key
1150 1150
1151(autoload 'nset-exclusive-or "cl-seq" "\ 1151(autoload 'nset-exclusive-or "cl-seq" "\
1152Combine LIST1 and LIST2 using a set-exclusive-or operation. 1152Combine LIST1 and LIST2 using a set-exclusive-or operation.
1153The result list contains all items that appear in exactly one of LIST1, LIST2. 1153The resulting list contains all items appearing in exactly one of LIST1, LIST2.
1154This is a destructive function; it reuses the storage of LIST1 and LIST2 1154This is a destructive function; it reuses the storage of LIST1 and LIST2
1155whenever possible. 1155whenever possible.
1156 1156
diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el
index fcd21b73de7..1c578556835 100644
--- a/lisp/emacs-lisp/cl-seq.el
+++ b/lisp/emacs-lisp/cl-seq.el
@@ -770,7 +770,7 @@ Return the sublist of LIST whose car matches.
770;;;###autoload 770;;;###autoload
771(defun union (cl-list1 cl-list2 &rest cl-keys) 771(defun union (cl-list1 cl-list2 &rest cl-keys)
772 "Combine LIST1 and LIST2 using a set-union operation. 772 "Combine LIST1 and LIST2 using a set-union operation.
773The result list contains all items that appear in either LIST1 or LIST2. 773The resulting list contains all items that appear in either LIST1 or LIST2.
774This is a non-destructive function; it makes a copy of the data if necessary 774This is a non-destructive function; it makes a copy of the data if necessary
775to avoid corrupting the original LIST1 and LIST2. 775to avoid corrupting the original LIST1 and LIST2.
776\nKeywords supported: :test :test-not :key 776\nKeywords supported: :test :test-not :key
@@ -791,7 +791,7 @@ to avoid corrupting the original LIST1 and LIST2.
791;;;###autoload 791;;;###autoload
792(defun nunion (cl-list1 cl-list2 &rest cl-keys) 792(defun nunion (cl-list1 cl-list2 &rest cl-keys)
793 "Combine LIST1 and LIST2 using a set-union operation. 793 "Combine LIST1 and LIST2 using a set-union operation.
794The result list contains all items that appear in either LIST1 or LIST2. 794The resulting list contains all items that appear in either LIST1 or LIST2.
795This is a destructive function; it reuses the storage of LIST1 and LIST2 795This is a destructive function; it reuses the storage of LIST1 and LIST2
796whenever possible. 796whenever possible.
797\nKeywords supported: :test :test-not :key 797\nKeywords supported: :test :test-not :key
@@ -802,7 +802,7 @@ whenever possible.
802;;;###autoload 802;;;###autoload
803(defun intersection (cl-list1 cl-list2 &rest cl-keys) 803(defun intersection (cl-list1 cl-list2 &rest cl-keys)
804 "Combine LIST1 and LIST2 using a set-intersection operation. 804 "Combine LIST1 and LIST2 using a set-intersection operation.
805The result list contains all items that appear in both LIST1 and LIST2. 805The resulting list contains all items that appear in both LIST1 and LIST2.
806This is a non-destructive function; it makes a copy of the data if necessary 806This is a non-destructive function; it makes a copy of the data if necessary
807to avoid corrupting the original LIST1 and LIST2. 807to avoid corrupting the original LIST1 and LIST2.
808\nKeywords supported: :test :test-not :key 808\nKeywords supported: :test :test-not :key
@@ -825,7 +825,7 @@ to avoid corrupting the original LIST1 and LIST2.
825;;;###autoload 825;;;###autoload
826(defun nintersection (cl-list1 cl-list2 &rest cl-keys) 826(defun nintersection (cl-list1 cl-list2 &rest cl-keys)
827 "Combine LIST1 and LIST2 using a set-intersection operation. 827 "Combine LIST1 and LIST2 using a set-intersection operation.
828The result list contains all items that appear in both LIST1 and LIST2. 828The resulting list contains all items that appear in both LIST1 and LIST2.
829This is a destructive function; it reuses the storage of LIST1 and LIST2 829This is a destructive function; it reuses the storage of LIST1 and LIST2
830whenever possible. 830whenever possible.
831\nKeywords supported: :test :test-not :key 831\nKeywords supported: :test :test-not :key
@@ -835,7 +835,7 @@ whenever possible.
835;;;###autoload 835;;;###autoload
836(defun set-difference (cl-list1 cl-list2 &rest cl-keys) 836(defun set-difference (cl-list1 cl-list2 &rest cl-keys)
837 "Combine LIST1 and LIST2 using a set-difference operation. 837 "Combine LIST1 and LIST2 using a set-difference operation.
838The result list contains all items that appear in LIST1 but not LIST2. 838The resulting list contains all items that appear in LIST1 but not LIST2.
839This is a non-destructive function; it makes a copy of the data if necessary 839This is a non-destructive function; it makes a copy of the data if necessary
840to avoid corrupting the original LIST1 and LIST2. 840to avoid corrupting the original LIST1 and LIST2.
841\nKeywords supported: :test :test-not :key 841\nKeywords supported: :test :test-not :key
@@ -855,7 +855,7 @@ to avoid corrupting the original LIST1 and LIST2.
855;;;###autoload 855;;;###autoload
856(defun nset-difference (cl-list1 cl-list2 &rest cl-keys) 856(defun nset-difference (cl-list1 cl-list2 &rest cl-keys)
857 "Combine LIST1 and LIST2 using a set-difference operation. 857 "Combine LIST1 and LIST2 using a set-difference operation.
858The result list contains all items that appear in LIST1 but not LIST2. 858The resulting list contains all items that appear in LIST1 but not LIST2.
859This is a destructive function; it reuses the storage of LIST1 and LIST2 859This is a destructive function; it reuses the storage of LIST1 and LIST2
860whenever possible. 860whenever possible.
861\nKeywords supported: :test :test-not :key 861\nKeywords supported: :test :test-not :key
@@ -866,7 +866,7 @@ whenever possible.
866;;;###autoload 866;;;###autoload
867(defun set-exclusive-or (cl-list1 cl-list2 &rest cl-keys) 867(defun set-exclusive-or (cl-list1 cl-list2 &rest cl-keys)
868 "Combine LIST1 and LIST2 using a set-exclusive-or operation. 868 "Combine LIST1 and LIST2 using a set-exclusive-or operation.
869The result list contains all items that appear in exactly one of LIST1, LIST2. 869The resulting list contains all items appearing in exactly one of LIST1, LIST2.
870This is a non-destructive function; it makes a copy of the data if necessary 870This is a non-destructive function; it makes a copy of the data if necessary
871to avoid corrupting the original LIST1 and LIST2. 871to avoid corrupting the original LIST1 and LIST2.
872\nKeywords supported: :test :test-not :key 872\nKeywords supported: :test :test-not :key
@@ -879,7 +879,7 @@ to avoid corrupting the original LIST1 and LIST2.
879;;;###autoload 879;;;###autoload
880(defun nset-exclusive-or (cl-list1 cl-list2 &rest cl-keys) 880(defun nset-exclusive-or (cl-list1 cl-list2 &rest cl-keys)
881 "Combine LIST1 and LIST2 using a set-exclusive-or operation. 881 "Combine LIST1 and LIST2 using a set-exclusive-or operation.
882The result list contains all items that appear in exactly one of LIST1, LIST2. 882The resulting list contains all items appearing in exactly one of LIST1, LIST2.
883This is a destructive function; it reuses the storage of LIST1 and LIST2 883This is a destructive function; it reuses the storage of LIST1 and LIST2
884whenever possible. 884whenever possible.
885\nKeywords supported: :test :test-not :key 885\nKeywords supported: :test :test-not :key
diff --git a/lisp/emulation/cua-base.el b/lisp/emulation/cua-base.el
index 3bdd9565fb5..267317594b1 100644
--- a/lisp/emulation/cua-base.el
+++ b/lisp/emulation/cua-base.el
@@ -246,7 +246,7 @@
246;; [C-d] Moves (i.e. deletes and inserts) a single character to the 246;; [C-d] Moves (i.e. deletes and inserts) a single character to the
247;; global mark. 247;; global mark.
248;; [backspace] deletes the character before the global mark, while 248;; [backspace] deletes the character before the global mark, while
249;; [delete] deltes the character after the global mark. 249;; [delete] deletes the character after the global mark.
250 250
251;; [S-C-space] Jumps to and cancels the global mark. 251;; [S-C-space] Jumps to and cancels the global mark.
252;; [C-u S-C-space] Cancels the global mark (stays in current buffer). 252;; [C-u S-C-space] Cancels the global mark (stays in current buffer).
diff --git a/lisp/faces.el b/lisp/faces.el
index 2a0badab370..11c4108644a 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1577,13 +1577,25 @@ Optional parameter FRAME is the frame whose definition of FACE
1577is used. If nil or omitted, use the selected frame." 1577is used. If nil or omitted, use the selected frame."
1578 (unless frame 1578 (unless frame
1579 (setq frame (selected-frame))) 1579 (setq frame (selected-frame)))
1580 (let ((list face-attribute-name-alist) 1580 (let* ((list face-attribute-name-alist)
1581 (match t)) 1581 (match t)
1582 (bold (and (plist-member attrs :bold)
1583 (not (plist-member attrs :weight))))
1584 (italic (and (plist-member attrs :italic)
1585 (not (plist-member attrs :slant))))
1586 (plist (if (or bold italic)
1587 (copy-sequence attrs)
1588 attrs)))
1589 ;; Handle the Emacs 20 :bold and :italic properties.
1590 (if bold
1591 (plist-put plist :weight (if bold 'bold 'normal)))
1592 (if italic
1593 (plist-put plist :slant (if italic 'italic 'normal)))
1582 (while (and match list) 1594 (while (and match list)
1583 (let* ((attr (caar list)) 1595 (let* ((attr (caar list))
1584 (specified-value 1596 (specified-value
1585 (if (plist-member attrs attr) 1597 (if (plist-member plist attr)
1586 (plist-get attrs attr) 1598 (plist-get plist attr)
1587 'unspecified)) 1599 'unspecified))
1588 (value-now (face-attribute face attr frame))) 1600 (value-now (face-attribute face attr frame)))
1589 (setq match (equal specified-value value-now)) 1601 (setq match (equal specified-value value-now))
diff --git a/lisp/gnus/ChangeLog b/lisp/gnus/ChangeLog
index 8781ab3c0ec..747f71f835a 100644
--- a/lisp/gnus/ChangeLog
+++ b/lisp/gnus/ChangeLog
@@ -1,3 +1,43 @@
12011-02-14 Teodor Zlatanov <tzz@lifelogs.com>
2
3 * auth-source.el (auth-source-backend-parse-parameters): Don't rely on
4 `plist-get' to accept non-list parameters (XEmacs issue). Fix
5 docstring.
6
72011-02-14 Lars Ingebrigtsen <larsi@gnus.org>
8
9 * nnimap.el (nnimap-inhibit-logging): New variable.
10 (nnimap-log-command): Don't log login commands.
11
12 * auth-source.el (auth-source-netrc-search): The asserts seem to want
13 to have more parameters.
14
15 * nnimap.el (nnimap-send-command): Mark the command time for each
16 command, so that we don't get NOOPs stepping on our toes.
17
18 * gnus-art.el (article-date-ut): Get the date from the Date header on
19 `t'.
20
212011-02-14 Katsumi Yamaoka <yamaoka@jpl.org>
22
23 * auth-source.el (auth-source-search): Use copy-sequence instead of
24 the cl.el copy-list.
25
262011-02-13 Adam Sjøgren <asjo@koldfront.dk>
27
28 * gnus-delay.el (gnus-delay-article) Fix number of seconds per day.
29 Improve prompt.
30
312011-02-13 Lars Ingebrigtsen <larsi@gnus.org>
32
33 * gnus-art.el (gnus-article-mode-line-format): Remove the article
34 washing status from the default format. It isn't very informative.
35
362011-02-13 Tassilo Horn <tassilo@member.fsf.org> (tiny change)
37
38 * nnimap.el (nnimap-request-accept-article, nnimap-process-quirk): Fix
39 Gcc processing on imap.
40
12011-02-10 Stefan Monnier <monnier@iro.umontreal.ca> 412011-02-10 Stefan Monnier <monnier@iro.umontreal.ca>
2 42
3 * message.el (message-bury): Don't pop up a new window when selected 43 * message.el (message-bury): Don't pop up a new window when selected
@@ -7,6 +47,30 @@
7 47
8 * gnus-sum.el (gnus-summary-save-parts): Use read-directory-name. 48 * gnus-sum.el (gnus-summary-save-parts): Use read-directory-name.
9 49
502011-02-10 Teodor Zlatanov <tzz@lifelogs.com>
51
52 * sieve-manage.el: Autoload `auth-source-search'.
53 (sieve-sasl-auth): Use it.
54
552011-02-09 Teodor Zlatanov <tzz@lifelogs.com>
56
57 * nnimap.el: Autoload `auth-source-forget+'.
58 (nnimap-open-connection-1): Use it if the connection fails.
59
60 * auth-source.el: Require `password-cache'.
61 (auth-source-hide-passwords, auth-source-cache): Remove and mark
62 obsolete.
63 (auth-source-magic): Marker for `password-cache' keys.
64 (auth-source-do-cache): Update docstring.
65 (auth-source-search): Use and check cache.
66 (auth-source-forget-all-cached, auth-source-remember)
67 (auth-source-recall, auth-source-forget, auth-source-forget+)
68 (auth-source-specmatchp): Caching support functions.
69 (auth-source-forget-user-or-password, auth-source-forget-all-cached):
70 Remove and obsolete.
71 (auth-source-user-or-password): Remove caching to further discourage
72 using it. Always hide passwords.
73
102011-02-09 Lars Ingebrigtsen <larsi@gnus.org> 742011-02-09 Lars Ingebrigtsen <larsi@gnus.org>
11 75
12 * nntp.el (nntp-retrieve-group-data-early-disabled): Disable the async 76 * nntp.el (nntp-retrieve-group-data-early-disabled): Disable the async
@@ -17,6 +81,22 @@
17 * message.el (message-options): Make message-options really buffer 81 * message.el (message-options): Make message-options really buffer
18 local. 82 local.
19 83
842011-02-08 Teodor Zlatanov <tzz@lifelogs.com>
85
86 * mail-source.el: Autoload `auth-source-search'.
87 (mail-source-keyword-map): Note order matters.
88 (mail-source-set-1): Get all the mail-source source values and
89 defaults and search auth-source on those if needed. This can all
90 probably be simplified.
91
92 * nnimap.el: Autoload `auth-source-search'.
93 (nnimap-credentials): Use it.
94 (nnimap-open-connection-1): Ask for the virtual server and physical
95 address in one shot.
96
97 * nntp.el: Autoload `auth-source-search'.
98 (nntp-send-authinfo): Use it. Note TODO.
99
202011-02-08 Julien Danjou <julien@danjou.info> 1002011-02-08 Julien Danjou <julien@danjou.info>
21 101
22 * shr.el (shr-tag-body): Add support for text attribute in body 102 * shr.el (shr-tag-body): Add support for text attribute in body
@@ -24,6 +104,13 @@
24 104
25 * message.el (message-options): Make message-options a local variable. 105 * message.el (message-options): Make message-options a local variable.
26 106
1072011-02-07 Teodor Zlatanov <tzz@lifelogs.com>
108
109 * auth-source.el (auth-source-secrets-search)
110 (auth-source-user-or-password): Use `append' instead of `nconc'.
111 (auth-source-user-or-password): Build return list better and protect
112 against nil :secret.
113
272011-02-07 Lars Ingebrigtsen <larsi@gnus.org> 1142011-02-07 Lars Ingebrigtsen <larsi@gnus.org>
28 115
29 * nnimap.el (nnimap-update-info): Refactor slightly. 116 * nnimap.el (nnimap-update-info): Refactor slightly.
@@ -35,6 +122,13 @@
35 (nnimap-update-info): Fix macrology bug-out. 122 (nnimap-update-info): Fix macrology bug-out.
36 (nnimap-update-info): Simplify split history test. 123 (nnimap-update-info): Simplify split history test.
37 124
1252011-02-06 Michael Albinus <michael.albinus@gmx.de>
126
127 * auth-source.el (top): Require 'eieio unconditionally. Autoload
128 `secrets-get-attributes' instead of `secrets-get-attribute'.
129 (auth-source-secrets-search): Limit search when `max' is greater than
130 number of results.
131
382011-02-06 Lars Ingebrigtsen <larsi@gnus.org> 1322011-02-06 Lars Ingebrigtsen <larsi@gnus.org>
39 133
40 * nntp.el (nntp-finish-retrieve-group-infos): Protect against the first 134 * nntp.el (nntp-finish-retrieve-group-infos): Protect against the first
@@ -42,11 +136,58 @@
42 136
43 * proto-stream.el (open-protocol-stream): Document the return value. 137 * proto-stream.el (open-protocol-stream): Document the return value.
44 138
1392011-02-06 Teodor Zlatanov <tzz@lifelogs.com>
140
141 * auth-source.el (auth-source-secrets-search): Add examples.
142
452011-02-06 Julien Danjou <julien@danjou.info> 1432011-02-06 Julien Danjou <julien@danjou.info>
46 144
47 * message.el (message-setup-1): Handle message-generate-headers-first 145 * message.el (message-setup-1): Handle message-generate-headers-first
48 set to t. 146 set to t.
49 147
1482011-02-06 Teodor Zlatanov <tzz@lifelogs.com>
149
150 * auth-source.el (auth-sources): Allow for simpler defaults for Secrets
151 API with a string "secrets:collection-name" and with 'default.
152 (auth-source-backend-parse): Parse "secrets:collection-name" and
153 'default. Recurse on parses instead of repeating code. Use the
154 Secrets API is the source is not nil and 'ignore otherwise. Emit a
155 message when ignoring a source.
156 (auth-source-search): List ignored search keys at the top level.
157 (auth-source-netrc-create): Use `case' instead of `cond'.
158 (auth-source-secrets-search): Created with TODOs.
159 (auth-source-secrets-create): Created with TODOs.
160 (auth-source-retrieve, auth-source-create, auth-source-delete)
161 (auth-source-protocol-defaults, auth-source-user-or-password-imap)
162 (auth-source-user-or-password-pop3, auth-source-user-or-password-ssh)
163 (auth-source-user-or-password-sftp)
164 (auth-source-user-or-password-smtp): Removed.
165 (auth-source-user-or-password): Deprecated and modified to be a wrapper
166 around `auth-source-search'. Not tested thoroughly.
167
1682011-02-04 Teodor Zlatanov <tzz@lifelogs.com>
169
170 * auth-source.el: Bring in assoc and eioeio libraries.
171 (secrets-enabled): New variable to track the status of the Secrets API.
172 (auth-source-backend): New EIOEIO class to represent a backend.
173 (auth-source-creation-defaults): New variable to set prompt defaults
174 during token creation (see the `auth-source-search' docstring for
175 details).
176 (auth-sources): Simplify to allow a simple string as a netrc backend
177 spec.
178 (auth-source-backend-parse): Parse a backend from an `auth-sources' spec.
179 (auth-source-backend-parse-parameters): Fill in the backend parameters.
180 (auth-source-search): Main auth-source API entry point.
181 (auth-source-delete): Wrapper around `auth-source-search' for deletion.
182 (auth-source-search-collection): Helper function for searching.
183 (auth-source-netrc-parse, auth-source-netrc-normalize)
184 (auth-source-netrc-search, auth-source-netrc-create): Netrc backend.
185 Supports search, create, and delete.
186 (auth-source-secrets-search, auth-source-secrets-create): Secrets API
187 backend stubs.
188 (auth-source-user-or-password): Call `auth-source-search' but it's not
189 ready yet.
190
502011-02-04 Lars Ingebrigtsen <larsi@gnus.org> 1912011-02-04 Lars Ingebrigtsen <larsi@gnus.org>
51 192
52 * message.el (message-setup-1): Remove the read-only stuff, since it 193 * message.el (message-setup-1): Remove the read-only stuff, since it
diff --git a/lisp/gnus/ChangeLog.2 b/lisp/gnus/ChangeLog.2
index 381ae544b24..4882032f284 100644
--- a/lisp/gnus/ChangeLog.2
+++ b/lisp/gnus/ChangeLog.2
@@ -6088,7 +6088,7 @@
6088 (nntp-retrieve-groups): Ditto for groups. 6088 (nntp-retrieve-groups): Ditto for groups.
6089 (nntp-retrieve-articles): Ditto for articles. 6089 (nntp-retrieve-articles): Ditto for articles.
6090 (*): Replaced nntp-possibly-change-group calls to 6090 (*): Replaced nntp-possibly-change-group calls to
6091 nntp-with-open-group forms in all, but one, occurrance. 6091 nntp-with-open-group forms in all, but one, occurrence.
6092 (nntp-accept-process-output): Bug fix. Detect when called with 6092 (nntp-accept-process-output): Bug fix. Detect when called with
6093 null process. 6093 null process.
6094 6094
diff --git a/lisp/gnus/auth-source.el b/lisp/gnus/auth-source.el
index e94cfb137b0..be698ad35d0 100644
--- a/lisp/gnus/auth-source.el
+++ b/lisp/gnus/auth-source.el
@@ -39,23 +39,64 @@
39 39
40;;; Code: 40;;; Code:
41 41
42(require 'password-cache)
42(require 'gnus-util) 43(require 'gnus-util)
43(require 'netrc) 44(require 'netrc)
44 45(require 'assoc)
45(eval-when-compile (require 'cl)) 46(eval-when-compile (require 'cl))
47(require 'eieio)
48
46(autoload 'secrets-create-item "secrets") 49(autoload 'secrets-create-item "secrets")
47(autoload 'secrets-delete-item "secrets") 50(autoload 'secrets-delete-item "secrets")
48(autoload 'secrets-get-alias "secrets") 51(autoload 'secrets-get-alias "secrets")
49(autoload 'secrets-get-attribute "secrets") 52(autoload 'secrets-get-attributes "secrets")
50(autoload 'secrets-get-secret "secrets") 53(autoload 'secrets-get-secret "secrets")
51(autoload 'secrets-list-collections "secrets") 54(autoload 'secrets-list-collections "secrets")
52(autoload 'secrets-search-items "secrets") 55(autoload 'secrets-search-items "secrets")
53 56
57(defvar secrets-enabled)
58
54(defgroup auth-source nil 59(defgroup auth-source nil
55 "Authentication sources." 60 "Authentication sources."
56 :version "23.1" ;; No Gnus 61 :version "23.1" ;; No Gnus
57 :group 'gnus) 62 :group 'gnus)
58 63
64(defclass auth-source-backend ()
65 ((type :initarg :type
66 :initform 'netrc
67 :type symbol
68 :custom symbol
69 :documentation "The backend type.")
70 (source :initarg :source
71 :type string
72 :custom string
73 :documentation "The backend source.")
74 (host :initarg :host
75 :initform t
76 :type t
77 :custom string
78 :documentation "The backend host.")
79 (user :initarg :user
80 :initform t
81 :type t
82 :custom string
83 :documentation "The backend user.")
84 (protocol :initarg :protocol
85 :initform t
86 :type t
87 :custom string
88 :documentation "The backend protocol.")
89 (create-function :initarg :create-function
90 :initform ignore
91 :type function
92 :custom function
93 :documentation "The create function.")
94 (search-function :initarg :search-function
95 :initform ignore
96 :type function
97 :custom function
98 :documentation "The search function.")))
99
59(defcustom auth-source-protocols '((imap "imap" "imaps" "143" "993") 100(defcustom auth-source-protocols '((imap "imap" "imaps" "143" "993")
60 (pop3 "pop3" "pop" "pop3s" "110" "995") 101 (pop3 "pop3" "pop" "pop3s" "110" "995")
61 (ssh "ssh" "22") 102 (ssh "ssh" "22")
@@ -81,11 +122,15 @@
81 p))) 122 p)))
82 auth-source-protocols)) 123 auth-source-protocols))
83 124
84(defvar auth-source-cache (make-hash-table :test 'equal) 125(defvar auth-source-creation-defaults nil
85 "Cache for auth-source data") 126 "Defaults for creating token values. Usually let-bound.")
127
128(make-obsolete 'auth-source-hide-passwords nil "Emacs 24.1")
129
130(defvar auth-source-magic "auth-source-magic ")
86 131
87(defcustom auth-source-do-cache t 132(defcustom auth-source-do-cache t
88 "Whether auth-source should cache information." 133 "Whether auth-source should cache information with `password-cache'."
89 :group 'auth-source 134 :group 'auth-source
90 :version "23.2" ;; No Gnus 135 :version "23.2" ;; No Gnus
91 :type `boolean) 136 :type `boolean)
@@ -108,65 +153,71 @@ If the value is a function, debug messages are logged by calling
108 (function :tag "Function that takes arguments like `message'") 153 (function :tag "Function that takes arguments like `message'")
109 (const :tag "Don't log anything" nil))) 154 (const :tag "Don't log anything" nil)))
110 155
111(defcustom auth-source-hide-passwords t 156(defcustom auth-sources '("~/.authinfo.gpg" "~/.authinfo")
112 "Whether auth-source should hide passwords in log messages.
113Only relevant if `auth-source-debug' is not nil."
114 :group 'auth-source
115 :version "23.2" ;; No Gnus
116 :type `boolean)
117
118(defcustom auth-sources '((:source "~/.authinfo.gpg")
119 (:source "~/.authinfo"))
120 "List of authentication sources. 157 "List of authentication sources.
121 158
122The default will get login and password information from a .gpg 159The default will get login and password information from
123file, which you should set up with the EPA/EPG packages to be 160\"~/.authinfo.gpg\", which you should set up with the EPA/EPG
124encrypted. See the auth.info manual for details. 161packages to be encrypted. If that file doesn't exist, it will
162try the unencrypted version \"~/.authinfo\".
163
164See the auth.info manual for details.
125 165
126Each entry is the authentication type with optional properties. 166Each entry is the authentication type with optional properties.
127 167
128It's best to customize this with `M-x customize-variable' because the choices 168It's best to customize this with `M-x customize-variable' because the choices
129can get pretty complex." 169can get pretty complex."
130 :group 'auth-source 170 :group 'auth-source
131 :version "23.2" ;; No Gnus 171 :version "24.1" ;; No Gnus
132 :type `(repeat :tag "Authentication Sources" 172 :type `(repeat :tag "Authentication Sources"
133 (list :tag "Source definition" 173 (choice
134 (const :format "" :value :source) 174 (string :tag "Just a file")
135 (choice :tag "Authentication backend choice" 175 (const :tag "Default Secrets API Collection" 'default)
136 (string :tag "Authentication Source (file)") 176 (const :tag "Login Secrets API Collection" "secrets:login")
137 (list :tag "secrets.el (Secret Service API/KWallet/GNOME Keyring)" 177 (const :tag "Temp Secrets API Collection" "secrets:session")
138 (const :format "" :value :secrets) 178 (list :tag "Source definition"
139 (choice :tag "Collection to use" 179 (const :format "" :value :source)
140 (string :tag "Collection name") 180 (choice :tag "Authentication backend choice"
141 (const :tag "Default" 'default) 181 (string :tag "Authentication Source (file)")
142 (const :tag "Login" "login") 182 (list
143 (const :tag "Temporary" "session")))) 183 :tag "Secret Service API/KWallet/GNOME Keyring"
144 (repeat :tag "Extra Parameters" :inline t 184 (const :format "" :value :secrets)
145 (choice :tag "Extra parameter" 185 (choice :tag "Collection to use"
146 (list :tag "Host (omit to match as a fallback)" 186 (string :tag "Collection name")
147 (const :format "" :value :host) 187 (const :tag "Default" 'default)
148 (choice :tag "Host (machine) choice" 188 (const :tag "Login" "login")
149 (const :tag "Any" t) 189 (const
150 (regexp :tag "Host (machine) regular expression"))) 190 :tag "Temporary" "session"))))
151 (list :tag "Protocol (omit to match as a fallback)" 191 (repeat :tag "Extra Parameters" :inline t
152 (const :format "" :value :protocol) 192 (choice :tag "Extra parameter"
153 (choice :tag "Protocol" 193 (list
154 (const :tag "Any" t) 194 :tag "Host"
155 ,@auth-source-protocols-customize)) 195 (const :format "" :value :host)
156 (list :tag "User (omit to match as a fallback)" :inline t 196 (choice :tag "Host (machine) choice"
157 (const :format "" :value :user) 197 (const :tag "Any" t)
158 (choice :tag "Personality or username" 198 (regexp
159 (const :tag "Any" t) 199 :tag "Regular expression")))
160 (string :tag "Specific user name")))))))) 200 (list
201 :tag "Protocol"
202 (const :format "" :value :protocol)
203 (choice
204 :tag "Protocol"
205 (const :tag "Any" t)
206 ,@auth-source-protocols-customize))
207 (list :tag "User" :inline t
208 (const :format "" :value :user)
209 (choice :tag "Personality/Username"
210 (const :tag "Any" t)
211 (string :tag "Name")))))))))
161 212
162(defcustom auth-source-gpg-encrypt-to t 213(defcustom auth-source-gpg-encrypt-to t
163 "List of recipient keys that `authinfo.gpg' encrypted to. 214 "List of recipient keys that `authinfo.gpg' encrypted to.
164If the value is not a list, symmetric encryption will be used." 215If the value is not a list, symmetric encryption will be used."
165 :group 'auth-source 216 :group 'auth-source
166 :version "23.2" ;; No Gnus 217 :version "24.1" ;; No Gnus
167 :type '(choice (const :tag "Symmetric encryption" t) 218 :type '(choice (const :tag "Symmetric encryption" t)
168 (repeat :tag "Recipient public keys" 219 (repeat :tag "Recipient public keys"
169 (string :tag "Recipient public key")))) 220 (string :tag "Recipient public key"))))
170 221
171;; temp for debugging 222;; temp for debugging
172;; (unintern 'auth-source-protocols) 223;; (unintern 'auth-source-protocols)
@@ -211,229 +262,801 @@ If the value is not a list, symmetric encryption will be used."
211 262
212;; (setq auth-sources '((:source "~/.authinfo.gpg" :host t :protocol t))) 263;; (setq auth-sources '((:source "~/.authinfo.gpg" :host t :protocol t)))
213 264
214(defun auth-get-source (entry) 265;; (auth-source-backend-parse "myfile.gpg")
215 "Return the source string of ENTRY, which is one entry in `auth-sources'. 266;; (auth-source-backend-parse 'default)
216If it is a Secret Service API, return the collection name, otherwise 267;; (auth-source-backend-parse "secrets:login")
217the file name." 268
218 (let ((source (plist-get entry :source))) 269(defun auth-source-backend-parse (entry)
219 (if (stringp source) 270 "Creates an auth-source-backend from an ENTRY in `auth-sources'."
220 source 271 (auth-source-backend-parse-parameters
221 ;; Secret Service API. 272 entry
222 (setq source (plist-get source :secrets)) 273 (cond
223 (when (eq source 'default) 274 ;; take 'default and recurse to get it as a Secrets API default collection
224 (setq source (or (secrets-get-alias "default") "login"))) 275 ;; matching any user, host, and protocol
225 (or source "session")))) 276 ((eq entry 'default)
226 277 (auth-source-backend-parse '(:source (:secrets default))))
227(defun auth-source-pick (&rest spec) 278 ;; take secrets:XYZ and recurse to get it as Secrets API collection "XYZ"
228 "Parse `auth-sources' for matches of the SPEC plist. 279 ;; matching any user, host, and protocol
229 280 ((and (stringp entry) (string-match "^secrets:\\(.+\\)" entry))
230Common keys are :host, :protocol, and :user. A value of t in 281 (auth-source-backend-parse `(:source (:secrets ,(match-string 1 entry)))))
231SPEC means to always succeed in the match. A string value is 282 ;; take just a file name and recurse to get it as a netrc file
232matched as a regex." 283 ;; matching any user, host, and protocol
233 (let ((keys (loop for i below (length spec) by 2 collect (nth i spec))) 284 ((stringp entry)
234 choices) 285 (auth-source-backend-parse `(:source ,entry)))
235 (dolist (choice (copy-tree auth-sources) choices) 286
236 (let ((source (plist-get choice :source)) 287 ;; a file name with parameters
237 (match t)) 288 ((stringp (plist-get entry :source))
238 (when 289 (auth-source-backend
239 (and 290 (plist-get entry :source)
240 ;; Check existence of source. 291 :source (plist-get entry :source)
241 (if (consp source) 292 :type 'netrc
242 ;; Secret Service API. 293 :search-function 'auth-source-netrc-search
243 (member (auth-get-source choice) (secrets-list-collections)) 294 :create-function 'auth-source-netrc-create))
244 ;; authinfo file. 295
245 (file-exists-p source)) 296 ;; the Secrets API. We require the package, in order to have a
246 297 ;; defined value for `secrets-enabled'.
247 ;; Check keywords. 298 ((and
248 (dolist (k keys match) 299 (not (null (plist-get entry :source))) ; the source must not be nil
249 (let* ((v (plist-get spec k)) 300 (listp (plist-get entry :source)) ; and it must be a list
250 (choicev (if (plist-member choice k) 301 (require 'secrets nil t) ; and we must load the Secrets API
251 (plist-get choice k) t))) 302 secrets-enabled) ; and that API must be enabled
252 (setq match 303
253 (and match 304 ;; the source is either the :secrets key in ENTRY or
254 (or 305 ;; if that's missing or nil, it's "session"
255 ;; source always matches spec key 306 (let ((source (or (plist-get (plist-get entry :source) :secrets)
256 (eq t choicev) 307 "session")))
257 ;; source key gives regex to match against spec 308
258 (and (stringp choicev) (string-match choicev v)) 309 ;; if the source is a symbol, we look for the alias named so,
259 ;; source key gives symbol to match against spec 310 ;; and if that alias is missing, we use "login"
260 (and (symbolp choicev) (eq choicev v)))))))) 311 (when (symbolp source)
261 312 (setq source (or (secrets-get-alias (symbol-name source))
262 (add-to-list 'choices choice 'append)))))) 313 "login")))
263 314
264(defun auth-source-retrieve (mode entry &rest spec) 315 (auth-source-backend
265 "Retrieve MODE credentials according to SPEC from ENTRY." 316 (format "Secrets API (%s)" source)
266 (catch 'no-password 317 :source source
267 (let ((host (plist-get spec :host)) 318 :type 'secrets
268 (user (plist-get spec :user)) 319 :search-function 'auth-source-secrets-search
269 (prot (plist-get spec :protocol)) 320 :create-function 'auth-source-secrets-create)))
270 (source (plist-get entry :source)) 321
271 result) 322 ;; none of them
272 (cond 323 (t
273 ;; Secret Service API. 324 (auth-source-do-debug
274 ((consp source) 325 "auth-source-backend-parse: invalid backend spec: %S" entry)
275 (let ((coll (auth-get-source entry)) 326 (auth-source-backend
276 item) 327 "Empty"
277 ;; Loop over candidates with a matching host attribute. 328 :source ""
278 (dolist (elt (secrets-search-items coll :host host) item) 329 :type 'ignore)))))
279 (when (and (or (not user) 330
280 (string-equal 331(defun auth-source-backend-parse-parameters (entry backend)
281 user (secrets-get-attribute coll elt :user))) 332 "Fills in the extra auth-source-backend parameters of ENTRY.
282 (or (not prot) 333Using the plist ENTRY, get the :host, :protocol, and :user search
283 (string-equal 334parameters. Accepts :port as an alias to :protocol."
284 prot (secrets-get-attribute coll elt :protocol)))) 335 (let ((entry (if (stringp entry)
285 (setq item elt) 336 nil
286 (return elt))) 337 entry))
287 ;; Compose result. 338 val)
288 (when item 339 (when (setq val (plist-get entry :host))
289 (setq result 340 (oset backend host val))
290 (mapcar (lambda (m) 341 (when (setq val (plist-get entry :user))
291 (if (string-equal "password" m) 342 (oset backend user val))
292 (or (secrets-get-secret coll item) 343 ;; accept :port as an alias for :protocol
293 ;; When we do not find a password, 344 (when (setq val (or (plist-get entry :protocol) (plist-get entry :port)))
294 ;; we return nil anyway. 345 (oset backend protocol val)))
295 (throw 'no-password nil)) 346 backend)
296 (or (secrets-get-attribute coll item :user) 347
297 user))) 348;; (mapcar 'auth-source-backend-parse auth-sources)
298 (if (consp mode) mode (list mode))))) 349
299 (if (consp mode) result (car result)))) 350(defun* auth-source-search (&rest spec
300 ;; Anything else is netrc. 351 &key type max host user protocol secret
301 (t 352 create delete
302 (let ((search (list source (list host) (list (format "%s" prot)) 353 &allow-other-keys)
303 (auth-source-protocol-defaults prot)))) 354 "Search or modify authentication backends according to SPEC.
304 (setq result 355
305 (mapcar (lambda (m) 356This function parses `auth-sources' for matches of the SPEC
306 (if (string-equal "password" m) 357plist. It can optionally create or update an authentication
307 (or (apply 358token if requested. A token is just a standard Emacs property
308 'netrc-machine-user-or-password m search) 359list with a :secret property that can be a function; all the
309 ;; When we do not find a password, we 360other properties will always hold scalar values.
310 ;; return nil anyway. 361
311 (throw 'no-password nil)) 362Typically the :secret property, if present, contains a password.
312 (or (apply 363
313 'netrc-machine-user-or-password m search) 364Common search keys are :max, :host, :protocol, and :user. In
314 user))) 365addition, :create specifies how tokens will be or created.
315 (if (consp mode) mode (list mode))))) 366Finally, :type can specify which backend types you want to check.
316 (if (consp mode) result (car result))))))) 367
317 368A string value is always matched literally. A symbol is matched
318(defun auth-source-create (mode entry &rest spec) 369as its string value, literally. All the SPEC values can be
319 "Create interactively credentials according to SPEC in ENTRY. 370single values (symbol or string) or lists thereof (in which case
320Return structure as specified by MODE." 371any of the search terms matches).
321 (let* ((host (plist-get spec :host)) 372
322 (user (plist-get spec :user)) 373:create t means to create a token if possible.
323 (prot (plist-get spec :protocol)) 374
324 (source (plist-get entry :source)) 375A new token will be created if no matching tokens were found.
325 (name (concat (if user (format "%s@" user)) 376The new token will have only the keys the backend requires. For
326 host 377the netrc backend, for instance, that's the user, host, and
327 (if prot (format ":%s" prot)))) 378protocol keys.
328 result) 379
329 (setq result 380Here's an example:
330 (mapcar 381
331 (lambda (m) 382\(let ((auth-source-creation-defaults '((user . \"defaultUser\")
332 (cons 383 (A . \"default A\"))))
333 m 384 (auth-source-search :host \"mine\" :type 'netrc :max 1
334 (cond 385 :P \"pppp\" :Q \"qqqq\"
335 ((equal "password" m) 386 :create t))
336 (let ((passwd (read-passwd 387
337 (format "Password for %s on %s: " prot host)))) 388which says:
338 (cond 389
339 ;; Secret Service API. 390\"Search for any entry matching host 'mine' in backends of type
340 ((consp source) 391 'netrc', maximum one result.
341 (apply 392
342 'secrets-create-item 393 Create a new entry if you found none. The netrc backend will
343 (auth-get-source entry) name passwd spec)) 394 automatically require host, user, and protocol. The host will be
344 (t)) ;; netrc not implemented yes. 395 'mine'. We prompt for the user with default 'defaultUser' and
345 passwd)) 396 for the protocol without a default. We will not prompt for A, Q,
346 ((equal "login" m) 397 or P. The resulting token will only have keys user, host, and
347 (or user 398 protocol.\"
348 (read-string 399
349 (format "User name for %s on %s (default %s): " prot host 400:create '(A B C) also means to create a token if possible.
350 (user-login-name)) 401
351 nil nil (user-login-name)))) 402The behavior is like :create t but if the list contains any
352 (t 403parameter, that parameter will be required in the resulting
353 "unknownuser")))) 404token. The value for that parameter will be obtained from the
354 (if (consp mode) mode (list mode)))) 405search parameters or from user input. If any queries are needed,
355 ;; Allow the source to save the data. 406the alist `auth-source-creation-defaults' will be checked for the
356 (cond 407default prompt.
357 ((consp source) 408
358 ;; Secret Service API -- not implemented. 409Here's an example:
359 ) 410
360 (t 411\(let ((auth-source-creation-defaults '((user . \"defaultUser\")
361 ;; netrc interface. 412 (A . \"default A\"))))
362 (when (y-or-n-p (format "Do you want to save this password in %s? " 413 (auth-source-search :host '(\"nonesuch\" \"twosuch\") :type 'netrc :max 1
363 source)) 414 :P \"pppp\" :Q \"qqqq\"
364 ;; the code below is almost same as `netrc-store-data' except 415 :create '(A B Q)))
365 ;; the `epa-file-encrypt-to' hack (see bug#7487). 416
366 (with-temp-buffer 417which says:
367 (when (file-exists-p source) 418
368 (insert-file-contents source)) 419\"Search for any entry matching host 'nonesuch'
369 (when auth-source-gpg-encrypt-to 420 or 'twosuch' in backends of type 'netrc', maximum one result.
370 ;; making `epa-file-encrypt-to' local to this buffer lets 421
371 ;; epa-file skip the key selection query (see the 422 Create a new entry if you found none. The netrc backend will
372 ;; `local-variable-p' check in `epa-file-write-region'). 423 automatically require host, user, and protocol. The host will be
373 (unless (local-variable-p 'epa-file-encrypt-to (current-buffer)) 424 'nonesuch' and Q will be 'qqqq'. We prompt for A with default
374 (make-local-variable 'epa-file-encrypt-to)) 425 'default A', for B and protocol with default nil, and for the
375 (if (listp auth-source-gpg-encrypt-to) 426 user with default 'defaultUser'. We will not prompt for Q. The
376 (setq epa-file-encrypt-to auth-source-gpg-encrypt-to))) 427 resulting token will have keys user, host, protocol, A, B, and Q.
377 (goto-char (point-max)) 428 It will not have P with any value, even though P is used in the
378 (unless (bolp) 429 search to find only entries that have P set to 'pppp'.\"
379 (insert "\n")) 430
380 (insert (format "machine %s login %s password %s port %s\n" 431When multiple values are specified in the search parameter, the
381 host 432first one is used for creation. So :host (X Y Z) would create a
382 (or user (cdr (assoc "login" result))) 433token for host X, for instance.
383 (cdr (assoc "password" result)) 434
384 prot)) 435This creation can fail if the search was not specific enough to
385 (write-region (point-min) (point-max) source nil 'silent))))) 436create a new token (it's up to the backend to decide that). You
386 (if (consp mode) 437should `catch' the backend-specific error as usual. Some
387 (mapcar #'cdr result) 438backends (netrc, at least) will prompt the user rather than throw
388 (cdar result)))) 439an error.
389 440
390(defun auth-source-delete (entry &rest spec) 441:delete t means to delete any found entries. nil by default.
391 "Delete credentials according to SPEC in ENTRY." 442Use `auth-source-delete' in ELisp code instead of calling
392 (let ((host (plist-get spec :host)) 443`auth-source-search' directly with this parameter.
393 (user (plist-get spec :user)) 444
394 (prot (plist-get spec :protocol)) 445:type (X Y Z) will check only those backend types. 'netrc and
395 (source (plist-get entry :source))) 446'secrets are the only ones supported right now.
396 (cond 447
397 ;; Secret Service API. 448:max N means to try to return at most N items (defaults to 1).
398 ((consp source) 449When 0 the function will return just t or nil to indicate if any
399 (let ((coll (auth-get-source entry))) 450matches were found. More than N items may be returned, depending
400 ;; Loop over candidates with a matching host attribute. 451on the search and the backend.
401 (dolist (elt (secrets-search-items coll :host host)) 452
402 (when (and (or (not user) 453:host (X Y Z) means to match only hosts X, Y, or Z according to
403 (string-equal 454the match rules above. Defaults to t.
404 user (secrets-get-attribute coll elt :user))) 455
405 (or (not prot) 456:user (X Y Z) means to match only users X, Y, or Z according to
406 (string-equal 457the match rules above. Defaults to t.
407 prot (secrets-get-attribute coll elt :protocol)))) 458
408 (secrets-delete-item coll elt))))) 459:protocol (P Q R) means to match only protocols P, Q, or R.
409 (t)))) ;; netrc not implemented yes. 460Defaults to t.
410 461
411(defun auth-source-forget-user-or-password 462:K (V1 V2 V3) for any other key K will match values V1, V2, or
412 (mode host protocol &optional username) 463V3 (note the match rules above).
413 "Remove cached authentication token." 464
414 (interactive "slogin/password: \nsHost: \nsProtocol: \n") ;for testing 465The return value is a list with at most :max tokens. Each token
415 (remhash 466is a plist with keys :backend :host :protocol :user, plus any other
416 (if username 467keys provided by the backend (notably :secret). But note the
417 (format "%s %s:%s %s" mode host protocol username) 468exception for :max 0, which see above.
418 (format "%s %s:%s" mode host protocol)) 469
419 auth-source-cache)) 470The token's :secret key can hold a function. In that case you
471must call it to obtain the actual value."
472 (let* ((backends (mapcar 'auth-source-backend-parse auth-sources))
473 (max (or max 1))
474 (ignored-keys '(:create :delete :max))
475 (keys (loop for i below (length spec) by 2
476 unless (memq (nth i spec) ignored-keys)
477 collect (nth i spec)))
478 (found (auth-source-recall spec))
479 filtered-backends accessor-key found-here goal)
480
481 (if (and found auth-source-do-cache)
482 (auth-source-do-debug
483 "auth-source-search: found %d CACHED results matching %S"
484 (length found) spec)
485
486 (assert
487 (or (eq t create) (listp create)) t
488 "Invalid auth-source :create parameter (must be nil, t, or a list): %s %s")
489
490 (setq filtered-backends (copy-sequence backends))
491 (dolist (backend backends)
492 (dolist (key keys)
493 ;; ignore invalid slots
494 (condition-case signal
495 (unless (eval `(auth-source-search-collection
496 (plist-get spec key)
497 (oref backend ,key)))
498 (setq filtered-backends (delq backend filtered-backends))
499 (return))
500 (invalid-slot-name))))
501
502 (auth-source-do-debug
503 "auth-source-search: found %d backends matching %S"
504 (length filtered-backends) spec)
505
506 ;; (debug spec "filtered" filtered-backends)
507 (setq goal max)
508 (dolist (backend filtered-backends)
509 (setq found-here (apply
510 (slot-value backend 'search-function)
511 :backend backend
512 :create create
513 :delete delete
514 spec))
515
516 ;; if max is 0, as soon as we find something, return it
517 (when (and (zerop max) (> 0 (length found-here)))
518 (return t))
519
520 ;; decrement the goal by the number of new results
521 (decf goal (length found-here))
522 ;; and append the new results to the full list
523 (setq found (append found found-here))
524
525 (auth-source-do-debug
526 "auth-source-search: found %d results (max %d/%d) in %S matching %S"
527 (length found-here) max goal backend spec)
528
529 ;; return full list if the goal is 0 or negative
530 (when (zerop (max 0 goal))
531 (return found))
532
533 ;; change the :max parameter in the spec to the goal
534 (setq spec (plist-put spec :max goal)))
535
536 (when (and found auth-source-do-cache)
537 (auth-source-remember spec found)))
538
539 found))
540
541;;; (auth-source-search :max 1)
542;;; (funcall (plist-get (nth 0 (auth-source-search :max 1)) :secret))
543;;; (auth-source-search :host "nonesuch" :type 'netrc :K 1)
544;;; (auth-source-search :host "nonesuch" :type 'secrets)
545
546(defun* auth-source-delete (&rest spec
547 &key delete
548 &allow-other-keys)
549 "Delete entries from the authentication backends according to SPEC.
550Calls `auth-source-search' with the :delete property in SPEC set to t.
551The backend may not actually delete the entries.
552
553Returns the deleted entries."
554 (auth-source-search (plist-put spec :delete t)))
555
556(defun auth-source-search-collection (collection value)
557 "Returns t is VALUE is t or COLLECTION is t or contains VALUE."
558 (when (and (atom collection) (not (eq t collection)))
559 (setq collection (list collection)))
560
561 ;; (debug :collection collection :value value)
562 (or (eq collection t)
563 (eq value t)
564 (equal collection value)
565 (member value collection)))
420 566
421(defun auth-source-forget-all-cached () 567(defun auth-source-forget-all-cached ()
422 "Forget all cached auth-source authentication tokens." 568 "Forget all cached auth-source data."
423 (interactive) 569 (interactive)
424 (setq auth-source-cache (make-hash-table :test 'equal))) 570 (loop for sym being the symbols of password-data
571 ;; when the symbol name starts with auth-source-magic
572 when (string-match (concat "^" auth-source-magic)
573 (symbol-name sym))
574 ;; remove that key
575 do (password-cache-remove (symbol-name sym))))
576
577(defun auth-source-remember (spec found)
578 "Remember FOUND search results for SPEC."
579 (password-cache-add
580 (concat auth-source-magic (format "%S" spec)) found))
581
582(defun auth-source-recall (spec)
583 "Recall FOUND search results for SPEC."
584 (password-read-from-cache
585 (concat auth-source-magic (format "%S" spec))))
586
587(defun auth-source-forget (spec)
588 "Forget any cached data matching SPEC exactly.
589
590This is the same SPEC you passed to `auth-source-search'.
591Returns t or nil for forgotten or not found."
592 (password-cache-remove (concat auth-source-magic (format "%S" spec))))
593
594;;; (loop for sym being the symbols of password-data when (string-match (concat "^" auth-source-magic) (symbol-name sym)) collect (symbol-name sym))
595
596;;; (auth-source-remember '(:host "wedd") '(4 5 6))
597;;; (auth-source-remember '(:host "xedd") '(1 2 3))
598;;; (auth-source-recall '(:host "xedd"))
599;;; (auth-source-recall '(:host t))
600;;; (auth-source-forget+ :host t)
601
602(defun* auth-source-forget+ (&rest spec &allow-other-keys)
603 "Forget any cached data matching SPEC. Returns forgotten count.
604
605This is not a full `auth-source-search' spec but works similarly.
606For instance, \(:host \"myhost\" \"yourhost\") would find all the
607cached data that was found with a search for those two hosts,
608while \(:host t) would find all host entries."
609 (let ((count 0)
610 sname)
611 (loop for sym being the symbols of password-data
612 ;; when the symbol name matches with auth-source-magic
613 when (and (setq sname (symbol-name sym))
614 (string-match (concat "^" auth-source-magic "\\(.+\\)")
615 sname)
616 ;; and the spec matches what was stored in the cache
617 (auth-source-specmatchp spec (read (match-string 1 sname))))
618 ;; remove that key
619 do (progn
620 (password-cache-remove sname)
621 (incf count)))
622 count))
623
624(defun auth-source-specmatchp (spec stored)
625 (let ((keys (loop for i below (length spec) by 2
626 collect (nth i spec))))
627 (not (eq
628 (dolist (key keys)
629 (unless (auth-source-search-collection (plist-get stored key)
630 (plist-get spec key))
631 (return 'no)))
632 'no))))
633
634;;; Backend specific parsing: netrc/authinfo backend
635
636;;; (auth-source-netrc-parse "~/.authinfo.gpg")
637(defun* auth-source-netrc-parse (&rest
638 spec
639 &key file max host user protocol delete
640 &allow-other-keys)
641 "Parse FILE and return a list of all entries in the file.
642Note that the MAX parameter is used so we can exit the parse early."
643 (if (listp file)
644 ;; We got already parsed contents; just return it.
645 file
646 (when (file-exists-p file)
647 (with-temp-buffer
648 (let ((tokens '("machine" "host" "default" "login" "user"
649 "password" "account" "macdef" "force"
650 "port" "protocol"))
651 (max (or max 5000)) ; sanity check: default to stop at 5K
652 (modified 0)
653 alist elem result pair)
654 (insert-file-contents file)
655 (goto-char (point-min))
656 ;; Go through the file, line by line.
657 (while (and (not (eobp))
658 (> max 0))
659
660 (narrow-to-region (point) (point-at-eol))
661 ;; For each line, get the tokens and values.
662 (while (not (eobp))
663 (skip-chars-forward "\t ")
664 ;; Skip lines that begin with a "#".
665 (if (eq (char-after) ?#)
666 (goto-char (point-max))
667 (unless (eobp)
668 (setq elem
669 (if (= (following-char) ?\")
670 (read (current-buffer))
671 (buffer-substring
672 (point) (progn (skip-chars-forward "^\t ")
673 (point)))))
674 (cond
675 ((equal elem "macdef")
676 ;; We skip past the macro definition.
677 (widen)
678 (while (and (zerop (forward-line 1))
679 (looking-at "$")))
680 (narrow-to-region (point) (point)))
681 ((member elem tokens)
682 ;; Tokens that don't have a following value are ignored,
683 ;; except "default".
684 (when (and pair (or (cdr pair)
685 (equal (car pair) "default")))
686 (push pair alist))
687 (setq pair (list elem)))
688 (t
689 ;; Values that haven't got a preceding token are ignored.
690 (when pair
691 (setcdr pair elem)
692 (push pair alist)
693 (setq pair nil)))))))
694
695 (when (and alist
696 (> max 0)
697 (auth-source-search-collection
698 host
699 (or
700 (aget alist "machine")
701 (aget alist "host")))
702 (auth-source-search-collection
703 user
704 (or
705 (aget alist "login")
706 (aget alist "account")
707 (aget alist "user")))
708 (auth-source-search-collection
709 protocol
710 (or
711 (aget alist "port")
712 (aget alist "protocol"))))
713 (decf max)
714 (push (nreverse alist) result)
715 ;; to delete a line, we just comment it out
716 (when delete
717 (goto-char (point-min))
718 (insert "#")
719 (incf modified)))
720 (setq alist nil
721 pair nil)
722 (widen)
723 (forward-line 1))
724
725 (when (< 0 modified)
726 (when auth-source-gpg-encrypt-to
727 ;; (see bug#7487) making `epa-file-encrypt-to' local to
728 ;; this buffer lets epa-file skip the key selection query
729 ;; (see the `local-variable-p' check in
730 ;; `epa-file-write-region').
731 (unless (local-variable-p 'epa-file-encrypt-to (current-buffer))
732 (make-local-variable 'epa-file-encrypt-to))
733 (if (listp auth-source-gpg-encrypt-to)
734 (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
735
736 ;; ask AFTER we've successfully opened the file
737 (when (y-or-n-p (format "Save file %s? (%d modifications)"
738 file modified))
739 (write-region (point-min) (point-max) file nil 'silent)
740 (auth-source-do-debug
741 "auth-source-netrc-parse: modified %d lines in %s"
742 modified file)))
743
744 (nreverse result))))))
745
746(defun auth-source-netrc-normalize (alist)
747 (mapcar (lambda (entry)
748 (let (ret item)
749 (while (setq item (pop entry))
750 (let ((k (car item))
751 (v (cdr item)))
752
753 ;; apply key aliases
754 (setq k (cond ((member k '("machine")) "host")
755 ((member k '("login" "account")) "user")
756 ((member k '("protocol")) "port")
757 ((member k '("password")) "secret")
758 (t k)))
759
760 ;; send back the secret in a function (lexical binding)
761 (when (equal k "secret")
762 (setq v (lexical-let ((v v))
763 (lambda () v))))
764
765 (setq ret (plist-put ret
766 (intern (concat ":" k))
767 v))
768 ))
769 ret))
770 alist))
771
772;;; (setq secret (plist-get (nth 0 (auth-source-search :host t :type 'netrc :K 1 :max 1)) :secret))
773;;; (funcall secret)
774
775(defun* auth-source-netrc-search (&rest
776 spec
777 &key backend create delete
778 type max host user protocol
779 &allow-other-keys)
780"Given a property list SPEC, return search matches from the :backend.
781See `auth-source-search' for details on SPEC."
782 ;; just in case, check that the type is correct (null or same as the backend)
783 (assert (or (null type) (eq type (oref backend type)))
784 t "Invalid netrc search: %s %s")
785
786 (let ((results (auth-source-netrc-normalize
787 (auth-source-netrc-parse
788 :max max
789 :delete delete
790 :file (oref backend source)
791 :host (or host t)
792 :user (or user t)
793 :protocol (or protocol t)))))
794
795 ;; if we need to create an entry AND none were found to match
796 (when (and create
797 (= 0 (length results)))
798
799 ;; create based on the spec
800 (apply (slot-value backend 'create-function) spec)
801 ;; turn off the :create key
802 (setq spec (plist-put spec :create nil))
803 ;; run the search again to get the updated data
804 ;; the result will be returned, even if the search fails
805 (setq results (apply 'auth-source-netrc-search spec)))
806
807 results))
808
809;;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t)
810;;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t :create-extra-keys '((A "default A") (B)))
811
812(defun* auth-source-netrc-create (&rest spec
813 &key backend
814 secret host user protocol create
815 &allow-other-keys)
816 (let* ((base-required '(host user protocol secret))
817 ;; we know (because of an assertion in auth-source-search) that the
818 ;; :create parameter is either t or a list (which includes nil)
819 (create-extra (if (eq t create) nil create))
820 (required (append base-required create-extra))
821 (file (oref backend source))
822 (add "")
823 ;; `valist' is an alist
824 valist)
825
826 ;; only for base required elements (defined as function parameters):
827 ;; fill in the valist with whatever data we may have from the search
828 ;; we take the first value if it's a list, the whole value otherwise
829 (dolist (br base-required)
830 (when (symbol-value br)
831 (aput 'valist br (if (listp (symbol-value br))
832 (nth 0 (symbol-value br))
833 (symbol-value br)))))
834
835 ;; for extra required elements, see if the spec includes a value for them
836 (dolist (er create-extra)
837 (let ((name (concat ":" (symbol-name er)))
838 (keys (loop for i below (length spec) by 2
839 collect (nth i spec))))
840 (dolist (k keys)
841 (when (equal (symbol-name k) name)
842 (aput 'valist er (plist-get spec k))))))
843
844 ;; for each required element
845 (dolist (r required)
846 (let* ((data (aget valist r))
847 (given-default (aget auth-source-creation-defaults r))
848 ;; the defaults are simple
849 (default (cond
850 ((and (not given-default) (eq r 'user))
851 (user-login-name))
852 ;; note we need this empty string
853 ((and (not given-default) (eq r 'protocol))
854 "")
855 (t given-default)))
856 ;; the prompt's default string depends on the data so far
857 (default-string (if (and default (< 0 (length default)))
858 (format " (default %s)" default)
859 " (no default)"))
860 ;; the prompt should also show what's entered so far
861 (user-value (aget valist 'user))
862 (host-value (aget valist 'host))
863 (protocol-value (aget valist 'protocol))
864 (info-so-far (concat (if user-value
865 (format "%s@" user-value)
866 "[USER?]")
867 (if host-value
868 (format "%s" host-value)
869 "[HOST?]")
870 (if protocol-value
871 ;; this distinguishes protocol between
872 (if (zerop (length protocol-value))
873 "" ; 'entered as "no default"' vs.
874 (format ":%s" protocol-value)) ; given
875 ;; and this is when the protocol is unknown
876 "[PROTOCOL?]"))))
425 877
426;; (progn 878 ;; now prompt if the search SPEC did not include a required key;
427;; (auth-source-forget-all-cached) 879 ;; take the result and put it in `data' AND store it in `valist'
428;; (list 880 (aput 'valist r
429;; (auth-source-user-or-password '("login" "password") "imap.myhost.com" "other") 881 (setq data
430;; (auth-source-user-or-password '("login" "password") "imap.myhost.com" "other" "tzz") 882 (cond
431;; (auth-source-user-or-password '("login" "password") "imap.myhost.com" "other" "joe"))) 883 ((and (null data) (eq r 'secret))
884 ;; special case prompt for passwords
885 (read-passwd (format "Password for %s: " info-so-far)))
886 ((null data)
887 (read-string
888 (format "Enter %s for %s%s: "
889 r info-so-far default-string)
890 nil nil default))
891 (t data))))
892
893 ;; when r is not an empty string...
894 (when (and (stringp data)
895 (< 0 (length data)))
896 ;; append the key (the symbol name of r) and the value in r
897 (setq add (concat add
898 (format "%s%s %S"
899 ;; prepend a space
900 (if (zerop (length add)) "" " ")
901 ;; remap auth-source tokens to netrc
902 (case r
903 ('user "login")
904 ('host "machine")
905 ('secret "password")
906 ('protocol "port")
907 (t (symbol-name r)))
908 ;; the value will be printed in %S format
909 data))))))
910
911 (with-temp-buffer
912 (when (file-exists-p file)
913 (insert-file-contents file))
914 (when auth-source-gpg-encrypt-to
915 ;; (see bug#7487) making `epa-file-encrypt-to' local to
916 ;; this buffer lets epa-file skip the key selection query
917 ;; (see the `local-variable-p' check in
918 ;; `epa-file-write-region').
919 (unless (local-variable-p 'epa-file-encrypt-to (current-buffer))
920 (make-local-variable 'epa-file-encrypt-to))
921 (if (listp auth-source-gpg-encrypt-to)
922 (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
923 (goto-char (point-max))
924
925 ;; ask AFTER we've successfully opened the file
926 (when (y-or-n-p (format "Add to file %s: line [%s]" file add))
927 (unless (bolp)
928 (insert "\n"))
929 (insert add "\n")
930 (write-region (point-min) (point-max) file nil 'silent)
931 (auth-source-do-debug
932 "auth-source-netrc-create: wrote 1 new line to %s"
933 file)))))
934
935;;; Backend specific parsing: Secrets API backend
936
937;;; (let ((auth-sources '(default))) (auth-source-search :max 1 :create t))
938;;; (let ((auth-sources '(default))) (auth-source-search :max 1 :delete t))
939;;; (let ((auth-sources '(default))) (auth-source-search :max 1))
940;;; (let ((auth-sources '(default))) (auth-source-search))
941;;; (let ((auth-sources '("secrets:login"))) (auth-source-search :max 1))
942;;; (let ((auth-sources '("secrets:login"))) (auth-source-search :max 1 :signon_realm "https://git.gnus.org/Git"))
943
944(defun* auth-source-secrets-search (&rest
945 spec
946 &key backend create delete label
947 type max host user protocol
948 &allow-other-keys)
949 "Search the Secrets API; spec is like `auth-source'.
950
951The :label key specifies the item's label. It is the only key
952that can specify a substring. Any :label value besides a string
953will allow any label.
954
955All other search keys must match exactly. If you need substring
956matching, do a wider search and narrow it down yourself.
957
958You'll get back all the properties of the token as a plist.
959
960Here's an example that looks for the first item in the 'login'
961Secrets collection:
962
963 \(let ((auth-sources '(\"secrets:login\")))
964 (auth-source-search :max 1)
965
966Here's another that looks for the first item in the 'login'
967Secrets collection whose label contains 'gnus':
968
969 \(let ((auth-sources '(\"secrets:login\")))
970 (auth-source-search :max 1 :label \"gnus\")
971
972And this one looks for the first item in the 'login' Secrets
973collection that's a Google Chrome entry for the git.gnus.org site
974login:
975
976 \(let ((auth-sources '(\"secrets:login\")))
977 (auth-source-search :max 1 :signon_realm \"https://git.gnus.org/Git\"))
978"
979
980 ;; TODO
981 (assert (not create) nil
982 "The Secrets API auth-source backend doesn't support creation yet")
983 ;; TODO
984 ;; (secrets-delete-item coll elt)
985 (assert (not delete) nil
986 "The Secrets API auth-source backend doesn't support deletion yet")
987
988 (let* ((coll (oref backend source))
989 (max (or max 5000)) ; sanity check: default to stop at 5K
990 (ignored-keys '(:create :delete :max :backend :label))
991 (search-keys (loop for i below (length spec) by 2
992 unless (memq (nth i spec) ignored-keys)
993 collect (nth i spec)))
994 ;; build a search spec without the ignored keys
995 ;; if a search key is nil or t (match anything), we skip it
996 (search-spec (mapcan (lambda (k) (if (or (null (plist-get spec k))
997 (eq t (plist-get spec k)))
998 nil
999 (list k (plist-get spec k))))
1000 search-keys))
1001 ;; needed keys (always including host, login, protocol, and secret)
1002 (returned-keys (remove-duplicates (append
1003 '(:host :login :protocol :secret)
1004 search-keys)))
1005 (items (loop for item in (apply 'secrets-search-items coll search-spec)
1006 unless (and (stringp label)
1007 (not (string-match label item)))
1008 collect item))
1009 ;; TODO: respect max in `secrets-search-items', not after the fact
1010 (items (subseq items 0 (min (length items) max)))
1011 ;; convert the item name to a full plist
1012 (items (mapcar (lambda (item)
1013 (append
1014 ;; make an entry for the secret (password) element
1015 (list
1016 :secret
1017 (lexical-let ((v (secrets-get-secret coll item)))
1018 (lambda () v)))
1019 ;; rewrite the entry from ((k1 v1) (k2 v2)) to plist
1020 (mapcan (lambda (entry)
1021 (list (car entry) (cdr entry)))
1022 (secrets-get-attributes coll item))))
1023 items))
1024 ;; ensure each item has each key in `returned-keys'
1025 (items (mapcar (lambda (plist)
1026 (append
1027 (mapcan (lambda (req)
1028 (if (plist-get plist req)
1029 nil
1030 (list req nil)))
1031 returned-keys)
1032 plist))
1033 items)))
1034 items))
1035
1036(defun* auth-source-secrets-create (&rest
1037 spec
1038 &key backend type max host user protocol
1039 &allow-other-keys)
1040 ;; TODO
1041 ;; (apply 'secrets-create-item (auth-get-source entry) name passwd spec)
1042 (debug spec))
1043
1044;;; older API
1045
1046;;; (auth-source-user-or-password '("login" "password") "imap.myhost.com" t "tzz")
1047
1048;; deprecate the old interface
1049(make-obsolete 'auth-source-user-or-password
1050 'auth-source-search "Emacs 24.1")
1051(make-obsolete 'auth-source-forget-user-or-password
1052 'auth-source-forget "Emacs 24.1")
432 1053
433(defun auth-source-user-or-password 1054(defun auth-source-user-or-password
434 (mode host protocol &optional username create-missing delete-existing) 1055 (mode host protocol &optional username create-missing delete-existing)
435 "Find MODE (string or list of strings) matching HOST and PROTOCOL. 1056 "Find MODE (string or list of strings) matching HOST and PROTOCOL.
436 1057
1058DEPRECATED in favor of `auth-source-search'!
1059
437USERNAME is optional and will be used as \"login\" in a search 1060USERNAME is optional and will be used as \"login\" in a search
438across the Secret Service API (see secrets.el) if the resulting 1061across the Secret Service API (see secrets.el) if the resulting
439items don't have a username. This means that if you search for 1062items don't have a username. This means that if you search for
@@ -452,8 +1075,9 @@ stored in the password database which matches best (see
452 1075
453MODE can be \"login\" or \"password\"." 1076MODE can be \"login\" or \"password\"."
454 (auth-source-do-debug 1077 (auth-source-do-debug
455 "auth-source-user-or-password: get %s for %s (%s) + user=%s" 1078 "auth-source-user-or-password: DEPRECATED get %s for %s (%s) + user=%s"
456 mode host protocol username) 1079 mode host protocol username)
1080
457 (let* ((listy (listp mode)) 1081 (let* ((listy (listp mode))
458 (mode (if listy mode (list mode))) 1082 (mode (if listy mode (list mode)))
459 (cname (if username 1083 (cname (if username
@@ -461,70 +1085,44 @@ MODE can be \"login\" or \"password\"."
461 (format "%s %s:%s" mode host protocol))) 1085 (format "%s %s:%s" mode host protocol)))
462 (search (list :host host :protocol protocol)) 1086 (search (list :host host :protocol protocol))
463 (search (if username (append search (list :user username)) search)) 1087 (search (if username (append search (list :user username)) search))
464 (found (if (not delete-existing) 1088 (search (if create-missing
465 (gethash cname auth-source-cache) 1089 (append search (list :create t))
466 (remhash cname auth-source-cache) 1090 search))
467 nil))) 1091 (search (if delete-existing
1092 (append search (list :delete t))
1093 search))
1094 ;; (found (if (not delete-existing)
1095 ;; (gethash cname auth-source-cache)
1096 ;; (remhash cname auth-source-cache)
1097 ;; nil)))
1098 (found nil))
468 (if found 1099 (if found
469 (progn 1100 (progn
470 (auth-source-do-debug 1101 (auth-source-do-debug
471 "auth-source-user-or-password: cached %s=%s for %s (%s) + %s" 1102 "auth-source-user-or-password: DEPRECATED cached %s=%s for %s (%s) + %s"
472 mode 1103 mode
473 ;; don't show the password 1104 ;; don't show the password
474 (if (and (member "password" mode) auth-source-hide-passwords) 1105 (if (and (member "password" mode) t)
475 "SECRET" 1106 "SECRET"
476 found) 1107 found)
477 host protocol username) 1108 host protocol username)
478 found) ; return the found data 1109 found) ; return the found data
479 ;; else, if not found 1110 ;; else, if not found, search with a max of 1
480 (let ((choices (apply 'auth-source-pick search))) 1111 (let ((choice (nth 0 (apply 'auth-source-search
481 (dolist (choice choices) 1112 (append '(:max 1) search)))))
482 (if delete-existing 1113 (when choice
483 (apply 'auth-source-delete choice search) 1114 (dolist (m mode)
484 (setq found (apply 'auth-source-retrieve mode choice search))) 1115 (cond
485 (and found (return found))) 1116 ((equal "password" m)
486 1117 (push (if (plist-get choice :secret)
487 ;; We haven't found something, so we will create it interactively. 1118 (funcall (plist-get choice :secret))
488 (when (and (not found) create-missing) 1119 nil) found))
489 (setq found (apply 'auth-source-create 1120 ((equal "login" m)
490 mode (if choices 1121 (push (plist-get choice :user) found)))))
491 (car choices) 1122 (setq found (nreverse found))
492 (car auth-sources)) 1123 (setq found (if listy found (car-safe found)))))
493 search)))
494
495 ;; Cache the result.
496 (when found
497 (auth-source-do-debug
498 "auth-source-user-or-password: found %s=%s for %s (%s) + %s"
499 mode
500 ;; don't show the password
501 (if (and (member "password" mode) auth-source-hide-passwords)
502 "SECRET" found)
503 host protocol username)
504 (setq found (if listy found (car-safe found)))
505 (when auth-source-do-cache
506 (puthash cname found auth-source-cache)))
507
508 found))))
509
510(defun auth-source-protocol-defaults (protocol)
511 "Return a list of default ports and names for PROTOCOL."
512 (cdr-safe (assoc protocol auth-source-protocols)))
513
514(defun auth-source-user-or-password-imap (mode host)
515 (auth-source-user-or-password mode host 'imap))
516
517(defun auth-source-user-or-password-pop3 (mode host)
518 (auth-source-user-or-password mode host 'pop3))
519
520(defun auth-source-user-or-password-ssh (mode host)
521 (auth-source-user-or-password mode host 'ssh))
522
523(defun auth-source-user-or-password-sftp (mode host)
524 (auth-source-user-or-password mode host 'sftp))
525 1124
526(defun auth-source-user-or-password-smtp (mode host) 1125 found))
527 (auth-source-user-or-password mode host 'smtp))
528 1126
529(provide 'auth-source) 1127(provide 'auth-source)
530 1128
diff --git a/lisp/gnus/gnus-art.el b/lisp/gnus/gnus-art.el
index 54797b2a518..3e1630804f7 100644
--- a/lisp/gnus/gnus-art.el
+++ b/lisp/gnus/gnus-art.el
@@ -683,7 +683,7 @@ beginning of a line."
683 :type 'regexp 683 :type 'regexp
684 :group 'gnus-article-various) 684 :group 'gnus-article-various)
685 685
686(defcustom gnus-article-mode-line-format "Gnus: %g [%w] %S%m" 686(defcustom gnus-article-mode-line-format "Gnus: %g %S%m"
687 "*The format specification for the article mode line. 687 "*The format specification for the article mode line.
688See `gnus-summary-mode-line-format' for a closer description. 688See `gnus-summary-mode-line-format' for a closer description.
689 689
@@ -691,6 +691,7 @@ The following additional specs are available:
691 691
692%w The article washing status. 692%w The article washing status.
693%m The number of MIME parts in the article." 693%m The number of MIME parts in the article."
694 :version "24.1"
694 :type 'string 695 :type 'string
695 :group 'gnus-article-various) 696 :group 'gnus-article-various)
696 697
@@ -3403,6 +3404,7 @@ possible values."
3403 (inhibit-read-only t) 3404 (inhibit-read-only t)
3404 (inhibit-point-motion-hooks t) 3405 (inhibit-point-motion-hooks t)
3405 (first t) 3406 (first t)
3407 (visible-date (mail-fetch-field "Date"))
3406 pos date bface eface) 3408 pos date bface eface)
3407 (save-excursion 3409 (save-excursion
3408 (save-restriction 3410 (save-restriction
@@ -3426,6 +3428,9 @@ possible values."
3426 (delete-region (point-at-bol) (progn 3428 (delete-region (point-at-bol) (progn
3427 (gnus-article-forward-header) 3429 (gnus-article-forward-header)
3428 (point)))) 3430 (point))))
3431 (when (and (not date)
3432 visible-date)
3433 (setq date visible-date))
3429 (when date 3434 (when date
3430 (article-transform-date date type bface eface))))))) 3435 (article-transform-date date type bface eface)))))))
3431 3436
diff --git a/lisp/gnus/gnus-delay.el b/lisp/gnus/gnus-delay.el
index a06a510ecdd..bfd17055ea5 100644
--- a/lisp/gnus/gnus-delay.el
+++ b/lisp/gnus/gnus-delay.el
@@ -78,7 +78,7 @@ DELAY is a string, giving the length of the time. Possible values are:
78 time, then the deadline is tomorrow, else today." 78 time, then the deadline is tomorrow, else today."
79 (interactive 79 (interactive
80 (list (read-string 80 (list (read-string
81 "Target date (YYYY-MM-DD) or length of delay (units in [mhdwMY]): " 81 "Target date (YYYY-MM-DD), time (hh:mm), or length of delay (units in [mhdwMY]): "
82 gnus-delay-default-delay))) 82 gnus-delay-default-delay)))
83 (let (num unit days year month day hour minute deadline) 83 (let (num unit days year month day hour minute deadline)
84 (cond ((string-match 84 (cond ((string-match
@@ -105,7 +105,7 @@ DELAY is a string, giving the length of the time. Possible values are:
105 (append deadline nil)))) 105 (append deadline nil))))
106 ;; If this time has passed already, add a day. 106 ;; If this time has passed already, add a day.
107 (when (< deadline (gnus-float-time)) 107 (when (< deadline (gnus-float-time))
108 (setq deadline (+ 3600 deadline))) ;3600 secs/day 108 (setq deadline (+ 86400 deadline))) ; 86400 secs/day
109 ;; Convert seconds to date header. 109 ;; Convert seconds to date header.
110 (setq deadline (message-make-date 110 (setq deadline (message-make-date
111 (seconds-to-time deadline)))) 111 (seconds-to-time deadline))))
diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el
index f98c195eada..6e6ef76c0c1 100644
--- a/lisp/gnus/mail-source.el
+++ b/lisp/gnus/mail-source.el
@@ -32,7 +32,7 @@
32(eval-when-compile 32(eval-when-compile
33 (require 'cl) 33 (require 'cl)
34 (require 'imap)) 34 (require 'imap))
35(autoload 'auth-source-user-or-password "auth-source") 35(autoload 'auth-source-search "auth-source")
36(autoload 'pop3-movemail "pop3") 36(autoload 'pop3-movemail "pop3")
37(autoload 'pop3-get-message-count "pop3") 37(autoload 'pop3-get-message-count "pop3")
38(autoload 'nnheader-cancel-timer "nnheader") 38(autoload 'nnheader-cancel-timer "nnheader")
@@ -332,6 +332,7 @@ Common keywords should be listed here.")
332 (:prescript) 332 (:prescript)
333 (:prescript-delay) 333 (:prescript-delay)
334 (:postscript) 334 (:postscript)
335 ;; note server and port need to come before user and password
335 (:server (getenv "MAILHOST")) 336 (:server (getenv "MAILHOST"))
336 (:port 110) 337 (:port 110)
337 (:user (or (user-login-name) (getenv "LOGNAME") (getenv "USER"))) 338 (:user (or (user-login-name) (getenv "LOGNAME") (getenv "USER")))
@@ -345,6 +346,7 @@ Common keywords should be listed here.")
345 (:subdirs ("cur" "new")) 346 (:subdirs ("cur" "new"))
346 (:function)) 347 (:function))
347 (imap 348 (imap
349 ;; note server and port need to come before user and password
348 (:server (getenv "MAILHOST")) 350 (:server (getenv "MAILHOST"))
349 (:port) 351 (:port)
350 (:stream) 352 (:stream)
@@ -417,42 +419,66 @@ the `mail-source-keyword-map' variable."
417(put 'mail-source-bind 'lisp-indent-function 1) 419(put 'mail-source-bind 'lisp-indent-function 1)
418(put 'mail-source-bind 'edebug-form-spec '(sexp body)) 420(put 'mail-source-bind 'edebug-form-spec '(sexp body))
419 421
420;; TODO: use the list format for auth-source-user-or-password modes
421(defun mail-source-set-1 (source) 422(defun mail-source-set-1 (source)
422 (let* ((type (pop source)) 423 (let* ((type (pop source))
423 (defaults (cdr (assq type mail-source-keyword-map))) 424 (defaults (cdr (assq type mail-source-keyword-map)))
424 default value keyword auth-info user-auth pass-auth) 425 (search '(:max 1))
426 found default value keyword auth-info user-auth pass-auth)
427
428 ;; append to the search the useful info from the source and the defaults:
429 ;; user, host, and port
430
431 ;; the msname is the mail-source parameter
432 (dolist (msname '(:server :user :port))
433 ;; the asname is the auth-source parameter
434 (let* ((asname (case msname
435 (:server :host) ; auth-source uses :host
436 (t msname)))
437 ;; this is the mail-source default
438 (msdef1 (or (plist-get source msname)
439 (nth 1 (assoc msname defaults))))
440 ;; ...evaluated
441 (msdef (mail-source-value msdef1)))
442 (setq search (append (list asname
443 (if msdef msdef t))
444 search))))
445 ;; if the port is unknown yet, get it from the mail-source type
446 (unless (plist-get search :port)
447 (setq search (append (list :port (symbol-name type)))))
448
425 (while (setq default (pop defaults)) 449 (while (setq default (pop defaults))
426 ;; for each default :SYMBOL, set SYMBOL to the plist value for :SYMBOL 450 ;; for each default :SYMBOL, set SYMBOL to the plist value for :SYMBOL
427 ;; using `mail-source-value' to evaluate the plist value 451 ;; using `mail-source-value' to evaluate the plist value
428 (set (mail-source-strip-keyword (setq keyword (car default))) 452 (set (mail-source-strip-keyword (setq keyword (car default)))
429 ;; note the following reasons for this structure: 453 ;; note the following reasons for this structure:
430 ;; 1) the auth-sources user and password override everything 454 ;; 1) the auth-sources user and password override everything
431 ;; 2) it avoids macros, so it's cleaner 455 ;; 2) it avoids macros, so it's cleaner
432 ;; 3) it falls through to the mail-sources and then default values 456 ;; 3) it falls through to the mail-sources and then default values
433 (cond 457 (cond
434 ((and 458 ((and
435 (eq keyword :user) 459 (eq keyword :user)
436 (setq user-auth 460 (setq user-auth (plist-get
437 (nth 0 (auth-source-user-or-password 461 ;; cache the search result in `found'
438 '("login" "password") 462 (or found
439 ;; this is "host" in auth-sources 463 (setq found (nth 0 (apply 'auth-source-search
440 (if (boundp 'server) (symbol-value 'server) "") 464 search))))
441 type)))) 465 :user)))
442 user-auth) 466 user-auth)
443 ((and 467 ((and
444 (eq keyword :password) 468 (eq keyword :password)
445 (setq pass-auth 469 (setq pass-auth (plist-get
446 (nth 1 470 ;; cache the search result in `found'
447 (auth-source-user-or-password 471 (or found
448 '("login" "password") 472 (setq found (nth 0 (apply 'auth-source-search
449 ;; this is "host" in auth-sources 473 search))))
450 (if (boundp 'server) (symbol-value 'server) "") 474 :secret)))
451 type)))) 475 ;; maybe set the password to the return of the :secret function
452 pass-auth) 476 (if (functionp pass-auth)
453 (t (if (setq value (plist-get source keyword)) 477 (setq pass-auth (funcall pass-auth))
454 (mail-source-value value) 478 pass-auth))
455 (mail-source-value (cadr default))))))))) 479 (t (if (setq value (plist-get source keyword))
480 (mail-source-value value)
481 (mail-source-value (cadr default)))))))))
456 482
457(eval-and-compile 483(eval-and-compile
458 (defun mail-source-bind-common-1 () 484 (defun mail-source-bind-common-1 ()
diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el
index a6fe6b1489b..a5a001f7e11 100644
--- a/lisp/gnus/nnimap.el
+++ b/lisp/gnus/nnimap.el
@@ -47,8 +47,8 @@
47(require 'nnmail) 47(require 'nnmail)
48(require 'proto-stream) 48(require 'proto-stream)
49 49
50(autoload 'auth-source-forget-user-or-password "auth-source") 50(autoload 'auth-source-forget+ "auth-source")
51(autoload 'auth-source-user-or-password "auth-source") 51(autoload 'auth-source-search "auth-source")
52 52
53(nnoo-declare nnimap) 53(nnoo-declare nnimap)
54 54
@@ -142,6 +142,8 @@ textual parts.")
142(defvar nnimap-quirks 142(defvar nnimap-quirks
143 '(("QRESYNC" "Zimbra" "QRESYNC "))) 143 '(("QRESYNC" "Zimbra" "QRESYNC ")))
144 144
145(defvar nnimap-inhibit-logging nil)
146
145(defun nnimap-buffer () 147(defun nnimap-buffer ()
146 (nnimap-find-process-buffer nntp-server-buffer)) 148 (nnimap-find-process-buffer nntp-server-buffer))
147 149
@@ -275,18 +277,18 @@ textual parts.")
275 (current-buffer))) 277 (current-buffer)))
276 278
277(defun nnimap-credentials (address ports &optional inhibit-create) 279(defun nnimap-credentials (address ports &optional inhibit-create)
278 (let (port credentials) 280 (let* ((found (nth 0 (auth-source-search :max 1
279 ;; Request the credentials from all ports, but only query on the 281 :host address
280 ;; last port if all the previous ones have failed. 282 :port ports
281 (while (and (null credentials) 283 :create (if inhibit-create
282 (setq port (pop ports))) 284 nil
283 (setq credentials 285 (null ports)))))
284 (auth-source-user-or-password 286 (user (plist-get found :user))
285 '("login" "password") address port nil 287 (secret (plist-get found :secret))
286 (if inhibit-create 288 (secret (if (functionp secret) (funcall secret) secret)))
287 nil 289 (if found
288 (null ports))))) 290 (list user secret)
289 credentials)) 291 nil)))
290 292
291(defun nnimap-keepalive () 293(defun nnimap-keepalive ()
292 (let ((now (current-time))) 294 (let ((now (current-time)))
@@ -381,26 +383,24 @@ textual parts.")
381 (if (eq nnimap-authenticator 'anonymous) 383 (if (eq nnimap-authenticator 'anonymous)
382 (list "anonymous" 384 (list "anonymous"
383 (message-make-address)) 385 (message-make-address))
384 (or 386 ;; Look for the credentials based on
385 ;; First look for the credentials based 387 ;; the virtual server name and the address
386 ;; on the virtual server name. 388 (nnimap-credentials
387 (nnimap-credentials 389 (list
388 (nnoo-current-server 'nnimap) ports t) 390 (nnoo-current-server 'nnimap)
389 ;; Then look them up based on the 391 nnimap-address)
390 ;; physical address. 392 ports t))))
391 (nnimap-credentials nnimap-address ports)))))
392 (setq nnimap-object nil) 393 (setq nnimap-object nil)
393 (setq login-result 394 (let ((nnimap-inhibit-logging t))
394 (nnimap-login (car credentials) (cadr credentials))) 395 (setq login-result
396 (nnimap-login (car credentials) (cadr credentials))))
395 (unless (car login-result) 397 (unless (car login-result)
396 ;; If the login failed, then forget the credentials 398 ;; If the login failed, then forget the credentials
397 ;; that are now possibly cached. 399 ;; that are now possibly cached.
398 (dolist (host (list (nnoo-current-server 'nnimap) 400 (dolist (host (list (nnoo-current-server 'nnimap)
399 nnimap-address)) 401 nnimap-address))
400 (dolist (port ports) 402 (dolist (port ports)
401 (dolist (element '("login" "password")) 403 (auth-source-forget+ :host host :protocol port)))
402 (auth-source-forget-user-or-password
403 element host port))))
404 (delete-process (nnimap-process nnimap-object)) 404 (delete-process (nnimap-process nnimap-object))
405 (setq nnimap-object nil)))) 405 (setq nnimap-object nil))))
406 (when nnimap-object 406 (when nnimap-object
@@ -969,7 +969,8 @@ textual parts.")
969 (nnimap-add-cr) 969 (nnimap-add-cr)
970 (setq message (buffer-substring-no-properties (point-min) (point-max))) 970 (setq message (buffer-substring-no-properties (point-min) (point-max)))
971 (with-current-buffer (nnimap-buffer) 971 (with-current-buffer (nnimap-buffer)
972 (when (setq message (nnimap-process-quirk "OK Gimap " 'append message)) 972 (when (setq message (or (nnimap-process-quirk "OK Gimap " 'append message)
973 message))
973 ;; If we have this group open read-only, then unselect it 974 ;; If we have this group open read-only, then unselect it
974 ;; before appending to it. 975 ;; before appending to it.
975 (when (equal (nnimap-examined nnimap-object) group) 976 (when (equal (nnimap-examined nnimap-object) group)
@@ -997,7 +998,7 @@ textual parts.")
997 998
998(defun nnimap-process-quirk (greeting-match type data) 999(defun nnimap-process-quirk (greeting-match type data)
999 (when (and (nnimap-greeting nnimap-object) 1000 (when (and (nnimap-greeting nnimap-object)
1000 (string-match "OK Gimap " (nnimap-greeting nnimap-object)) 1001 (string-match greeting-match (nnimap-greeting nnimap-object))
1001 (eq type 'append) 1002 (eq type 'append)
1002 (string-match "\000" data)) 1003 (string-match "\000" data))
1003 (let ((choice (gnus-multiple-choice 1004 (let ((choice (gnus-multiple-choice
@@ -1567,6 +1568,7 @@ textual parts.")
1567(defvar nnimap-sequence 0) 1568(defvar nnimap-sequence 0)
1568 1569
1569(defun nnimap-send-command (&rest args) 1570(defun nnimap-send-command (&rest args)
1571 (setf (nnimap-last-command-time nnimap-object) (current-time))
1570 (process-send-string 1572 (process-send-string
1571 (get-buffer-process (current-buffer)) 1573 (get-buffer-process (current-buffer))
1572 (nnimap-log-command 1574 (nnimap-log-command
@@ -1585,12 +1587,14 @@ textual parts.")
1585(defun nnimap-log-command (command) 1587(defun nnimap-log-command (command)
1586 (with-current-buffer (get-buffer-create "*imap log*") 1588 (with-current-buffer (get-buffer-create "*imap log*")
1587 (goto-char (point-max)) 1589 (goto-char (point-max))
1588 (insert (format-time-string "%H:%M:%S") " " command)) 1590 (insert (format-time-string "%H:%M:%S") " "
1591 (if nnimap-inhibit-logging
1592 "(inhibited)"
1593 command)))
1589 command) 1594 command)
1590 1595
1591(defun nnimap-command (&rest args) 1596(defun nnimap-command (&rest args)
1592 (erase-buffer) 1597 (erase-buffer)
1593 (setf (nnimap-last-command-time nnimap-object) (current-time))
1594 (let* ((sequence (apply #'nnimap-send-command args)) 1598 (let* ((sequence (apply #'nnimap-send-command args))
1595 (response (nnimap-get-response sequence))) 1599 (response (nnimap-get-response sequence)))
1596 (if (equal (caar response) "OK") 1600 (if (equal (caar response) "OK")
diff --git a/lisp/gnus/nntp.el b/lisp/gnus/nntp.el
index eb2dd004638..4b42637978e 100644
--- a/lisp/gnus/nntp.el
+++ b/lisp/gnus/nntp.el
@@ -40,7 +40,7 @@
40 40
41(eval-when-compile (require 'cl)) 41(eval-when-compile (require 'cl))
42 42
43(autoload 'auth-source-user-or-password "auth-source") 43(autoload 'auth-source-search "auth-source")
44 44
45(defgroup nntp nil 45(defgroup nntp nil
46 "NNTP access for Gnus." 46 "NNTP access for Gnus."
@@ -1231,10 +1231,16 @@ If SEND-IF-FORCE, only send authinfo to the server if the
1231 (let* ((list (netrc-parse nntp-authinfo-file)) 1231 (let* ((list (netrc-parse nntp-authinfo-file))
1232 (alist (netrc-machine list nntp-address "nntp")) 1232 (alist (netrc-machine list nntp-address "nntp"))
1233 (force (or (netrc-get alist "force") nntp-authinfo-force)) 1233 (force (or (netrc-get alist "force") nntp-authinfo-force))
1234 (auth-info 1234 (auth-info
1235 (auth-source-user-or-password '("login" "password") nntp-address "nntp")) 1235 (nth 0 (auth-source-search :max 1
1236 (auth-user (nth 0 auth-info)) 1236 ;; TODO: allow the virtual server name too
1237 (auth-passwd (nth 1 auth-info)) 1237 :host nntp-address
1238 :port '("119" "nntp"))))
1239 (auth-user (plist-get auth-info :user))
1240 (auth-passwd (plist-get auth-info :secret))
1241 (auth-passwd (if (functionp auth-passwd)
1242 (funcall auth-passwd)
1243 auth-passwd))
1238 (user (or 1244 (user (or
1239 ;; this is preferred to netrc-* 1245 ;; this is preferred to netrc-*
1240 auth-user 1246 auth-user
diff --git a/lisp/gnus/sieve-manage.el b/lisp/gnus/sieve-manage.el
index d115f40528b..c9a0df20590 100644
--- a/lisp/gnus/sieve-manage.el
+++ b/lisp/gnus/sieve-manage.el
@@ -83,7 +83,7 @@
83 (require 'starttls)) 83 (require 'starttls))
84(autoload 'sasl-find-mechanism "sasl") 84(autoload 'sasl-find-mechanism "sasl")
85(autoload 'starttls-open-stream "starttls") 85(autoload 'starttls-open-stream "starttls")
86(autoload 'auth-source-user-or-password "auth-source") 86(autoload 'auth-source-search "auth-source")
87 87
88;; User customizable variables: 88;; User customizable variables:
89 89
@@ -273,16 +273,20 @@ Valid states are `closed', `initial', `nonauth', and `auth'.")
273 "Login to server using the SASL MECH method." 273 "Login to server using the SASL MECH method."
274 (message "sieve: Authenticating using %s..." mech) 274 (message "sieve: Authenticating using %s..." mech)
275 (with-current-buffer buffer 275 (with-current-buffer buffer
276 (let* ((user-password (auth-source-user-or-password 276 (let* ((auth-info (auth-source-search :host sieve-manage-server
277 '("login" "password") 277 :port "sieve"
278 sieve-manage-server 278 :max 1))
279 "sieve" nil t)) 279 (user-name (plist-get (nth 0 auth-info) :user))
280 (user-password (plist-get (nth 0 auth-info) :secret))
281 (user-password (if (functionp user-password)
282 (funcall user-password)
283 user-password))
280 (client (sasl-make-client (sasl-find-mechanism (list mech)) 284 (client (sasl-make-client (sasl-find-mechanism (list mech))
281 (car user-password) "sieve" sieve-manage-server)) 285 user-name "sieve" sieve-manage-server))
282 (sasl-read-passphrase 286 (sasl-read-passphrase
283 ;; We *need* to copy the password, because sasl will modify it 287 ;; We *need* to copy the password, because sasl will modify it
284 ;; somehow. 288 ;; somehow.
285 `(lambda (prompt) ,(copy-sequence (cadr user-password)))) 289 `(lambda (prompt) ,(copy-sequence user-password)))
286 (step (sasl-next-step client nil)) 290 (step (sasl-next-step client nil))
287 (tag (sieve-manage-send 291 (tag (sieve-manage-send
288 (concat 292 (concat
diff --git a/lisp/makefile.w32-in b/lisp/makefile.w32-in
index eb65bb7a60f..0e3d54408fd 100644
--- a/lisp/makefile.w32-in
+++ b/lisp/makefile.w32-in
@@ -443,9 +443,9 @@ $(lisp)/mh-e/mh-loaddefs.el: $(MH_E_SRC)
443TRAMP_SRC = $(lisp)/net/tramp.el $(lisp)/net/tramp-cache.el \ 443TRAMP_SRC = $(lisp)/net/tramp.el $(lisp)/net/tramp-cache.el \
444 $(lisp)/net/tramp-cmds.el $(lisp)/net/tramp-compat.el \ 444 $(lisp)/net/tramp-cmds.el $(lisp)/net/tramp-compat.el \
445 $(lisp)/net/tramp-ftp.el $(lisp)/net/tramp-gvfs.el \ 445 $(lisp)/net/tramp-ftp.el $(lisp)/net/tramp-gvfs.el \
446 $(lisp)/net/tramp-gw.el $(lisp)/net/tramp-imap.el \ 446 $(lisp)/net/tramp-gw.el $(lisp)/net/tramp-sh.el \
447 $(lisp)/net/tramp-sh.el $(lisp)/net/tramp-smb.el \ 447 $(lisp)/net/tramp-smb.el $(lisp)/net/tramp-uu.el \
448 $(lisp)/net/tramp-uu.el $(lisp)/net/trampver.el 448 $(lisp)/net/trampver.el
449 449
450$(lisp)/net/tramp-loaddefs.el: $(TRAMP_SRC) 450$(lisp)/net/tramp-loaddefs.el: $(TRAMP_SRC)
451 "$(EMACS)" $(EMACSOPT) \ 451 "$(EMACS)" $(EMACSOPT) \
diff --git a/lisp/mh-e/ChangeLog.1 b/lisp/mh-e/ChangeLog.1
index 2abfea9ac6b..69ca927d5e7 100644
--- a/lisp/mh-e/ChangeLog.1
+++ b/lisp/mh-e/ChangeLog.1
@@ -3499,7 +3499,7 @@
3499 3499
35002003-05-08 Satyaki Das <satyakid@stanford.edu> 35002003-05-08 Satyaki Das <satyakid@stanford.edu>
3501 3501
3502 * mh-seq.el (mh-translate-range): Take into account differnt 3502 * mh-seq.el (mh-translate-range): Take into account different
3503 semantics of split-string in Emacs and XEmacs. 3503 semantics of split-string in Emacs and XEmacs.
3504 (mh-read-pick-regexp, mh-narrow-to-from, mh-narrow-to-cc) 3504 (mh-read-pick-regexp, mh-narrow-to-from, mh-narrow-to-cc)
3505 (mh-narrow-to-to, mh-narrow-to-header-field) 3505 (mh-narrow-to-to, mh-narrow-to-header-field)
diff --git a/lisp/net/imap-hash.el b/lisp/net/imap-hash.el
deleted file mode 100644
index a07277cee68..00000000000
--- a/lisp/net/imap-hash.el
+++ /dev/null
@@ -1,374 +0,0 @@
1;;; imap-hash.el --- Hashtable-like interface to an IMAP mailbox
2
3;; Copyright (C) 2009-2011 Free Software Foundation, Inc.
4
5;; Author: Teodor Zlatanov <tzz@lifelogs.com>
6;; Keywords: mail
7
8;; This program is free software; you can redistribute it and/or modify
9;; it under the terms of the GNU General Public License as published by
10;; the Free Software Foundation, either version 3 of the License, or
11;; (at your option) any later version.
12
13;; This program is distributed in the hope that it will be useful,
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;; GNU General Public License for more details.
17
18;; You should have received a copy of the GNU General Public License
19;; along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21;;; Commentary:
22
23;; This module provides hashtable-like functions on top of imap.el
24;; functionality. All the authentication is handled by auth-source so
25;; there are no authentication options here, only the server and
26;; mailbox names are needed.
27
28;; Create a IHT (imap-hash table) object with `imap-hash-make'. Then
29;; use it with `imap-hash-map' to map a function across all the
30;; messages. Use `imap-hash-get' and `imap-hash-rem' to operate on
31;; individual messages. See the tramp-imap.el library in Tramp if you
32;; need to see practical examples.
33
34;; This only works with IMAP4r1. Sorry to everyone without it, but
35;; the compatibility code is too annoying and it's 2009.
36
37;; TODO: Use SEARCH instead of FETCH when a test is specified. List
38;; available mailboxes. Don't select an invalid mailbox.
39
40;;; Code:
41
42(require 'assoc)
43(require 'imap)
44(require 'sendmail) ; for mail-header-separator
45(require 'message)
46(autoload 'auth-source-search "auth-source")
47
48;; retrieve these headers
49(defvar imap-hash-headers
50 (append '(Subject From Date Message-Id References In-Reply-To Xref)))
51
52;; from nnheader.el
53(defsubst imap-hash-remove-cr-followed-by-lf ()
54 (goto-char (point-max))
55 (while (search-backward "\r\n" nil t)
56 (delete-char 1)))
57
58;; from nnheader.el
59(defun imap-hash-ms-strip-cr (&optional string)
60 "Strip ^M from the end of all lines in current buffer or STRING."
61 (if string
62 (with-temp-buffer
63 (insert string)
64 (imap-hash-remove-cr-followed-by-lf)
65 (buffer-string))
66 (save-excursion
67 (imap-hash-remove-cr-followed-by-lf))))
68
69(defun imap-hash-make (server port mailbox &optional user password ssl)
70 "Make a new imap-hash object using SERVER, PORT, and MAILBOX.
71USER, PASSWORD and SSL are optional.
72The test is set to t, meaning all messages are considered."
73 (when (and server port mailbox)
74 (list :server server :port port :mailbox mailbox
75 :ssl ssl :user user :password password
76 :test t)))
77
78(defun imap-hash-p (iht)
79 "Check whether IHT is a valid imap-hash."
80 (and
81 (imap-hash-server iht)
82 (imap-hash-port iht)
83 (imap-hash-mailbox iht)
84 (imap-hash-test iht)))
85
86(defmacro imap-hash-gather (uid)
87 `(imap-message-get ,uid 'BODYDETAIL))
88
89(defmacro imap-hash-data-body (details)
90 `(nth 2 (nth 1 ,details)))
91
92(defmacro imap-hash-data-headers (details)
93 `(nth 2 (nth 0 ,details)))
94
95(defun imap-hash-get (key iht &optional refetch)
96 "Get the value for KEY in the imap-hash IHT.
97Requires either `imap-hash-fetch' to be called beforehand
98\(e.g. by `imap-hash-map'), or REFETCH to be t.
99Returns a list of the headers (an alist, see `imap-hash-map') and
100the body of the message as a string.
101Also see `imap-hash-test'."
102 (with-current-buffer (imap-hash-get-buffer iht)
103 (when refetch
104 (imap-hash-fetch iht nil key))
105 (let ((details (imap-hash-gather key)))
106 (list
107 (imap-hash-get-headers
108 (imap-hash-data-headers details))
109 (imap-hash-get-body
110 (imap-hash-data-body details))))))
111
112(defun imap-hash-put (value iht &optional key)
113 "Put VALUE in the imap-hash IHT. Return the new key.
114If KEY is given, removes it.
115VALUE can be a list of the headers (an alist, see `imap-hash-map')
116and the body of the message as a string. It can also be a uid,
117in which case `imap-hash-get' will be called to get the value.
118Also see `imap-hash-test'."
119 (let ((server-buffer (imap-hash-get-buffer iht))
120 (value (if (listp value) value (imap-hash-get value iht)))
121 newuid)
122 (when value
123 (with-temp-buffer
124 (funcall 'imap-hash-make-message
125 (nth 0 value)
126 (nth 1 value)
127 nil)
128 (setq newuid (nth 1 (imap-message-append
129 (imap-hash-mailbox iht)
130 (current-buffer) nil nil server-buffer)))
131 (when key (imap-hash-rem key iht))))
132 newuid))
133
134(defun imap-hash-make-message (headers body &optional overrides)
135 "Make a message with HEADERS and BODY suitable for `imap-append',
136using `message-setup'.
137Look in the alist OVERRIDES for header overrides as per `imap-hash-headers'."
138 ;; don't insert a signature no matter what
139 (let (message-signature)
140 (message-setup
141 (append overrides headers))
142 (message-generate-headers message-required-mail-headers)
143 (message-remove-header "X-Draft-From")
144 (message-goto-body)
145 (insert (or (aget overrides 'body)
146 body
147 ""))
148 (goto-char (point-min))
149 ;; TODO: make this search better
150 (if (search-forward mail-header-separator nil t)
151 (delete-region (line-beginning-position) (line-end-position))
152 (error "Could not find the body separator in the encoded message!"))))
153
154(defun imap-hash-rem (key iht)
155 "Remove KEY in the imap-hash IHT.
156Also see `imap-hash-test'. Requires `imap-hash-fetch' to have
157been called and the imap-hash server buffer to be current,
158so it's best to use it inside `imap-hash-map'.
159The key will not be found on the next `imap-hash-map' call."
160 (with-current-buffer (imap-hash-get-buffer iht)
161 (imap-message-flags-add
162 (imap-range-to-message-set (list key))
163 "\\Deleted" 'silent)
164 (imap-mailbox-expunge t)))
165
166(defun imap-hash-clear (iht)
167 "Remove all keys in the imap-hash IHT.
168Also see `imap-hash-test'."
169 (imap-hash-map (lambda (uid b c) (imap-hash-rem uid iht)) iht))
170
171(defun imap-hash-get-headers (text-headers)
172 (with-temp-buffer
173 (insert (or text-headers ""))
174 (imap-hash-remove-cr-followed-by-lf)
175 (mapcar (lambda (header)
176 (cons header
177 (message-fetch-field (format "%s" header))))
178 imap-hash-headers)))
179
180(defun imap-hash-get-body (text)
181 (with-temp-buffer
182 (insert (or text ""))
183 (imap-hash-remove-cr-followed-by-lf)
184 (buffer-string)))
185
186(defun imap-hash-map (function iht &optional headers-only &rest messages)
187 "Call FUNCTION for all entries in IHT and pass it the message uid,
188the headers (an alist, see `imap-hash-headers'), and the body
189contents as a string. If HEADERS-ONLY is not nil, the body will be nil.
190Returns results of evaluating, as would `mapcar'.
191If MESSAGES are given, iterate only over those UIDs.
192Also see `imap-hash-test'."
193 (imap-hash-fetch iht headers-only)
194 (let ((test (imap-hash-test iht)))
195 (with-current-buffer (imap-hash-get-buffer iht)
196 (delq nil
197 (imap-message-map (lambda (message ignored-parameter)
198 (let* ((details (imap-hash-gather message))
199 (headers (imap-hash-data-headers details))
200 (hlist (imap-hash-get-headers headers))
201 (runit (cond
202 ((stringp test)
203 (string-match
204 test
205 (format "%s" (aget hlist 'Subject))))
206 ((functionp test)
207 (funcall test hlist))
208 ;; otherwise, return test itself
209 (t test))))
210 ;;(debug message headers)
211 (when runit
212 (funcall function
213 message
214 (imap-hash-get-headers
215 headers)
216 (imap-hash-get-body
217 (imap-hash-data-body details))))))
218 "UID")))))
219
220(defun imap-hash-count (iht)
221 "Count the number of messages in the imap-hash IHT.
222Also see `imap-hash-test'. It uses `imap-hash-map' so just use that
223function if you want to do more than count the elements."
224 (length (imap-hash-map (lambda (a b c)) iht t)))
225
226(defalias 'imap-hash-size 'imap-hash-count)
227
228(defun imap-hash-test (iht)
229 "Return the test used by `imap-hash-map' for IHT.
230When the test is t, any key will be a candidate.
231When the test is a string, messages will be filtered on that string as a
232regexp against the subject.
233When the test is a function, messages will be filtered with it.
234The function is passed the message headers (see `imap-hash-get-headers')."
235 (plist-get iht :test))
236
237(defun imap-hash-server (iht)
238 "Return the server used by the imap-hash IHT."
239 (plist-get iht :server))
240
241(defun imap-hash-port (iht)
242 "Return the port used by the imap-hash IHT."
243 (plist-get iht :port))
244
245(defun imap-hash-ssl (iht)
246 "Return the SSL need for the imap-hash IHT."
247 (plist-get iht :ssl))
248
249(defun imap-hash-mailbox (iht)
250 "Return the mailbox used by the imap-hash IHT."
251 (plist-get iht :mailbox))
252
253(defun imap-hash-user (iht)
254 "Return the username used by the imap-hash IHT."
255 (plist-get iht :user))
256
257(defun imap-hash-password (iht)
258 "Return the password used by the imap-hash IHT."
259 (plist-get iht :password))
260
261(defun imap-hash-open-connection (iht)
262 "Open the connection used for IMAP interactions with the imap-hash IHT."
263 (let* ((server (imap-hash-server iht))
264 (port (imap-hash-port iht))
265 (ssl-need (imap-hash-ssl iht))
266 (auth-need (not (and (imap-hash-user iht)
267 (imap-hash-password iht))))
268 ;; this will not be needed if auth-need is t
269 (auth-info (when auth-need
270 (nth 0 (auth-source-search :host server :port port))))
271 (auth-user (or (imap-hash-user iht)
272 (plist-get auth-info :user)))
273 (auth-passwd (or (imap-hash-password iht)
274 (plist-get auth-info :secret)))
275 (auth-passwd (if (functionp auth-passwd)
276 (funcall auth-passwd)
277 auth-passwd))
278 (imap-logout-timeout nil))
279
280 ;; (debug "opening server: opened+state" (imap-opened) imap-state)
281 ;; this is the only place where IMAP vs IMAPS matters
282 (if (imap-open server port (if ssl-need 'ssl nil) nil (current-buffer))
283 (progn
284 ;; (debug "after opening server: opened+state" (imap-opened (current-buffer)) imap-state)
285 ;; (debug "authenticating" auth-user auth-passwd)
286 (if (not (imap-capability 'IMAP4rev1))
287 (error "IMAP server does not support IMAP4r1, it won't work, sorry")
288 (imap-authenticate auth-user auth-passwd)
289 (imap-id)
290 ;; (debug "after authenticating: opened+state" (imap-opened (current-buffer)) imap-state)
291 (imap-opened (current-buffer))))
292 (error "Could not open the IMAP buffer"))))
293
294(defun imap-hash-get-buffer (iht)
295 "Get or create the connection buffer to be used for the imap-hash IHT."
296 (let* ((name (imap-hash-buffer-name iht))
297 (buffer (get-buffer name)))
298 (if (and buffer (imap-opened buffer))
299 buffer
300 (when buffer (kill-buffer buffer))
301 (with-current-buffer (get-buffer-create name)
302 (setq buffer-undo-list t)
303 (when (imap-hash-open-connection iht)
304 (current-buffer))))))
305
306(defun imap-hash-buffer-name (iht)
307 "Get the connection buffer to be used for the imap-hash IHT."
308 (when (imap-hash-p iht)
309 (let ((server (imap-hash-server iht))
310 (port (imap-hash-port iht))
311 (ssl-text (if (imap-hash-ssl iht) "SSL" "NoSSL")))
312 (format "*imap-hash/%s:%s:%s*" server port ssl-text))))
313
314(defun imap-hash-fetch (iht &optional headers-only &rest messages)
315 "Fetch all the messages for imap-hash IHT.
316Get only the headers if HEADERS-ONLY is not nil."
317 (with-current-buffer (imap-hash-get-buffer iht)
318 (let ((range (if messages
319 (list
320 (imap-range-to-message-set messages)
321 (imap-range-to-message-set messages))
322 '("1:*" . "1,*:*"))))
323
324 ;; (with-current-buffer "*imap-debug*"
325 ;; (erase-buffer))
326 (imap-mailbox-unselect)
327 (imap-mailbox-select (imap-hash-mailbox iht))
328 ;; (debug "after selecting mailbox: opened+state" (imap-opened) imap-state)
329 ;; (setq imap-message-data (make-vector imap-message-prime 0)
330 (imap-fetch-safe range
331 (concat (format "(UID RFC822.SIZE BODY %s "
332 (if headers-only "" "BODY.PEEK[TEXT]"))
333 (format "BODY.PEEK[HEADER.FIELDS %s])"
334 imap-hash-headers))))))
335
336(provide 'imap-hash)
337;;; imap-hash.el ends here
338
339;; ignore, for testing only
340
341;;; (setq iht (imap-hash-make "yourhosthere.com" "imap" "INBOX.test"))
342;;; (setq iht (imap-hash-make "yourhosthere.com" "imap" "test"))
343;;; (imap-hash-make "server1" "INBOX.mailbox2")
344;;; (imap-hash-p iht)
345;;; (imap-hash-get 35 iht)
346;;; (imap-hash-get 38 iht)
347;;; (imap-hash-get 37 iht t)
348;;; (mapc (lambda (buffer) (with-current-buffer buffer (erase-buffer))) '("*imap-debug*" "*imap-log*"))
349;;; (imap-hash-put (imap-hash-get 5 iht) iht)
350;;; (with-current-buffer (imap-hash-get-buffer iht) (let ((uid (imap-hash-put (imap-hash-get 5 iht) iht))) (imap-hash-put uid iht uid)))
351;;; (imap-hash-put (imap-hash-get 35 iht) iht)
352;;; (imap-hash-make-message '((Subject . "normal")) "normal body")
353;;; (imap-hash-make-message '((Subject . "old")) "old body" '((Subject . "new")))
354;;; (imap-hash-make-message '((Subject . "old")) "old body" '((body . "new body")) (lambda (subject) (concat "overwrite-" subject)))
355;;; (imap-hash-make-message '((Subject . "old")) "old body" '((Subject . "change this")) (lambda (subject) (concat "overwrite-" subject)))
356;;; (imap-hash-make-message '((Subject . "Twelcome")) "body here" nil)
357;; (with-current-buffer (imap-hash-get-buffer iht) (imap-hash-rem (imap-hash-put (imap-hash-get 5 iht) iht) iht))
358;;; (kill-buffer (imap-hash-buffer-name iht))
359;;; (imap-hash-map 'debug iht)
360;;; (imap-hash-map 'debug iht t)
361;;;(tramp-imap-handle-file-inode "/imap:yourhosthere.com:/test/welcome")
362;;;(imap-hash-count iht)
363;;; (mapc (lambda (buffer) (with-current-buffer buffer (erase-buffer))) '("*imap-debug*" "*imap-log*"))
364;;; (kill-buffer (imap-hash-buffer-name iht))
365;;; this should always return t if the server is up, automatically reopening if needed
366;;; (imap-opened (imap-hash-get-buffer iht))
367;;; (imap-hash-buffer-name iht)
368;;; (with-current-buffer (imap-hash-get-buffer iht) (debug "mailbox data, auth and state" imap-mailbox-data imap-auth imap-state))
369;;;(tramp-imap-handle-file-inode "/imap:yourhosthere.com:/test/welcome")
370;;; (imap-hash-fetch iht nil)
371;;; (imap-hash-fetch iht t)
372;;; (imap-hash-fetch iht nil 1 2 3)
373;;; (imap-hash-fetch iht t 1 2 3)
374
diff --git a/lisp/net/tramp-imap.el b/lisp/net/tramp-imap.el
deleted file mode 100644
index 4157265b0e1..00000000000
--- a/lisp/net/tramp-imap.el
+++ /dev/null
@@ -1,850 +0,0 @@
1;;; tramp-imap.el --- Tramp interface to IMAP through imap.el
2
3;; Copyright (C) 2009-2011 Free Software Foundation, Inc.
4
5;; Author: Teodor Zlatanov <tzz@lifelogs.com>
6;; Keywords: mail, comm
7;; Package: tramp
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
26;; Package to provide Tramp over IMAP
27
28;;; Setup:
29
30;; just load and open files, e.g.
31;; /imaps:user@yourhosthere.com:/INBOX.test/1
32;; or
33;; /imap:user@yourhosthere.com:/INBOX.test/1
34
35;; where `imap' goes over IMAP, while `imaps' goes over IMAP+SSL
36
37;; This module will use imap-hash.el to access the IMAP mailbox.
38
39;; This module will use auth-source.el to authenticate against the
40;; IMAP server, PLUS it will use auth-source.el to get your passphrase
41;; for the symmetrically encrypted messages. For the former, use the
42;; usual IMAP ports. For the latter, use the port "tramp-imap".
43
44;; example .authinfo / .netrc file:
45
46;; machine yourhosthere.com port tramp-imap login USER password SYMMETRIC-PASSPHRASE
47
48;; note above is the symmetric encryption passphrase for GPG
49;; below is the regular password for IMAP itself and other things on that host
50
51;; machine yourhosthere.com login USER password NORMAL-PASSWORD
52
53
54;;; Code:
55
56(require 'assoc)
57(require 'tramp)
58
59(autoload 'auth-source-search "auth-source")
60(autoload 'epg-context-operation "epg")
61(autoload 'epg-context-set-armor "epg")
62(autoload 'epg-context-set-passphrase-callback "epg")
63(autoload 'epg-context-set-progress-callback "epg")
64(autoload 'epg-decrypt-string "epg")
65(autoload 'epg-encrypt-string "epg")
66(autoload 'epg-make-context "epg")
67(autoload 'imap-hash-get "imap-hash")
68(autoload 'imap-hash-make "imap-hash")
69(autoload 'imap-hash-map "imap-hash")
70(autoload 'imap-hash-put "imap-hash")
71(autoload 'imap-hash-rem "imap-hash")
72
73;; We use the additional header "X-Size" for encoding the size of a file.
74(eval-after-load "imap-hash"
75 '(add-to-list 'imap-hash-headers 'X-Size 'append))
76
77;; Define Tramp IMAP method ...
78;;;###tramp-autoload
79(defconst tramp-imap-method "imap"
80 "*Method to connect via IMAP protocol.")
81
82;;;###tramp-autoload
83(when (and (locate-library "epa") (locate-library "imap-hash"))
84 (add-to-list 'tramp-methods
85 (list tramp-imap-method '(tramp-default-port 143))))
86
87;; Define Tramp IMAPS method ...
88;;;###tramp-autoload
89(defconst tramp-imaps-method "imaps"
90 "*Method to connect via secure IMAP protocol.")
91
92;; ... and add it to the method list.
93;;;###tramp-autoload
94(when (and (locate-library "epa") (locate-library "imap-hash"))
95 (add-to-list 'tramp-methods
96 (list tramp-imaps-method '(tramp-default-port 993))))
97
98;; Add a default for `tramp-default-user-alist'. Default is the local user.
99;;;###tramp-autoload
100(add-to-list
101 'tramp-default-user-alist
102 (list (concat "\\`"
103 (regexp-opt (list tramp-imap-method tramp-imaps-method))
104 "\\'")
105 nil (user-login-name)))
106
107;; Add completion function for IMAP method.
108;; (tramp-set-completion-function
109;; tramp-imap-method tramp-completion-function-alist-ssh) ; TODO: test this
110;; tramp-imaps-method tramp-completion-function-alist-ssh) ; TODO: test this
111
112;; New handlers should be added here.
113(defconst tramp-imap-file-name-handler-alist
114 '(
115 ;; `access-file' performed by default handler
116 (add-name-to-file . ignore)
117 ;; `byte-compiler-base-file-name' performed by default handler
118 ;; `copy-directory' performed by default handler
119 (copy-file . tramp-imap-handle-copy-file)
120 (delete-directory . ignore) ;; tramp-imap-handle-delete-directory)
121 (delete-file . tramp-imap-handle-delete-file)
122 ;; `diff-latest-backup-file' performed by default handler
123 (directory-file-name . tramp-handle-directory-file-name)
124 (directory-files . tramp-handle-directory-files)
125 (directory-files-and-attributes
126 . tramp-handle-directory-files-and-attributes)
127 (dired-call-process . ignore)
128 ;; `dired-compress-file' performed by default handler
129 ;; `dired-uncache' performed by default handler
130 (expand-file-name . tramp-imap-handle-expand-file-name)
131 ;; `file-accessible-directory-p' performed by default handler
132 (file-attributes . tramp-imap-handle-file-attributes)
133 (file-directory-p . tramp-imap-handle-file-directory-p)
134 (file-executable-p . ignore)
135 (file-exists-p . tramp-handle-file-exists-p)
136 (file-local-copy . tramp-imap-handle-file-local-copy)
137 (file-modes . tramp-handle-file-modes)
138 (file-name-all-completions . tramp-imap-handle-file-name-all-completions)
139 (file-name-as-directory . tramp-handle-file-name-as-directory)
140 (file-name-completion . tramp-handle-file-name-completion)
141 (file-name-directory . tramp-handle-file-name-directory)
142 (file-name-nondirectory . tramp-handle-file-name-nondirectory)
143 ;; `file-name-sans-versions' performed by default handler
144 (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
145 (file-ownership-preserved-p . ignore)
146 (file-readable-p . tramp-handle-file-exists-p)
147 (file-regular-p . tramp-handle-file-regular-p)
148 (file-remote-p . tramp-handle-file-remote-p)
149 ;; `file-selinux-context' performed by default handler.
150 (file-symlink-p . tramp-handle-file-symlink-p)
151 ;; `file-truename' performed by default handler
152 (file-writable-p . tramp-imap-handle-file-writable-p)
153 (find-backup-file-name . tramp-handle-find-backup-file-name)
154 ;; `find-file-noselect' performed by default handler
155 ;; `get-file-buffer' performed by default handler
156 (insert-directory . tramp-imap-handle-insert-directory)
157 (insert-file-contents . tramp-imap-handle-insert-file-contents)
158 (load . tramp-handle-load)
159 (make-directory . ignore) ;; tramp-imap-handle-make-directory)
160 (make-directory-internal . ignore) ;; tramp-imap-handle-make-directory-internal)
161 (make-symbolic-link . ignore)
162 (rename-file . tramp-imap-handle-rename-file)
163 (set-file-modes . ignore)
164 ;; `set-file-selinux-context' performed by default handler.
165 (set-file-times . ignore) ;; tramp-imap-handle-set-file-times)
166 (set-visited-file-modtime . ignore)
167 (shell-command . ignore)
168 (substitute-in-file-name . tramp-handle-substitute-in-file-name)
169 (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory)
170 (vc-registered . ignore)
171 (verify-visited-file-modtime . ignore)
172 (write-region . tramp-imap-handle-write-region)
173 (executable-find . ignore)
174 (start-file-process . ignore)
175 (process-file . ignore)
176)
177 "Alist of handler functions for Tramp IMAP method.
178Operations not mentioned here will be handled by the default Emacs primitives.")
179
180(defgroup tramp-imap nil
181 "Tramp over IMAP configuration."
182 :version "23.2"
183 :group 'tramp)
184
185(defcustom tramp-imap-subject-marker "tramp-imap-subject-marker"
186 "The subject marker that Tramp-IMAP will use."
187 :type 'string
188 :version "23.2"
189 :group 'tramp-imap)
190
191;; TODO: these will be defcustoms later.
192(defvar tramp-imap-passphrase-cache nil) ;; can be t or 'never
193(defvar tramp-imap-passphrase nil)
194
195;;;###tramp-autoload
196(defsubst tramp-imap-file-name-p (filename)
197 "Check if it's a filename for IMAP protocol."
198 (let ((v (tramp-dissect-file-name filename)))
199 (or
200 (string= (tramp-file-name-method v) tramp-imap-method)
201 (string= (tramp-file-name-method v) tramp-imaps-method))))
202
203;;;###tramp-autoload
204(defun tramp-imap-file-name-handler (operation &rest args)
205 "Invoke the IMAP related OPERATION.
206First arg specifies the OPERATION, second arg is a list of arguments to
207pass to the OPERATION."
208 (let ((fn (assoc operation tramp-imap-file-name-handler-alist)))
209 (if fn
210 (save-match-data (apply (cdr fn) args))
211 (tramp-run-real-handler operation args))))
212
213;;;###tramp-autoload
214(when (and (locate-library "epa") (locate-library "imap-hash"))
215 (add-to-list 'tramp-foreign-file-name-handler-alist
216 (cons 'tramp-imap-file-name-p 'tramp-imap-file-name-handler)))
217
218(defun tramp-imap-handle-copy-file
219 (filename newname &optional ok-if-already-exists keep-date
220 preserve-uid-gid preserve-selinux-context)
221 "Like `copy-file' for Tramp files."
222 (tramp-imap-do-copy-or-rename-file
223 'copy filename newname ok-if-already-exists keep-date preserve-uid-gid))
224
225(defun tramp-imap-handle-rename-file
226 (filename newname &optional ok-if-already-exists)
227 "Like `rename-file' for Tramp files."
228 (tramp-imap-do-copy-or-rename-file
229 'rename filename newname ok-if-already-exists t t))
230
231(defun tramp-imap-do-copy-or-rename-file
232 (op filename newname &optional ok-if-already-exists keep-date preserve-uid-gid)
233 "Copy or rename a remote file.
234OP must be `copy' or `rename' and indicates the operation to perform.
235FILENAME specifies the file to copy or rename, NEWNAME is the name of
236the new file (for copy) or the new name of the file (for rename).
237OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists already.
238KEEP-DATE means to make sure that NEWNAME has the same timestamp
239as FILENAME. PRESERVE-UID-GID, when non-nil, instructs to keep
240the uid and gid if both files are on the same host.
241
242This function is invoked by `tramp-imap-handle-copy-file' and
243`tramp-imap-handle-rename-file'. It is an error if OP is neither
244of `copy' and `rename'."
245 (unless (memq op '(copy rename))
246 (error "Unknown operation `%s', must be `copy' or `rename'" op))
247 (setq filename (expand-file-name filename))
248 (setq newname (expand-file-name newname))
249 (when (file-directory-p newname)
250 (setq newname (expand-file-name (file-name-nondirectory filename) newname)))
251
252 (let ((t1 (and (tramp-tramp-file-p filename)
253 (tramp-imap-file-name-p filename)))
254 (t2 (and (tramp-tramp-file-p newname)
255 (tramp-imap-file-name-p newname))))
256
257 (with-parsed-tramp-file-name (if t1 filename newname) nil
258 (when (and (not ok-if-already-exists) (file-exists-p newname))
259 (tramp-error
260 v 'file-already-exists "File %s already exists" newname))
261
262 (with-progress-reporter
263 v 0 (format "%s %s to %s"
264 (if (eq op 'copy) "Copying" "Renaming")
265 filename newname)
266
267 ;; We just make a local copy of FILENAME, and write it then to
268 ;; NEWNAME. This must be optimized when both files are
269 ;; located on the same IMAP server.
270 (with-temp-buffer
271 (if (and t1 t2)
272 ;; We don't encrypt.
273 (with-parsed-tramp-file-name newname v1
274 (insert (tramp-imap-get-file filename nil))
275 (tramp-imap-put-file
276 v1 (current-buffer)
277 (tramp-imap-file-name-name v1)
278 nil nil (nth 7 (file-attributes filename))))
279 ;; One of them is not located on a IMAP mailbox.
280 (insert-file-contents filename)
281 (write-region (point-min) (point-max) newname)))))
282
283 (when (eq op 'rename) (delete-file filename))))
284
285;; TODO: revise this much
286(defun tramp-imap-handle-expand-file-name (name &optional dir)
287 "Like `expand-file-name' for Tramp files."
288 ;; If DIR is not given, use DEFAULT-DIRECTORY or "/".
289 (setq dir (or dir default-directory "/"))
290 ;; Unless NAME is absolute, concat DIR and NAME.
291 (unless (file-name-absolute-p name)
292 (setq name (concat (file-name-as-directory dir) name)))
293 ;; If NAME is not a Tramp file, run the real handler.
294 (if (or (tramp-completion-mode-p) (not (tramp-tramp-file-p name)))
295 (tramp-drop-volume-letter
296 (tramp-run-real-handler 'expand-file-name (list name nil)))
297 ;; Dissect NAME.
298 (with-parsed-tramp-file-name name nil
299 (unless (tramp-run-real-handler 'file-name-absolute-p (list localname))
300 (setq localname (concat "/" localname)))
301 ;; There might be a double slash, for example when "~/"
302 ;; expands to "/". Remove this.
303 (while (string-match "//" localname)
304 (setq localname (replace-match "/" t t localname)))
305 ;; Do normal `expand-file-name' (this does "/./" and "/../").
306 ;; We bind `directory-sep-char' here for XEmacs on Windows,
307 ;; which would otherwise use backslash. `default-directory' is
308 ;; bound, because on Windows there would be problems with UNC
309 ;; shares or Cygwin mounts.
310 (let ((default-directory (tramp-compat-temporary-file-directory)))
311 (tramp-make-tramp-file-name
312 method user host
313 (tramp-drop-volume-letter
314 (tramp-run-real-handler
315 'expand-file-name (list localname))))))))
316
317;; This function should return "foo/" for directories and "bar" for
318;; files.
319(defun tramp-imap-handle-file-name-all-completions (filename directory)
320 "Like `file-name-all-completions' for Tramp files."
321 (all-completions
322 filename
323 (with-parsed-tramp-file-name (expand-file-name directory) nil
324 (save-match-data
325 (let ((entries
326 (tramp-imap-get-file-entries v localname)))
327 (mapcar
328 (lambda (x)
329 (list
330 (if (string-match "d" (nth 9 x))
331 (file-name-as-directory (nth 0 x))
332 (nth 0 x))))
333 entries))))))
334
335(defun tramp-imap-get-file-entries (vec localname &optional exact)
336 "Read entries returned by IMAP server. EXACT limits to exact matches.
337Result is a list of (LOCALNAME LINK COUNT UID GID ATIME MTIME CTIME
338SIZE MODE WEIRD INODE DEVICE)."
339 (tramp-message vec 5 "working on %s" localname)
340 (let* ((name (tramp-imap-file-name-name vec))
341 (search-name (or name ""))
342 (search-name (if exact (concat search-name "$") search-name))
343 (iht (tramp-imap-make-iht vec search-name)))
344;; TODO: catch errors
345 ;; (tramp-error vec 'none "bad name %s or mailbox %s" name mbox))
346 (imap-hash-map (lambda (uid headers body)
347 (let ((subject (substring
348 (aget headers 'Subject "")
349 (length tramp-imap-subject-marker)))
350 (from (aget headers 'From ""))
351 (date (date-to-time (aget headers 'Date "")))
352 (size (string-to-number
353 (or (aget headers 'X-Size "0") "0"))))
354 (setq from
355 (if (string-match "<\\([^@]+\\)@" from)
356 (match-string 1 from)
357 "nobody"))
358 (list
359 subject
360 nil
361 -1
362 from
363 "nogroup"
364 date
365 date
366 date
367 size
368 "-rw-rw-rw-"
369 nil
370 uid
371 (tramp-get-device vec))))
372 iht t)))
373
374(defun tramp-imap-handle-write-region (start end filename &optional append visit lockname confirm)
375 "Like `write-region' for Tramp files."
376 (setq filename (expand-file-name filename))
377 (with-parsed-tramp-file-name filename nil
378 ;; XEmacs takes a coding system as the seventh argument, not `confirm'.
379 (when (and (not (featurep 'xemacs))
380 confirm (file-exists-p filename))
381 (unless (y-or-n-p (format "File %s exists; overwrite anyway? "
382 filename))
383 (tramp-error v 'file-error "File not overwritten")))
384 (tramp-flush-file-property v localname)
385 (let* ((old-buffer (current-buffer))
386 (inode (tramp-imap-get-file-inode filename))
387 (min 1)
388 (max (point-max))
389 ;; Make sure we have good start and end values.
390 (start (or start min))
391 (end (or end max))
392 temp-buffer)
393 (with-temp-buffer
394 (setq temp-buffer (if (and (eq start min) (eq end max))
395 old-buffer
396 ;; If this is a region write, insert the substring.
397 (insert
398 (with-current-buffer old-buffer
399 (buffer-substring-no-properties start end)))
400 (current-buffer)))
401 (tramp-imap-put-file v
402 temp-buffer
403 (tramp-imap-file-name-name v)
404 inode
405 t)))
406 (when (eq visit t)
407 (set-visited-file-modtime))))
408
409(defun tramp-imap-handle-insert-directory
410 (filename switches &optional wildcard full-directory-p)
411 "Like `insert-directory' for Tramp files."
412 (setq filename (expand-file-name filename))
413 (if full-directory-p
414 ;; Called from `dired-add-entry'.
415 (setq filename (file-name-as-directory filename))
416 (setq filename (directory-file-name filename)))
417 (with-parsed-tramp-file-name filename nil
418 (save-match-data
419 (let ((base (file-name-nondirectory localname))
420 (entries (copy-sequence
421 (tramp-imap-get-file-entries
422 v (file-name-directory localname)))))
423
424 (when wildcard
425 (when (string-match "\\." base)
426 (setq base (replace-match "\\\\." nil nil base)))
427 (when (string-match "\\*" base)
428 (setq base (replace-match ".*" nil nil base)))
429 (when (string-match "\\?" base)
430 (setq base (replace-match ".?" nil nil base))))
431
432 ;; Filter entries.
433 (setq entries
434 (delq
435 nil
436 (if (or wildcard (zerop (length base)))
437 ;; Check for matching entries.
438 (mapcar
439 (lambda (x)
440 (when (string-match
441 (format "^%s" base) (nth 0 x))
442 x))
443 entries)
444 ;; We just need the only and only entry FILENAME.
445 (list (assoc base entries)))))
446
447 ;; Sort entries.
448 (setq entries
449 (sort
450 entries
451 (lambda (x y)
452 (if (string-match "t" switches)
453 ;; Sort by date.
454 (tramp-time-less-p (nth 6 y) (nth 6 x))
455 ;; Sort by name.
456 (string-lessp (nth 0 x) (nth 0 y))))))
457
458 ;; Handle "-F" switch.
459 (when (string-match "F" switches)
460 (mapc
461 (lambda (x)
462 (when (not (zerop (length (car x))))
463 (cond
464 ((char-equal ?d (string-to-char (nth 9 x)))
465 (setcar x (concat (car x) "/")))
466 ((char-equal ?x (string-to-char (nth 9 x)))
467 (setcar x (concat (car x) "*"))))))
468 entries))
469
470 ;; Print entries.
471 (mapcar
472 (lambda (x)
473 (when (not (zerop (length (nth 0 x))))
474 (insert
475 (format
476 "%10s %3d %-8s %-8s %8s %s "
477 (nth 9 x) ; mode
478 (nth 11 x) ; inode
479 (nth 3 x) ; uid
480 (nth 4 x) ; gid
481 (nth 8 x) ; size
482 (format-time-string
483 (if (tramp-time-less-p
484 (tramp-time-subtract (current-time) (nth 6 x))
485 tramp-half-a-year)
486 "%b %e %R"
487 "%b %e %Y")
488 (nth 6 x)))) ; date
489 ;; For the file name, we set the `dired-filename'
490 ;; property. This allows to handle file names with
491 ;; leading or trailing spaces as well. The inserted name
492 ;; could be from somewhere else, so we use the relative
493 ;; file name of `default-directory'.
494 (let ((pos (point)))
495 (insert
496 (format
497 "%s\n"
498 (file-relative-name
499 (expand-file-name (nth 0 x) (file-name-directory filename)))))
500 (put-text-property pos (1- (point)) 'dired-filename t))
501 (forward-line)
502 (beginning-of-line)))
503 entries)))))
504
505(defun tramp-imap-handle-insert-file-contents
506 (filename &optional visit beg end replace)
507 "Like `insert-file-contents' for Tramp files."
508 (barf-if-buffer-read-only)
509 (when visit
510 (setq buffer-file-name (expand-file-name filename))
511 (set-visited-file-modtime)
512 (set-buffer-modified-p nil))
513 (with-parsed-tramp-file-name filename nil
514 (if (not (file-exists-p filename))
515 (tramp-error
516 v 'file-error "File `%s' not found on remote host" filename)
517 (let ((point (point))
518 size data)
519 (with-progress-reporter v 3 (format "Fetching file %s" filename)
520 (insert (tramp-imap-get-file filename t))
521 (setq size (- (point) point))
522;;; TODO: handle ranges.
523;;; (let ((beg (or beg (point-min)))
524;;; (end (min (or end (point-max)) (point-max))))
525;;; (setq size (- end beg))
526;;; (buffer-substring beg end))
527 (goto-char point)
528 (list (expand-file-name filename) size))))))
529
530(defun tramp-imap-handle-file-directory-p (filename)
531 "Like `file-directory-p' for Tramp-IMAP files."
532 ;; We allow only mailboxes to be a directory.
533 (with-parsed-tramp-file-name (expand-file-name filename default-directory) nil
534 (and (string-match "^/[^/]*$" (directory-file-name localname)) t)))
535
536(defun tramp-imap-handle-file-attributes (filename &optional id-format)
537 "Like `file-attributes' for Tramp-IMAP FILENAME."
538 (with-parsed-tramp-file-name (expand-file-name filename) nil
539 (let ((res (cdr-safe (nth 0 (tramp-imap-get-file-entries v localname)))))
540 (unless (or (null res) (eq id-format 'string))
541 (setcar (nthcdr 2 res) 1)
542 (setcar (nthcdr 3 res) 1))
543 res)))
544
545(defun tramp-imap-get-file-inode (filename &optional id-format)
546 "Get inode equivalent \(actually the UID) for Tramp-IMAP FILENAME."
547 (nth 10 (tramp-compat-file-attributes filename id-format)))
548
549(defun tramp-imap-handle-file-writable-p (filename)
550 "Like `file-writable-p' for Tramp files. True for IMAP."
551 ;; `file-exists-p' does not work yet for directories.
552 ;; (file-exists-p (file-name-directory filename)))
553 (file-directory-p (file-name-directory filename)))
554
555(defun tramp-imap-handle-delete-file (filename &optional trash)
556 "Like `delete-file' for Tramp files."
557 (cond
558 ((not (file-exists-p filename)) nil)
559 (t (with-parsed-tramp-file-name (expand-file-name filename) nil
560 (let ((iht (tramp-imap-make-iht v)))
561 (imap-hash-rem (tramp-imap-get-file-inode filename) iht))))))
562
563(defun tramp-imap-handle-file-local-copy (filename)
564 "Like `file-local-copy' for Tramp files."
565 (with-parsed-tramp-file-name (expand-file-name filename) nil
566 (unless (file-exists-p filename)
567 (tramp-error
568 v 'file-error
569 "Cannot make local copy of non-existing file `%s'" filename))
570 (let ((tmpfile (tramp-compat-make-temp-file filename)))
571 (with-progress-reporter
572 v 3 (format "Fetching %s to tmp file %s" filename tmpfile)
573 (with-temp-buffer
574 (insert-file-contents filename)
575 (write-region (point-min) (point-max) tmpfile)
576 tmpfile)))))
577
578(defun tramp-imap-put-file
579 (vec filename-or-buffer &optional subject inode encode size)
580 "Write contents of FILENAME-OR-BUFFER to Tramp-IMAP file VEC with name SUBJECT.
581When INODE is given, delete that old remote file after writing the new one
582\(normally this is the old file with the same name). A non-nil ENCODE
583forces the encoding of the buffer or file. SIZE, when available, indicates
584the file size; this is needed, if the file or buffer is already encoded."
585 ;; `tramp-current-host' is used in `tramp-imap-passphrase-callback-function'.
586 (let ((tramp-current-host (tramp-file-name-real-host vec))
587 (iht (tramp-imap-make-iht vec)))
588 (imap-hash-put (list
589 (list (cons
590 'Subject
591 (format
592 "%s%s"
593 tramp-imap-subject-marker
594 (or subject "no subject")))
595 (cons
596 'X-Size
597 (number-to-string
598 (cond
599 ((numberp size) size)
600 ((bufferp filename-or-buffer)
601 (buffer-size filename-or-buffer))
602 ((stringp filename-or-buffer)
603 (nth 7 (file-attributes filename-or-buffer)))
604 ;; We don't know the size.
605 (t -1)))))
606 (cond ((bufferp filename-or-buffer)
607 (with-current-buffer filename-or-buffer
608 (if encode
609 (tramp-imap-encode-buffer)
610 (buffer-string))))
611 ;; TODO: allow file names.
612 (t "No body available")))
613 iht
614 inode)))
615
616(defun tramp-imap-get-file (filename &optional decode)
617 ;; (debug (tramp-imap-get-file-inode filename))
618 (with-parsed-tramp-file-name (expand-file-name filename) nil
619 (condition-case ()
620 ;; `tramp-current-host' is used in
621 ;; `tramp-imap-passphrase-callback-function'.
622 (let* ((tramp-current-host (tramp-file-name-real-host v))
623 (iht (tramp-imap-make-iht v))
624 (inode (tramp-imap-get-file-inode filename))
625 (data (imap-hash-get inode iht t)))
626 (if decode
627 (with-temp-buffer
628 (insert (nth 1 data))
629 ;;(debug inode (buffer-string))
630 (tramp-imap-decode-buffer))
631 (nth 1 data)))
632 (error (tramp-error
633 v 'file-error "File `%s' could not be read" filename)))))
634
635(defun tramp-imap-passphrase-callback-function (context key-id handback)
636 "Called by EPG to get a passphrase for Tramp-IMAP.
637CONTEXT is the encryption/decryption EPG context.
638HANDBACK is just carried through.
639KEY-ID can be 'SYM or 'PIN among others."
640 (let* ((server tramp-current-host)
641 (port "tramp-imap") ; this is NOT the server password!
642 (auth-passwd (plist-get
643 (nth 0 (auth-source-search :max 1
644 :host server
645 :port port))
646 :secret))
647 (auth-passwd (if (functionp auth-passwd)
648 (funcall auth-passwd)
649 auth-passwd)))
650 (or
651 (copy-sequence auth-passwd)
652 ;; If we cache the passphrase and we have one.
653 (if (and (eq tramp-imap-passphrase-cache t)
654 tramp-imap-passphrase)
655 ;; Do we reuse it?
656 (if (y-or-n-p "Reuse the passphrase? ")
657 (copy-sequence tramp-imap-passphrase)
658 ;; Don't reuse: revert caching behavior to nil, erase passphrase,
659 ;; call ourselves again.
660 (setq tramp-imap-passphrase-cache nil)
661 (setq tramp-imap-passphrase nil)
662 (tramp-imap-passphrase-callback-function context key-id handback))
663 (let ((p (if (eq key-id 'SYM)
664 (read-passwd
665 "Tramp-IMAP passphrase for symmetric encryption: "
666 (eq (epg-context-operation context) 'encrypt)
667 tramp-imap-passphrase)
668 (read-passwd
669 (if (eq key-id 'PIN)
670 "Tramp-IMAP passphrase for PIN: "
671 (let ((entry (assoc key-id
672 (symbol-value 'epg-user-id-alist))))
673 (if entry
674 (format "Tramp-IMAP passphrase for %s %s: "
675 key-id (cdr entry))
676 (format "Tramp-IMAP passphrase for %s: " key-id))))
677 nil
678 tramp-imap-passphrase))))
679
680 ;; If we have an answer, the passphrase has changed,
681 ;; the user hasn't declined keeping the passphrase,
682 ;; and they answer yes to keep it now...
683 (when (and
684 p
685 (not (equal tramp-imap-passphrase p))
686 (not (eq tramp-imap-passphrase-cache 'never))
687 (y-or-n-p "Keep the passphrase? "))
688 (setq tramp-imap-passphrase (copy-sequence p))
689 (setq tramp-imap-passphrase-cache t))
690
691 ;; If we still don't have a passphrase, the user didn't want
692 ;; to keep it.
693 (when (and
694 p
695 (not tramp-imap-passphrase))
696 (setq tramp-imap-passphrase-cache 'never))
697
698 p)))))
699
700(defun tramp-imap-encode-buffer ()
701 (let ((context (epg-make-context 'OpenPGP))
702 cipher)
703 (epg-context-set-armor context t)
704 (epg-context-set-passphrase-callback context
705 #'tramp-imap-passphrase-callback-function)
706 (epg-context-set-progress-callback context
707 (cons #'epa-progress-callback-function
708 "Encrypting..."))
709 (message "Encrypting...")
710 (setq cipher (epg-encrypt-string
711 context
712 (encode-coding-string (buffer-string) 'utf-8)
713 nil))
714 (message "Encrypting...done")
715 cipher))
716
717(defun tramp-imap-decode-buffer ()
718 (let ((context (epg-make-context 'OpenPGP))
719 plain)
720 (epg-context-set-passphrase-callback context
721 #'tramp-imap-passphrase-callback-function)
722 (epg-context-set-progress-callback context
723 (cons #'epa-progress-callback-function
724 "Decrypting..."))
725 (message "Decrypting...")
726 (setq plain (decode-coding-string
727 (epg-decrypt-string context (buffer-string))
728 'utf-8))
729 (message "Decrypting...done")
730 plain))
731
732(defun tramp-imap-file-name-mailbox (vec)
733 (nth 0 (tramp-imap-file-name-parse vec)))
734
735(defun tramp-imap-file-name-name (vec)
736 (nth 1 (tramp-imap-file-name-parse vec)))
737
738(defun tramp-imap-file-name-localname (vec)
739 (nth 1 (tramp-imap-file-name-parse vec)))
740
741(defun tramp-imap-file-name-parse (vec)
742 (let ((name (substring-no-properties (tramp-file-name-localname vec))))
743 (if (string-match "^/\\([^/]+\\)/?\\(.*\\)$" name)
744 (list (match-string 1 name)
745 (match-string 2 name))
746 nil)))
747
748(defun tramp-imap-make-iht (vec &optional needed-subject)
749 "Translate the Tramp vector VEC to the imap-hash structure.
750With NEEDED-SUBJECT, alters the imap-hash test accordingly."
751 (let* ((mbox (tramp-imap-file-name-mailbox vec))
752 (server (tramp-file-name-real-host vec))
753 (method (tramp-file-name-method vec))
754 (user (tramp-file-name-user vec))
755 (ssl (string-equal method tramp-imaps-method))
756 (port (tramp-file-name-port vec))
757 (result (imap-hash-make server port mbox user nil ssl)))
758 ;; Return the IHT with a test override to look for the subject
759 ;; marker.
760 (plist-put
761 result
762 :test (format "^%s%s"
763 tramp-imap-subject-marker
764 (if needed-subject needed-subject "")))))
765
766(add-hook 'tramp-unload-hook
767 (lambda ()
768 (unload-feature 'tramp-imap 'force)))
769
770;;; TODO:
771
772;; * Implement `tramp-imap-handle-delete-directory',
773;; `tramp-imap-handle-make-directory',
774;; `tramp-imap-handle-make-directory-internal',
775;; `tramp-imap-handle-set-file-times'.
776
777;; * Encode the subject. If the filename has trailing spaces (like
778;; "test "), those characters get lost, for example in dired listings.
779
780;; * When opening a dired buffer, like "/imap::INBOX.test", there are
781;; several error messages:
782;; "Buffer has a running process; kill it? (yes or no) "
783;; "error in process filter: Internal error, tag 6 status BAD code nil text No mailbox selected."
784;; Afterwards, everything seems to be fine.
785
786;; * imaps works for local IMAP servers. Accessing
787;; "/imaps:imap.gmail.com:/INBOX.test/" results in error
788;; "error in process filter: Internal error, tag 5 status BAD code nil text UNSELECT not allowed now."
789
790;; * Improve `tramp-imap-handle-file-attributes' for directories.
791
792;; * Saving a file creates a second one, instead of overwriting.
793
794;; * Backup files: just *one* is kept.
795
796;; * Password requests shall have a descriptive prompt.
797
798;; * Exiting Emacs, there are running IMAP processes. Make them quiet
799;; by `set-process-query-on-exit-flag'.
800
801(provide 'tramp-imap)
802;;; tramp-imap.el ends here
803
804;; Ignore, for testing only.
805
806;;; (setq tramp-imap-subject-marker "T")
807;;; (tramp-imap-get-file-entries (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/4") t)
808;;; (tramp-imap-get-file-entries (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/") t)
809;;; (tramp-imap-get-file-entries (tramp-dissect-file-name "/imap:yourhosthere.com:/test/4") t)
810;;; (tramp-imap-get-file-entries (tramp-dissect-file-name "/imap:yourhosthere.com:/test/") t)
811;;; (tramp-imap-get-file-entries (tramp-dissect-file-name "/imap:yourhosthere.com:/test/welcommen") t)
812;;; (tramp-imap-get-file-entries (tramp-dissect-file-name "/imap:yourhosthere.com:/test/welcommen") t t)
813;;;(tramp-imap-get-file-inode "/imap:yourhosthere.com:/test/welcome")
814;;; (dired-copy-file "/etc/fstab" "/imap:yourhosthere.com:/test/welcome" t)
815;;; (write-region 1 100 "/imap:yourhosthere.com:/test/welcome")
816;;; (tramp-imap-get-file "/imap:yourhosthere.com:/test/welcome" t)
817;;(with-temp-buffer (insert "hello") (write-file "/imap:yourhosthere.com:/test/welcome"))
818;;(with-temp-buffer (insert "hello") (write-file "/imap:yourhosthere.com:/test/welcome2"))
819;;(file-writable-p "/imap:yourhosthere.com:/test/welcome2")
820;;(file-name-directory "/imap:yourhosthere.com:/test/welcome2")
821;;(with-temp-buffer (insert "hello") (delete-file "/tmp/hellotest") (write-file "/tmp/hellotest") (write-file "/imap:yourhosthere.com:/test/welcome2"))
822;;;(file-exists-p "/imap:yourhosthere.com:/INBOX.test/4")
823;;;(file-attributes "/imap:yourhosthere.com:/INBOX.test/4")
824;;;(setq vec (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/4"))
825;;;(tramp-imap-handle-file-attributes "/imap:yourhosthere.com:/INBOX.test/4")
826;;; (tramp-imap-handle-insert-file-contents "/imap:user@yourhosthere.com:/INBOX.test/4" nil nil nil nil)
827;;;(insert-file-contents "/imap:yourhosthere.com:/INBOX.test/4")
828;;;(file-attributes "/imap:yourhosthere.com:/test/welcommen")
829;;;(insert-file-contents "/imap:yourhosthere.com:/test/welcome")
830;;;(file-exists-p "/imap:yourhosthere.com:/test/welcome2")
831;;;(tramp-imap-handle-file-attributes "/imap:yourhosthere.com:/test/welcome")
832;;;(tramp-imap-get-file-inode "/imap:yourhosthere.com:/test/welcommen")
833;;;(tramp-imap-get-file-inode "/imap:yourhosthere.com:/test/welcome")
834;;;(file-writable-p "/imap:yourhosthere.com:/test/welcome2")
835;;; (delete-file "/imap:yourhosthere.com:/test/welcome")
836;;; (tramp-imap-get-file "/imap:yourhosthere.com:/test/welcommen" t)
837;;; (tramp-imap-get-file "/imap:yourhosthere.com:/test/welcome" t)
838;;;(tramp-imap-file-name-mailbox (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test"))
839;;;(tramp-imap-file-name-mailbox (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/new/old"))
840;;;(tramp-imap-file-name-mailbox (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/new"))
841;;;(tramp-imap-file-name-parse (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/new/two"))
842;;;(tramp-imap-file-name-parse (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/new/one"))
843;;;(tramp-imap-file-name-parse (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test"))
844;;; (tramp-imap-file-name-parse (tramp-dissect-file-name "/imap:yourhosthere.com:/test/4"))
845;;; (tramp-imap-file-name-parse (tramp-dissect-file-name "/imap:yourhosthere.com:/test/"))
846;;; (tramp-imap-file-name-parse (tramp-dissect-file-name "/imap:yourhosthere.com:/test/welcommen"))
847;;; (tramp-imap-file-name-parse (tramp-dissect-file-name "/imap:yourhosthere.com:/test/welcommen"))
848;;; (tramp-imap-make-iht (tramp-dissect-file-name "/imap:yourhosthere.com:/test/welcommen"))
849;;; (tramp-imap-make-iht (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/4"))
850;;; (tramp-imap-make-iht (tramp-dissect-file-name "/imap:yourhosthere.com:/INBOX.test/4") "extra")
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 5d0f3935884..9be093743b5 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -3526,23 +3526,24 @@ Invokes `password-read' if available, `read-passwd' else."
3526 (with-parsed-tramp-file-name key nil 3526 (with-parsed-tramp-file-name key nil
3527 (prog1 3527 (prog1
3528 (or 3528 (or
3529 ;; See if auth-sources contains something useful, if it's bound. 3529 ;; See if auth-sources contains something useful, if it's
3530 ;; bound. `auth-source-user-or-password' is an obsoleted
3531 ;; function, it has been replaced by `auth-source-search'.
3530 (and (boundp 'auth-sources) 3532 (and (boundp 'auth-sources)
3531 (tramp-get-connection-property v "first-password-request" nil) 3533 (tramp-get-connection-property v "first-password-request" nil)
3532 ;; Try with Tramp's current method. 3534 ;; Try with Tramp's current method.
3533 (if (fboundp 'auth-source-search) 3535 (if (fboundp 'auth-source-search)
3534 (progn 3536 (setq auth-info
3535 (setq auth-info
3536 (tramp-compat-funcall 3537 (tramp-compat-funcall
3537 'auth-source-search 3538 'auth-source-search
3538 :max 1 3539 :max 1
3539 :user (or tramp-current-user t) 3540 :user (or tramp-current-user t)
3540 :host tramp-current-host 3541 :host tramp-current-host
3541 :port tramp-current-method)) 3542 :port tramp-current-method)
3542 (setq auth-passwd (plist-get (nth 0 auth-info) :secret)) 3543 auth-passwd (plist-get (nth 0 auth-info) :secret)
3543 (setq auth-passwd (if (functionp auth-passwd) 3544 auth-passwd (if (functionp auth-passwd)
3544 (funcall auth-passwd) 3545 (funcall auth-passwd)
3545 auth-passwd))) 3546 auth-passwd))
3546 (tramp-compat-funcall 3547 (tramp-compat-funcall
3547 'auth-source-user-or-password 3548 'auth-source-user-or-password
3548 "password" tramp-current-host tramp-current-method))) 3549 "password" tramp-current-host tramp-current-method)))
diff --git a/lisp/password-cache.el b/lisp/password-cache.el
index fcae55ad597..8738aa65a9f 100644
--- a/lisp/password-cache.el
+++ b/lisp/password-cache.el
@@ -111,9 +111,10 @@ that a password is invalid, so that `password-read' query the
111user again." 111user again."
112 (let ((password (symbol-value (intern-soft key password-data)))) 112 (let ((password (symbol-value (intern-soft key password-data))))
113 (when password 113 (when password
114 (if (fboundp 'clear-string) 114 (when (stringp password)
115 (clear-string password) 115 (if (fboundp 'clear-string)
116 (fillarray password ?_)) 116 (clear-string password)
117 (fillarray password ?_)))
117 (unintern key password-data)))) 118 (unintern key password-data))))
118 119
119(defun password-cache-add (key password) 120(defun password-cache-add (key password)
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index f90d29bf009..de1debd6456 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -5371,8 +5371,6 @@ comment at the start of cc-engine.el for more info."
5371;; cc-mode requires cc-fonts. 5371;; cc-mode requires cc-fonts.
5372(declare-function c-fontify-recorded-types-and-refs "cc-fonts" ()) 5372(declare-function c-fontify-recorded-types-and-refs "cc-fonts" ())
5373 5373
5374(defvar c-forward-<>-arglist-recur-depth)
5375
5376(defun c-forward-<>-arglist (all-types) 5374(defun c-forward-<>-arglist (all-types)
5377 ;; The point is assumed to be at a "<". Try to treat it as the open 5375 ;; The point is assumed to be at a "<". Try to treat it as the open
5378 ;; paren of an angle bracket arglist and move forward to the 5376 ;; paren of an angle bracket arglist and move forward to the
@@ -5398,8 +5396,7 @@ comment at the start of cc-engine.el for more info."
5398 ;; If `c-record-type-identifiers' is set then activate 5396 ;; If `c-record-type-identifiers' is set then activate
5399 ;; recording of any found types that constitute an argument in 5397 ;; recording of any found types that constitute an argument in
5400 ;; the arglist. 5398 ;; the arglist.
5401 (c-record-found-types (if c-record-type-identifiers t)) 5399 (c-record-found-types (if c-record-type-identifiers t)))
5402 (c-forward-<>-arglist-recur--depth 0))
5403 (if (catch 'angle-bracket-arglist-escape 5400 (if (catch 'angle-bracket-arglist-escape
5404 (setq c-record-found-types 5401 (setq c-record-found-types
5405 (c-forward-<>-arglist-recur all-types))) 5402 (c-forward-<>-arglist-recur all-types)))
@@ -5416,14 +5413,6 @@ comment at the start of cc-engine.el for more info."
5416 nil))) 5413 nil)))
5417 5414
5418(defun c-forward-<>-arglist-recur (all-types) 5415(defun c-forward-<>-arglist-recur (all-types)
5419
5420 ;; Temporary workaround for Bug#7722.
5421 (when (boundp 'c-forward-<>-arglist-recur--depth)
5422 (if (> c-forward-<>-arglist-recur--depth 200)
5423 (error "Max recursion depth reached in <> arglist")
5424 (setq c-forward-<>-arglist-recur--depth
5425 (1+ c-forward-<>-arglist-recur--depth))))
5426
5427 ;; Recursive part of `c-forward-<>-arglist'. 5416 ;; Recursive part of `c-forward-<>-arglist'.
5428 ;; 5417 ;;
5429 ;; This function might do hidden buffer changes. 5418 ;; This function might do hidden buffer changes.
@@ -5455,9 +5444,11 @@ comment at the start of cc-engine.el for more info."
5455 (goto-char start) 5444 (goto-char start)
5456 nil)) 5445 nil))
5457 5446
5458 (forward-char) 5447 (forward-char) ; Forward over the opening '<'.
5459 5448
5460 (unless (looking-at c-<-op-cont-regexp) 5449 (unless (looking-at c-<-op-cont-regexp)
5450 ;; go forward one non-alphanumeric character (group) per iteration of
5451 ;; this loop.
5461 (while (and 5452 (while (and
5462 (progn 5453 (progn
5463 (c-forward-syntactic-ws) 5454 (c-forward-syntactic-ws)
@@ -5486,7 +5477,7 @@ comment at the start of cc-engine.el for more info."
5486 (c-forward-type) 5477 (c-forward-type)
5487 (c-forward-syntactic-ws)))))) 5478 (c-forward-syntactic-ws))))))
5488 5479
5489 (setq pos (point)) 5480 (setq pos (point)) ; e.g. first token inside the '<'
5490 5481
5491 ;; Note: These regexps exploit the match order in \| so 5482 ;; Note: These regexps exploit the match order in \| so
5492 ;; that "<>" is matched by "<" rather than "[^>:-]>". 5483 ;; that "<>" is matched by "<" rather than "[^>:-]>".
@@ -5522,37 +5513,35 @@ comment at the start of cc-engine.el for more info."
5522 ;; Either an operator starting with '<' or a nested arglist. 5513 ;; Either an operator starting with '<' or a nested arglist.
5523 (setq pos (point)) 5514 (setq pos (point))
5524 (let (id-start id-end subres keyword-match) 5515 (let (id-start id-end subres keyword-match)
5525 (if (if (looking-at c-<-op-cont-regexp) 5516 (cond
5526 (setq tmp (match-end 0)) 5517 ;; The '<' begins a multi-char operator.
5527 (setq tmp pos) 5518 ((looking-at c-<-op-cont-regexp)
5528 (backward-char) 5519 (setq tmp (match-end 0))
5529 (not 5520 (goto-char (match-end 0)))
5530 (and 5521 ;; We're at a nested <.....>
5531 5522 ((progn
5532 (save-excursion 5523 (setq tmp pos)
5533 ;; There's always an identifier before an angle 5524 (backward-char) ; to the '<'
5534 ;; bracket arglist, or a keyword in 5525 (and
5535 ;; `c-<>-type-kwds' or `c-<>-arglist-kwds'. 5526 (save-excursion
5536 (c-backward-syntactic-ws) 5527 ;; There's always an identifier before an angle
5537 (setq id-end (point)) 5528 ;; bracket arglist, or a keyword in `c-<>-type-kwds'
5538 (c-simple-skip-symbol-backward) 5529 ;; or `c-<>-arglist-kwds'.
5539 (when (or (setq keyword-match 5530 (c-backward-syntactic-ws)
5540 (looking-at c-opt-<>-sexp-key)) 5531 (setq id-end (point))
5541 (not (looking-at c-keywords-regexp))) 5532 (c-simple-skip-symbol-backward)
5542 (setq id-start (point)))) 5533 (when (or (setq keyword-match
5543 5534 (looking-at c-opt-<>-sexp-key))
5544 (setq subres 5535 (not (looking-at c-keywords-regexp)))
5545 (let ((c-promote-possible-types t) 5536 (setq id-start (point))))
5546 (c-record-found-types t)) 5537 (setq subres
5547 (c-forward-<>-arglist-recur 5538 (let ((c-promote-possible-types t)
5548 (and keyword-match 5539 (c-record-found-types t))
5549 (c-keyword-member 5540 (c-forward-<>-arglist-recur
5550 (c-keyword-sym (match-string 1)) 5541 (and keyword-match
5551 'c-<>-type-kwds))))) 5542 (c-keyword-member
5552 ))) 5543 (c-keyword-sym (match-string 1))
5553 5544 'c-<>-type-kwds)))))))
5554 ;; It was not an angle bracket arglist.
5555 (goto-char tmp)
5556 5545
5557 ;; It was an angle bracket arglist. 5546 ;; It was an angle bracket arglist.
5558 (setq c-record-found-types subres) 5547 (setq c-record-found-types subres)
@@ -5567,8 +5556,13 @@ comment at the start of cc-engine.el for more info."
5567 (c-forward-syntactic-ws) 5556 (c-forward-syntactic-ws)
5568 (looking-at c-opt-identifier-concat-key))) 5557 (looking-at c-opt-identifier-concat-key)))
5569 (c-record-ref-id (cons id-start id-end)) 5558 (c-record-ref-id (cons id-start id-end))
5570 (c-record-type-id (cons id-start id-end)))))) 5559 (c-record-type-id (cons id-start id-end)))))
5571 t) 5560
5561 ;; At a "less than" operator.
5562 (t
5563 (forward-char)
5564 )))
5565 t) ; carry on looping.
5572 5566
5573 ((and (not c-restricted-<>-arglists) 5567 ((and (not c-restricted-<>-arglists)
5574 (or (and (eq (char-before) ?&) 5568 (or (and (eq (char-before) ?&)
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index 0d738700cc7..c7bb93f73e7 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -1082,7 +1082,7 @@ casts and declarations are fontified. Used on level 2 and higher."
1082 (boundp 'parse-sexp-lookup-properties)))) 1082 (boundp 'parse-sexp-lookup-properties))))
1083 1083
1084 ;; Below we fontify a whole declaration even when it crosses the limit, 1084 ;; Below we fontify a whole declaration even when it crosses the limit,
1085 ;; to avoid gaps when lazy-lock fontifies the file a screenful at a 1085 ;; to avoid gaps when jit/lazy-lock fontifies the file a block at a
1086 ;; time. That is however annoying during editing, e.g. the following is 1086 ;; time. That is however annoying during editing, e.g. the following is
1087 ;; a common situation while the first line is being written: 1087 ;; a common situation while the first line is being written:
1088 ;; 1088 ;;
@@ -1094,9 +1094,9 @@ casts and declarations are fontified. Used on level 2 and higher."
1094 ;; "some_other_variable" as an identifier, and the latter will not 1094 ;; "some_other_variable" as an identifier, and the latter will not
1095 ;; correct itself until the second line is changed. To avoid that we 1095 ;; correct itself until the second line is changed. To avoid that we
1096 ;; narrow to the limit if the region to fontify is a single line. 1096 ;; narrow to the limit if the region to fontify is a single line.
1097 (narrow-to-region 1097 (if (<= limit (c-point 'bonl))
1098 (point-min) 1098 (narrow-to-region
1099 (if (<= limit (c-point 'bonl)) 1099 (point-min)
1100 (save-excursion 1100 (save-excursion
1101 ;; Narrow after any operator chars following the limit though, 1101 ;; Narrow after any operator chars following the limit though,
1102 ;; since those characters can be useful in recognizing a 1102 ;; since those characters can be useful in recognizing a
@@ -1104,8 +1104,7 @@ casts and declarations are fontified. Used on level 2 and higher."
1104 ;; after the header). 1104 ;; after the header).
1105 (goto-char limit) 1105 (goto-char limit)
1106 (skip-chars-forward c-nonsymbol-chars) 1106 (skip-chars-forward c-nonsymbol-chars)
1107 (point)) 1107 (point))))
1108 limit))
1109 1108
1110 (c-find-decl-spots 1109 (c-find-decl-spots
1111 limit 1110 limit
diff --git a/lisp/simple.el b/lisp/simple.el
index f19525aba4c..4d2a0e69836 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -304,8 +304,8 @@ runs `next-error-hook' with `run-hooks', and stays with that buffer
304until you use it in some other buffer which uses Compilation mode 304until you use it in some other buffer which uses Compilation mode
305or Compilation Minor mode. 305or Compilation Minor mode.
306 306
307See variables `compilation-parse-errors-function' and 307To control which errors are matched, customize the variable
308\`compilation-error-regexp-alist' for customization ideas." 308`compilation-error-regexp-alist'."
309 (interactive "P") 309 (interactive "P")
310 (if (consp arg) (setq reset t arg nil)) 310 (if (consp arg) (setq reset t arg nil))
311 (when (setq next-error-last-buffer (next-error-find-buffer)) 311 (when (setq next-error-last-buffer (next-error-find-buffer))
diff --git a/lisp/textmodes/reftex-index.el b/lisp/textmodes/reftex-index.el
index f75d8b57909..79df6135806 100644
--- a/lisp/textmodes/reftex-index.el
+++ b/lisp/textmodes/reftex-index.el
@@ -1957,7 +1957,7 @@ both ends."
1957 ((equal char ?\C-g) 1957 ((equal char ?\C-g)
1958 (keyboard-quit)) 1958 (keyboard-quit))
1959 ((member char '(?o ?O)) 1959 ((member char '(?o ?O))
1960 ;; Select a differnt macro 1960 ;; Select a different macro
1961 (let* ((nc (reftex-index-select-phrases-macro 2)) 1961 (let* ((nc (reftex-index-select-phrases-macro 2))
1962 (macro-data 1962 (macro-data
1963 (cdr (assoc nc reftex-index-phrases-macro-data))) 1963 (cdr (assoc nc reftex-index-phrases-macro-data)))
diff --git a/lisp/vc/ediff-mult.el b/lisp/vc/ediff-mult.el
index 2aea75aa427..cadcdec29b4 100644
--- a/lisp/vc/ediff-mult.el
+++ b/lisp/vc/ediff-mult.el
@@ -306,7 +306,7 @@ buffers."
306 (nth 3 elt)) 306 (nth 3 elt))
307(defsubst ediff-get-session-objC (elt) 307(defsubst ediff-get-session-objC (elt)
308 (nth 4 elt)) 308 (nth 4 elt))
309;; Take the "name" component of the object into acount. ObjA/C/B is of the form 309;; Take the "name" component of the object into account. ObjA/C/B is of the form
310;; (name . equality-indicator) 310;; (name . equality-indicator)
311(defsubst ediff-get-session-objA-name (elt) 311(defsubst ediff-get-session-objA-name (elt)
312 (car (nth 2 elt))) 312 (car (nth 2 elt)))
diff --git a/lisp/vc/ediff-ptch.el b/lisp/vc/ediff-ptch.el
index 468d12057ab..d930a1bec69 100644
--- a/lisp/vc/ediff-ptch.el
+++ b/lisp/vc/ediff-ptch.el
@@ -417,7 +417,7 @@ Ediff has inferred that
417are two possible targets for applying the patch. 417are two possible targets for applying the patch.
418Both files seem to be plausible alternatives. 418Both files seem to be plausible alternatives.
419 419
420Please advice: 420Please advise:
421 Type `y' to use %s as the target; 421 Type `y' to use %s as the target;
422 Type `n' to use %s as the target. 422 Type `n' to use %s as the target.
423" 423"
diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el
index 11ffc9a5e36..fa731e77a6e 100644
--- a/lisp/vc/log-view.el
+++ b/lisp/vc/log-view.el
@@ -130,6 +130,7 @@
130 ("z" . kill-this-buffer) 130 ("z" . kill-this-buffer)
131 ("q" . quit-window) 131 ("q" . quit-window)
132 ("g" . revert-buffer) 132 ("g" . revert-buffer)
133 ("\C-m" . log-view-toggle-entry-display)
133 134
134 ("m" . log-view-toggle-mark-entry) 135 ("m" . log-view-toggle-mark-entry)
135 ("e" . log-view-modify-change-comment) 136 ("e" . log-view-modify-change-comment)
@@ -147,7 +148,6 @@
147 ("\M-n" . log-view-file-next) 148 ("\M-n" . log-view-file-next)
148 ("\M-p" . log-view-file-prev)) 149 ("\M-p" . log-view-file-prev))
149 "Log-View's keymap." 150 "Log-View's keymap."
150 :inherit widget-keymap
151 :group 'log-view) 151 :group 'log-view)
152 152
153(easy-menu-define log-view-mode-menu log-view-mode-map 153(easy-menu-define log-view-mode-menu log-view-mode-map
@@ -168,6 +168,8 @@
168 :help "Annotate the version at point"] 168 :help "Annotate the version at point"]
169 ["Modify Log Comment" log-view-modify-change-comment 169 ["Modify Log Comment" log-view-modify-change-comment
170 :help "Edit the change comment displayed at point"] 170 :help "Edit the change comment displayed at point"]
171 ["Toggle Details at Point" log-view-toggle-entry-display
172 :active log-view-expanded-log-entry-function]
171 "-----" 173 "-----"
172 ["Next Log Entry" log-view-msg-next 174 ["Next Log Entry" log-view-msg-next
173 :help "Go to the next count'th log message"] 175 :help "Go to the next count'th log message"]
@@ -181,6 +183,12 @@
181(defvar log-view-mode-hook nil 183(defvar log-view-mode-hook nil
182 "Hook run at the end of `log-view-mode'.") 184 "Hook run at the end of `log-view-mode'.")
183 185
186(defvar log-view-expanded-log-entry-function nil
187 "Function returning the detailed description of a Log View entry.
188It is called by the command `log-view-toggle-entry-display' with
189one arg, the revision tag (a string), and should return a string.
190If it is nil, `log-view-toggle-entry-display' does nothing.")
191
184(defface log-view-file 192(defface log-view-file
185 '((((class color) (background light)) 193 '((((class color) (background light))
186 (:background "grey70" :weight bold)) 194 (:background "grey70" :weight bold))
@@ -300,15 +308,36 @@ The match group number 1 should match the revision number itself.")
300 (when cvsdir (setq dir (expand-file-name cvsdir dir)))) 308 (when cvsdir (setq dir (expand-file-name cvsdir dir))))
301 (expand-file-name file dir)))) 309 (expand-file-name file dir))))
302 310
303(defun log-view-current-tag (&optional where) 311(defun log-view-current-entry (&optional pos move)
304 (save-excursion 312 "Return the position and revision tag of the Log View entry at POS.
305 (when where (goto-char where)) 313This is a list (BEG TAG), where BEG is a buffer position and TAG
306 (forward-line 1) 314is a string. If POS is nil or omitted, it defaults to point.
307 (let ((pt (point))) 315If there is no entry at POS, return nil.
308 (when (re-search-backward log-view-message-re nil t) 316
309 (let ((rev (match-string-no-properties 1))) 317If optional arg MOVE is non-nil, move point to BEG if found.
310 (unless (re-search-forward log-view-file-re pt t) 318Otherwise, don't move point."
311 rev)))))) 319 (let ((looping t)
320 result)
321 (save-excursion
322 (when pos (goto-char pos))
323 (forward-line 1)
324 (while looping
325 (setq pos (re-search-backward log-view-message-re nil 'move)
326 looping (and pos (log-view-inside-comment-p (point)))))
327 (when pos
328 (setq result
329 (list pos (match-string-no-properties 1)))))
330 (and move result (goto-char pos))
331 result))
332
333(defun log-view-inside-comment-p (pos)
334 "Return non-nil if POS lies inside an expanded log entry."
335 (eq (get-text-property pos 'log-view-comment) t))
336
337(defun log-view-current-tag (&optional pos)
338 "Return the revision tag (a string) of the Log View entry at POS.
339if POS is omitted or nil, it defaults to point."
340 (cadr (log-view-current-entry pos)))
312 341
313(defun log-view-toggle-mark-entry () 342(defun log-view-toggle-mark-entry ()
314 "Toggle the marked state for the log entry at point. 343 "Toggle the marked state for the log entry at point.
@@ -318,29 +347,24 @@ entries are denoted by changing their background color.
318log entries." 347log entries."
319 (interactive) 348 (interactive)
320 (save-excursion 349 (save-excursion
321 (forward-line 1) 350 (let* ((entry (log-view-current-entry nil t))
322 (let ((pt (point))) 351 (beg (car entry))
323 (when (re-search-backward log-view-message-re nil t) 352 found)
324 (let ((beg (match-beginning 0)) 353 (when entry
325 end ov ovlist found tag) 354 ;; Look to see if the current entry is marked.
326 (unless (re-search-forward log-view-file-re pt t) 355 (setq found (get-char-property beg 'log-view-self))
327 ;; Look to see if the current entry is marked. 356 (if found
328 (setq found (get-char-property (point) 'log-view-self)) 357 (delete-overlay found)
329 (if found 358 ;; Create an overlay covering this entry and change its color.
330 (delete-overlay found) 359 (let* ((end (if (get-text-property beg 'log-view-entry-expanded)
331 ;; Create an overlay that covers this entry and change 360 (next-single-property-change beg 'log-view-comment)
332 ;; its color. 361 (log-view-end-of-defun)
333 (setq tag (log-view-current-tag (point))) 362 (point)))
334 (forward-line 1) 363 (ov (make-overlay beg end)))
335 (setq end 364 (overlay-put ov 'face 'log-view-file)
336 (if (re-search-forward log-view-message-re nil t) 365 ;; This is used to check if the overlay is present.
337 (match-beginning 0) 366 (overlay-put ov 'log-view-self ov)
338 (point-max))) 367 (overlay-put ov 'log-view-marked (nth 1 entry))))))))
339 (setq ov (make-overlay beg end))
340 (overlay-put ov 'face 'log-view-file)
341 ;; This is used to check if the overlay is present.
342 (overlay-put ov 'log-view-self ov)
343 (overlay-put ov 'log-view-marked tag))))))))
344 368
345(defun log-view-get-marked () 369(defun log-view-get-marked ()
346 "Return the list of tags for the marked log entries." 370 "Return the list of tags for the marked log entries."
@@ -353,50 +377,74 @@ log entries."
353 (setq pos (overlay-end ov)))) 377 (setq pos (overlay-end ov))))
354 marked-list))) 378 marked-list)))
355 379
356(defun log-view-beginning-of-defun () 380(defun log-view-toggle-entry-display ()
357 ;; This assumes that a log entry starts with a line matching 381 (interactive)
358 ;; `log-view-message-re'. Modes that derive from `log-view-mode' 382 ;; Don't do anything unless `log-view-expanded-log-entry-function'
359 ;; for which this assumption is not valid will have to provide 383 ;; is defined in this mode.
360 ;; another implementation of this function. `log-view-msg-prev' 384 (when (functionp log-view-expanded-log-entry-function)
361 ;; does a similar job to this function, we can't use it here 385 (let* ((opoint (point))
362 ;; directly because it prints messages that are not appropriate in 386 (entry (log-view-current-entry nil t))
363 ;; this context and it does not move to the beginning of the buffer 387 (beg (car entry))
364 ;; when the point is before the first log entry. 388 (buffer-read-only nil))
365 389 (when entry
366 ;; `log-view-beginning-of-defun' and `log-view-end-of-defun' have 390 (if (get-text-property beg 'log-view-entry-expanded)
367 ;; been checked to work with logs produced by RCS, CVS, git, 391 ;; If the entry is expanded, collapse it.
368 ;; mercurial and subversion. 392 (let ((pos (next-single-property-change beg 'log-view-comment)))
369 393 (unless (and pos (log-view-inside-comment-p pos))
370 (re-search-backward log-view-message-re nil 'move)) 394 (error "Broken markup in `log-view-toggle-entry-display'"))
395 (delete-region pos
396 (next-single-property-change pos 'log-view-comment))
397 (put-text-property beg (1+ beg) 'log-view-entry-expanded nil)
398 (if (< opoint pos)
399 (goto-char opoint)))
400 ;; Otherwise, expand the entry.
401 (let ((long-entry (funcall log-view-expanded-log-entry-function
402 (nth 1 entry))))
403 (when long-entry
404 (put-text-property beg (1+ beg) 'log-view-entry-expanded t)
405 (log-view-end-of-defun)
406 (setq beg (point))
407 (insert long-entry "\n")
408 (add-text-properties
409 beg (point)
410 '(font-lock-face font-lock-comment-face log-view-comment t))
411 (goto-char opoint))))))))
412
413(defun log-view-beginning-of-defun (&optional arg)
414 "Move backward to the beginning of a Log View entry.
415With ARG, do it that many times. Negative ARG means move forward
416to the beginning of the ARGth following entry.
417
418This is Log View mode's default `beginning-of-defun-function'.
419It assumes that a log entry starts with a line matching
420`log-view-message-re'."
421 (if (or (null arg) (zerop arg))
422 (setq arg 1))
423 (if (< arg 0)
424 (dotimes (n (- arg))
425 (log-view-end-of-defun))
426 (catch 'beginning-of-buffer
427 (dotimes (n arg)
428 (or (log-view-current-entry nil t)
429 (throw 'beginning-of-buffer nil)))
430 (point))))
371 431
372(defun log-view-end-of-defun () 432(defun log-view-end-of-defun ()
373 ;; The idea in this function is to search for the beginning of the 433 "Move forward to the next Log View entry."
374 ;; next log entry using `log-view-message-re' and then go back one 434 (let ((looping t))
375 ;; line when finding it. Modes that derive from `log-view-mode' for 435 (if (looking-at log-view-message-re)
376 ;; which this assumption is not valid will have to provide another 436 (goto-char (match-end 0)))
377 ;; implementation of this function. 437 (while looping
378 438 (cond
379 ;; Look back and if there is no entry there it means we are before 439 ((re-search-forward log-view-message-re nil 'move)
380 ;; the first log entry, so go forward until finding one. 440 (unless (log-view-inside-comment-p (point))
381 (unless (save-excursion (re-search-backward log-view-message-re nil t)) 441 (setq looping nil)
382 (re-search-forward log-view-message-re nil t)) 442 (goto-char (match-beginning 0))))
383 443 ;; Don't advance past the end buttons inserted by
384 ;; In case we are at the end of log entry going forward a line will 444 ;; `vc-print-log-setup-buttons'.
385 ;; make us find the next entry when searching. If we are inside of 445 ((looking-back "Show 2X entries Show unlimited entries")
386 ;; an entry going forward a line will still keep the point inside 446 (setq looping nil)
387 ;; the same entry. 447 (forward-line -1))))))
388 (forward-line 1)
389
390 ;; In case we are at the beginning of an entry, move past it.
391 (when (looking-at log-view-message-re)
392 (goto-char (match-end 0))
393 (forward-line 1))
394
395 ;; Search for the start of the next log entry. Go to the end of the
396 ;; buffer if we could not find a next entry.
397 (when (re-search-forward log-view-message-re nil 'move)
398 (goto-char (match-beginning 0))
399 (forward-line -1)))
400 448
401(defvar cvs-minor-current-files) 449(defvar cvs-minor-current-files)
402(defvar cvs-branch-prefix) 450(defvar cvs-branch-prefix)
diff --git a/lisp/vc/vc-bzr.el b/lisp/vc/vc-bzr.el
index 9f86a28a575..a36fdc60d15 100644
--- a/lisp/vc/vc-bzr.el
+++ b/lisp/vc/vc-bzr.el
@@ -590,6 +590,7 @@ REV non-nil gets an error."
590(defvar log-view-font-lock-keywords) 590(defvar log-view-font-lock-keywords)
591(defvar log-view-current-tag-function) 591(defvar log-view-current-tag-function)
592(defvar log-view-per-file-logs) 592(defvar log-view-per-file-logs)
593(defvar log-view-expanded-log-entry-function)
593 594
594(define-derived-mode vc-bzr-log-view-mode log-view-mode "Bzr-Log-View" 595(define-derived-mode vc-bzr-log-view-mode log-view-mode "Bzr-Log-View"
595 (remove-hook 'log-view-mode-hook 'vc-bzr-log-view-mode) ;Deactivate the hack. 596 (remove-hook 'log-view-mode-hook 'vc-bzr-log-view-mode) ;Deactivate the hack.
@@ -600,6 +601,11 @@ REV non-nil gets an error."
600 (if (eq vc-log-view-type 'short) 601 (if (eq vc-log-view-type 'short)
601 "^ *\\([0-9.]+\\): \\(.*?\\)[ \t]+\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)\\( \\[merge\\]\\)?" 602 "^ *\\([0-9.]+\\): \\(.*?\\)[ \t]+\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)\\( \\[merge\\]\\)?"
602 "^ *\\(?:revno: \\([0-9.]+\\)\\|merged: .+\\)")) 603 "^ *\\(?:revno: \\([0-9.]+\\)\\|merged: .+\\)"))
604 ;; Allow expanding short log entries
605 (when (eq vc-log-view-type 'short)
606 (setq truncate-lines t)
607 (set (make-local-variable 'log-view-expanded-log-entry-function)
608 'vc-bzr-expanded-log-entry))
603 (set (make-local-variable 'log-view-font-lock-keywords) 609 (set (make-local-variable 'log-view-font-lock-keywords)
604 ;; log-view-font-lock-keywords is careful to use the buffer-local 610 ;; log-view-font-lock-keywords is careful to use the buffer-local
605 ;; value of log-view-message-re only since Emacs-23. 611 ;; value of log-view-message-re only since Emacs-23.
@@ -637,6 +643,16 @@ REV non-nil gets an error."
637 (list vc-bzr-log-switches) 643 (list vc-bzr-log-switches)
638 vc-bzr-log-switches))))) 644 vc-bzr-log-switches)))))
639 645
646(defun vc-bzr-expanded-log-entry (revision)
647 (with-temp-buffer
648 (apply 'vc-bzr-command "log" t nil nil
649 (list (format "-r%s" revision)))
650 (goto-char (point-min))
651 (when (looking-at "^-+\n")
652 ;; Indent the expanded log entry.
653 (indent-region (match-end 0) (point-max) 2)
654 (buffer-substring (match-end 0) (point-max)))))
655
640(defun vc-bzr-log-incoming (buffer remote-location) 656(defun vc-bzr-log-incoming (buffer remote-location)
641 (apply 'vc-bzr-command "missing" buffer 'async nil 657 (apply 'vc-bzr-command "missing" buffer 'async nil
642 (list "--theirs-only" (unless (string= remote-location "") remote-location)))) 658 (list "--theirs-only" (unless (string= remote-location "") remote-location))))
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index de729c969ae..3b4d0e5f421 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -119,6 +119,27 @@ If nil, use the value of `vc-diff-switches'. If t, use no switches."
119 :version "23.1" 119 :version "23.1"
120 :group 'vc) 120 :group 'vc)
121 121
122(defcustom vc-git-root-log-format
123 '("%d%h..: %an %ad %s"
124 ;; The first shy group matches the characters drawn by --graph.
125 ;; We use numbered groups because `log-view-message-re' wants the
126 ;; revision number to be group 1.
127 "^\\(?:[*/\\| ]+ \\)?\\(?2: ([^)]+)\\)?\\(?1:[0-9a-z]+\\)..: \
128\\(?3:.*?\\)[ \t]+\\(?4:[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)"
129 ((1 'log-view-message-face)
130 (2 'change-log-list nil lax)
131 (3 'change-log-name)
132 (4 'change-log-date)))
133 "Git log format for `vc-print-root-log'.
134This should be a list (FORMAT REGEXP KEYWORDS), where FORMAT is a
135format string (which is passed to \"git log\" via the argument
136\"--pretty=tformat:FORMAT\"), REGEXP is a regular expression
137matching the resulting Git log output, and KEYWORDS is a list of
138`font-lock-keywords' for highlighting the Log View buffer."
139 :type '(list string string (repeat sexp))
140 :group 'vc
141 :version "24.1")
142
122(defvar vc-git-commits-coding-system 'utf-8 143(defvar vc-git-commits-coding-system 'utf-8
123 "Default coding system for git commits.") 144 "Default coding system for git commits.")
124 145
@@ -666,8 +687,10 @@ for the --graph option."
666 (append 687 (append
667 '("log" "--no-color") 688 '("log" "--no-color")
668 (when shortlog 689 (when shortlog
669 '("--graph" "--decorate" "--date=short" 690 `("--graph" "--decorate" "--date=short"
670 "--pretty=tformat:%d%h %ad %s" "--abbrev-commit")) 691 ,(format "--pretty=tformat:%s"
692 (car vc-git-root-log-format))
693 "--abbrev-commit"))
671 (when limit (list "-n" (format "%s" limit))) 694 (when limit (list "-n" (format "%s" limit)))
672 (when start-revision (list start-revision)) 695 (when start-revision (list start-revision))
673 '("--"))))))) 696 '("--")))))))
@@ -678,7 +701,8 @@ for the --graph option."
678 buffer 0 nil 701 buffer 0 nil
679 "log" 702 "log"
680 "--no-color" "--graph" "--decorate" "--date=short" 703 "--no-color" "--graph" "--decorate" "--date=short"
681 "--pretty=tformat:%d%h %ad %s" "--abbrev-commit" 704 (format "--pretty=tformat:%s" (car vc-git-root-log-format))
705 "--abbrev-commit"
682 (concat (if (string= remote-location "") 706 (concat (if (string= remote-location "")
683 "@{upstream}" 707 "@{upstream}"
684 remote-location) 708 remote-location)
@@ -689,9 +713,10 @@ for the --graph option."
689 (vc-git-command nil 0 nil "fetch") 713 (vc-git-command nil 0 nil "fetch")
690 (vc-git-command 714 (vc-git-command
691 buffer 0 nil 715 buffer 0 nil
692 "log" 716 "log"
693 "--no-color" "--graph" "--decorate" "--date=short" 717 "--no-color" "--graph" "--decorate" "--date=short"
694 "--pretty=tformat:%d%h %ad %s" "--abbrev-commit" 718 (format "--pretty=tformat:%s" (car vc-git-root-log-format))
719 "--abbrev-commit"
695 (concat "HEAD.." (if (string= remote-location "") 720 (concat "HEAD.." (if (string= remote-location "")
696 "@{upstream}" 721 "@{upstream}"
697 remote-location)))) 722 remote-location))))
@@ -700,6 +725,7 @@ for the --graph option."
700(defvar log-view-file-re) 725(defvar log-view-file-re)
701(defvar log-view-font-lock-keywords) 726(defvar log-view-font-lock-keywords)
702(defvar log-view-per-file-logs) 727(defvar log-view-per-file-logs)
728(defvar log-view-expanded-log-entry-function)
703 729
704(define-derived-mode vc-git-log-view-mode log-view-mode "Git-Log-View" 730(define-derived-mode vc-git-log-view-mode log-view-mode "Git-Log-View"
705 (require 'add-log) ;; We need the faces add-log. 731 (require 'add-log) ;; We need the faces add-log.
@@ -708,37 +734,37 @@ for the --graph option."
708 (set (make-local-variable 'log-view-per-file-logs) nil) 734 (set (make-local-variable 'log-view-per-file-logs) nil)
709 (set (make-local-variable 'log-view-message-re) 735 (set (make-local-variable 'log-view-message-re)
710 (if (not (eq vc-log-view-type 'long)) 736 (if (not (eq vc-log-view-type 'long))
711 "^\\(?:[*/\\| ]+ \\)?\\(?: ([^)]+)\\)?\\([0-9a-z]+\\) \\([-a-z0-9]+\\) \\(.*\\)" 737 (cadr vc-git-root-log-format)
712 "^commit *\\([0-9a-z]+\\)")) 738 "^commit *\\([0-9a-z]+\\)"))
739 ;; Allow expanding short log entries
740 (when (eq vc-log-view-type 'short)
741 (setq truncate-lines t)
742 (set (make-local-variable 'log-view-expanded-log-entry-function)
743 'vc-git-expanded-log-entry))
713 (set (make-local-variable 'log-view-font-lock-keywords) 744 (set (make-local-variable 'log-view-font-lock-keywords)
714 (if (not (eq vc-log-view-type 'long)) 745 (if (not (eq vc-log-view-type 'long))
715 '( 746 (list (cons (nth 1 vc-git-root-log-format)
716 ;; Same as log-view-message-re, except that we don't 747 (nth 2 vc-git-root-log-format)))
717 ;; want the shy group for the tag name. 748 (append
718 ("^\\(?:[*/\\| ]+ \\)?\\( ([^)]+)\\)?\\([0-9a-z]+\\) \\([-a-z0-9]+\\) \\(.*\\)" 749 `((,log-view-message-re (1 'change-log-acknowledgement)))
719 (1 'highlight nil lax) 750 ;; Handle the case:
720 (2 'change-log-acknowledgement) 751 ;; user: foo@bar
721 (3 'change-log-date))) 752 '(("^Author:[ \t]+\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)"
722 (append 753 (1 'change-log-email))
723 `((,log-view-message-re (1 'change-log-acknowledgement))) 754 ;; Handle the case:
724 ;; Handle the case: 755 ;; user: FirstName LastName <foo@bar>
725 ;; user: foo@bar 756 ("^Author:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]"
726 '(("^Author:[ \t]+\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)" 757 (1 'change-log-name)
727 (1 'change-log-email)) 758 (2 'change-log-email))
728 ;; Handle the case: 759 ("^ +\\(?:\\(?:[Aa]cked\\|[Ss]igned-[Oo]ff\\)-[Bb]y:\\)[ \t]+\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)"
729 ;; user: FirstName LastName <foo@bar> 760 (1 'change-log-name))
730 ("^Author:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]" 761 ("^ +\\(?:\\(?:[Aa]cked\\|[Ss]igned-[Oo]ff\\)-[Bb]y:\\)[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]"
731 (1 'change-log-name) 762 (1 'change-log-name)
732 (2 'change-log-email)) 763 (2 'change-log-email))
733 ("^ +\\(?:\\(?:[Aa]cked\\|[Ss]igned-[Oo]ff\\)-[Bb]y:\\)[ \t]+\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)" 764 ("^Merge: \\([0-9a-z]+\\) \\([0-9a-z]+\\)"
734 (1 'change-log-name)) 765 (1 'change-log-acknowledgement)
735 ("^ +\\(?:\\(?:[Aa]cked\\|[Ss]igned-[Oo]ff\\)-[Bb]y:\\)[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]" 766 (2 'change-log-acknowledgement))
736 (1 'change-log-name) 767 ("^Date: \\(.+\\)" (1 'change-log-date))
737 (2 'change-log-email))
738 ("^Merge: \\([0-9a-z]+\\) \\([0-9a-z]+\\)"
739 (1 'change-log-acknowledgement)
740 (2 'change-log-acknowledgement))
741 ("^Date: \\(.+\\)" (1 'change-log-date))
742 ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message))))))) 768 ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message)))))))
743 769
744 770
@@ -758,6 +784,15 @@ or BRANCH^ (where \"^\" can be repeated)."
758 (t nil)))) 784 (t nil))))
759 (beginning-of-line))) 785 (beginning-of-line)))
760 786
787(defun vc-git-expanded-log-entry (revision)
788 (with-temp-buffer
789 (apply 'vc-git-command t nil nil (list "log" revision "-1"))
790 (goto-char (point-min))
791 (unless (eobp)
792 ;; Indent the expanded log entry.
793 (indent-region (point-min) (point-max) 2)
794 (buffer-string))))
795
761(defun vc-git-diff (files &optional rev1 rev2 buffer) 796(defun vc-git-diff (files &optional rev1 rev2 buffer)
762 "Get a difference report using Git between two revisions of FILES." 797 "Get a difference report using Git between two revisions of FILES."
763 (let (process-file-side-effects) 798 (let (process-file-side-effects)
diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index 10348544357..d283c39362a 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -138,6 +138,24 @@ If nil, use the value of `vc-diff-switches'. If t, use no switches."
138 "Name of the Mercurial executable (excluding any arguments)." 138 "Name of the Mercurial executable (excluding any arguments)."
139 :type 'string 139 :type 'string
140 :group 'vc) 140 :group 'vc)
141
142(defcustom vc-hg-root-log-format
143 '("{rev}:{tags}: {author|person} {date|shortdate} {desc|firstline}\\n"
144 "^\\([0-9]+\\):\\([^:]*\\): \\(.*?\\)[ \t]+\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)"
145 ((1 'log-view-message-face)
146 (2 'change-log-list)
147 (3 'change-log-name)
148 (4 'change-log-date)))
149 "Mercurial log template for `vc-print-root-log'.
150This should be a list (TEMPLATE REGEXP KEYWORDS), where TEMPLATE
151is the \"--template\" argument string to pass to Mercurial,
152REGEXP is a regular expression matching the resulting Mercurial
153output, and KEYWORDS is a list of `font-lock-keywords' for
154highlighting the Log View buffer."
155 :type '(list string string (repeat sexp))
156 :group 'vc
157 :version "24.1")
158
141 159
142;;; Properties of the backend 160;;; Properties of the backend
143 161
@@ -266,13 +284,14 @@ If nil, use the value of `vc-diff-switches'. If t, use no switches."
266 (nconc 284 (nconc
267 (when start-revision (list (format "-r%s:" start-revision))) 285 (when start-revision (list (format "-r%s:" start-revision)))
268 (when limit (list "-l" (format "%s" limit))) 286 (when limit (list "-l" (format "%s" limit)))
269 (when shortlog (list "--style" "compact")) 287 (when shortlog (list "--template" (car vc-hg-root-log-format)))
270 vc-hg-log-switches))))) 288 vc-hg-log-switches)))))
271 289
272(defvar log-view-message-re) 290(defvar log-view-message-re)
273(defvar log-view-file-re) 291(defvar log-view-file-re)
274(defvar log-view-font-lock-keywords) 292(defvar log-view-font-lock-keywords)
275(defvar log-view-per-file-logs) 293(defvar log-view-per-file-logs)
294(defvar log-view-expanded-log-entry-function)
276 295
277(define-derived-mode vc-hg-log-view-mode log-view-mode "Hg-Log-View" 296(define-derived-mode vc-hg-log-view-mode log-view-mode "Hg-Log-View"
278 (require 'add-log) ;; we need the add-log faces 297 (require 'add-log) ;; we need the add-log faces
@@ -280,33 +299,34 @@ If nil, use the value of `vc-diff-switches'. If t, use no switches."
280 (set (make-local-variable 'log-view-per-file-logs) nil) 299 (set (make-local-variable 'log-view-per-file-logs) nil)
281 (set (make-local-variable 'log-view-message-re) 300 (set (make-local-variable 'log-view-message-re)
282 (if (eq vc-log-view-type 'short) 301 (if (eq vc-log-view-type 'short)
283 "^\\([0-9]+\\)\\(\\[.*\\]\\)? +\\([0-9a-z]\\{12\\}\\) +\\(\\(?:[0-9]+\\)-\\(?:[0-9]+\\)-\\(?:[0-9]+\\) \\(?:[0-9]+\\):\\(?:[0-9]+\\) \\(?:[-+0-9]+\\)\\) +\\(.*\\)$" 302 (cadr vc-hg-root-log-format)
284 "^changeset:[ \t]*\\([0-9]+\\):\\(.+\\)")) 303 "^changeset:[ \t]*\\([0-9]+\\):\\(.+\\)"))
304 ;; Allow expanding short log entries
305 (when (eq vc-log-view-type 'short)
306 (setq truncate-lines t)
307 (set (make-local-variable 'log-view-expanded-log-entry-function)
308 'vc-hg-expanded-log-entry))
285 (set (make-local-variable 'log-view-font-lock-keywords) 309 (set (make-local-variable 'log-view-font-lock-keywords)
286 (if (eq vc-log-view-type 'short) 310 (if (eq vc-log-view-type 'short)
287 (append `((,log-view-message-re 311 (list (cons (nth 1 vc-hg-root-log-format)
288 (1 'log-view-message-face) 312 (nth 2 vc-hg-root-log-format)))
289 (2 'highlight nil lax) 313 (append
290 (3 'log-view-message-face) 314 log-view-font-lock-keywords
291 (4 'change-log-date) 315 '(
292 (5 'change-log-name)))) 316 ;; Handle the case:
293 (append 317 ;; user: FirstName LastName <foo@bar>
294 log-view-font-lock-keywords 318 ("^user:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]"
295 '( 319 (1 'change-log-name)
296 ;; Handle the case: 320 (2 'change-log-email))
297 ;; user: FirstName LastName <foo@bar> 321 ;; Handle the cases:
298 ("^user:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]" 322 ;; user: foo@bar
299 (1 'change-log-name) 323 ;; and
300 (2 'change-log-email)) 324 ;; user: foo
301 ;; Handle the cases: 325 ("^user:[ \t]+\\([A-Za-z0-9_.+-]+\\(?:@[A-Za-z0-9_.-]+\\)?\\)"
302 ;; user: foo@bar 326 (1 'change-log-email))
303 ;; and 327 ("^date: \\(.+\\)" (1 'change-log-date))
304 ;; user: foo 328 ("^tag: +\\([^ ]+\\)$" (1 'highlight))
305 ("^user:[ \t]+\\([A-Za-z0-9_.+-]+\\(?:@[A-Za-z0-9_.-]+\\)?\\)" 329 ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message)))))))
306 (1 'change-log-email))
307 ("^date: \\(.+\\)" (1 'change-log-date))
308 ("^tag: +\\([^ ]+\\)$" (1 'highlight))
309 ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message)))))))
310 330
311(defun vc-hg-diff (files &optional oldvers newvers buffer) 331(defun vc-hg-diff (files &optional oldvers newvers buffer)
312 "Get a difference report using hg between two revisions of FILES." 332 "Get a difference report using hg between two revisions of FILES."
@@ -324,6 +344,16 @@ If nil, use the value of `vc-diff-switches'. If t, use no switches."
324 (list "-r" oldvers "-r" newvers) 344 (list "-r" oldvers "-r" newvers)
325 (list "-r" oldvers))))))) 345 (list "-r" oldvers)))))))
326 346
347(defun vc-hg-expanded-log-entry (revision)
348 (with-temp-buffer
349 (vc-hg-command t nil nil "log" "-r" revision)
350 (goto-char (point-min))
351 (unless (eobp)
352 ;; Indent the expanded log entry.
353 (indent-region (point-min) (point-max) 2)
354 (goto-char (point-max))
355 (buffer-string))))
356
327(defun vc-hg-revision-table (files) 357(defun vc-hg-revision-table (files)
328 (let ((default-directory (file-name-directory (car files)))) 358 (let ((default-directory (file-name-directory (car files))))
329 (with-temp-buffer 359 (with-temp-buffer
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index be0f568d304..815bdbfc5bf 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -2014,22 +2014,20 @@ Not all VC backends support short logs!")
2014 (goto-char (point-max)) 2014 (goto-char (point-max))
2015 (lexical-let ((working-revision working-revision) 2015 (lexical-let ((working-revision working-revision)
2016 (limit limit)) 2016 (limit limit))
2017 (widget-create 'push-button 2017 (insert "\n")
2018 :notify (lambda (&rest ignore) 2018 (insert-text-button "Show 2X entries"
2019 (vc-print-log-internal 2019 'action (lambda (&rest ignore)
2020 log-view-vc-backend log-view-vc-fileset 2020 (vc-print-log-internal
2021 working-revision nil (* 2 limit))) 2021 log-view-vc-backend log-view-vc-fileset
2022 :help-echo "Show the log again, and double the number of log entries shown" 2022 working-revision nil (* 2 limit)))
2023 "Show 2X entries") 2023 'help-echo "Show the log again, and double the number of log entries shown")
2024 (widget-insert " ") 2024 (insert " ")
2025 (widget-create 'push-button 2025 (insert-text-button "Show unlimited entries"
2026 :notify (lambda (&rest ignore) 2026 'action (lambda (&rest ignore)
2027 (vc-print-log-internal 2027 (vc-print-log-internal
2028 log-view-vc-backend log-view-vc-fileset 2028 log-view-vc-backend log-view-vc-fileset
2029 working-revision nil nil)) 2029 working-revision nil nil))
2030 :help-echo "Show the log again, showing all entries" 2030 'help-echo "Show the log again, including all entries"))))
2031 "Show unlimited entries"))
2032 (widget-setup)))
2033 2031
2034(defun vc-print-log-internal (backend files working-revision 2032(defun vc-print-log-internal (backend files working-revision
2035 &optional is-start-revision limit) 2033 &optional is-start-revision limit)