diff options
| author | Paul Eggert | 2011-02-14 21:41:07 -0800 |
|---|---|---|
| committer | Paul Eggert | 2011-02-14 21:41:07 -0800 |
| commit | fae95934b8edae3f538063e756ac799ed94313b2 (patch) | |
| tree | 3bb814c43cd50db54591bf685e5cb72b863b5833 /lisp | |
| parent | 6d302144c218f12bd380344ae2d3ed87a6ea9327 (diff) | |
| parent | bb55f713d2e4ea089a861a257d7d000432642ce9 (diff) | |
| download | emacs-fae95934b8edae3f538063e756ac799ed94313b2.tar.gz emacs-fae95934b8edae3f538063e756ac799ed94313b2.zip | |
Merge from mainline.
Diffstat (limited to 'lisp')
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 @@ | |||
| 1 | 2011-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 | |||
| 22 | 2011-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 | |||
| 32 | 2011-02-14 Juanma Barranquero <lekktu@gmail.com> | ||
| 33 | |||
| 34 | * makefile.w32-in (TRAMP_SRC): Remove tramp-imap.el. | ||
| 35 | |||
| 36 | 2011-02-13 Teodor Zlatanov <tzz@lifelogs.com> | ||
| 37 | |||
| 38 | * net/imap.el: Bring it back. | ||
| 39 | |||
| 40 | 2011-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 | |||
| 49 | 2011-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 | |||
| 68 | 2011-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 | |||
| 74 | 2011-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 | |||
| 83 | 2011-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 | |||
| 91 | 2011-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 | |||
| 104 | 2011-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 | |||
| 1 | 2011-02-12 Chong Yidong <cyd@stupidchicken.com> | 109 | 2011-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 | ||
| 280 | 2011-02-09 Teodor Zlatanov <tzz@lifelogs.com> | ||
| 281 | |||
| 282 | * password-cache.el (password-cache-remove): Accept secrets that are | ||
| 283 | not strings. | ||
| 284 | |||
| 172 | 2011-02-09 Stefan Monnier <monnier@iro.umontreal.ca> | 285 | 2011-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 | ||
| 508 | 2011-01-31 Deniz Dogan <deniz.a.m.dogan@gmail.com> | 621 | 2011-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 | ||
| 827 | 2011-01-22 Giorgos Keramidas <keramida@ceid.upatras.gr> (tiny change) | 940 | 2011-01-22 Giorgos Keramidas <keramida@ceid.upatras.gr> (tiny change) |
| 828 | 941 | ||
| @@ -928,7 +1041,7 @@ | |||
| 928 | 1041 | ||
| 929 | 2011-01-20 Ken Manheimer <ken.manheimer@gmail.com> | 1042 | 2011-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 @@ | |||
| 3851 | 2010-10-31 Jan Djärv <jan.h.d@swipnet.se> | 3964 | 2010-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 | ||
| 3856 | 2010-10-31 Michael Albinus <michael.albinus@gmx.de> | 3969 | 2010-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 | ||
| 4158 | 2010-10-24 Wilson Snyder <wsnyder@wsnyder.org> | 4271 | 2010-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. |
| 335 | TRAMP_DIR = $(lisp)/net | 335 | TRAMP_DIR = $(lisp)/net |
| 336 | TRAMP_SRC = $(TRAMP_DIR)/tramp.el $(TRAMP_DIR)/tramp-cache.el \ | 336 | TRAMP_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 | ||
| 602 | See interactive function `dired-omit-mode' \(\\[dired-omit-mode]\) and | 557 | See interactive function `dired-omit-mode' \(\\[dired-omit-mode]\) and |
| 603 | variables `dired-omit-mode' and `dired-omit-files'.") | 558 | variables `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. |
| 901 | If this file found in current directory, then it will be inserted into dired | 859 | If this file found in current directory, then it will be inserted into dired |
| 902 | buffer and `hack-local-variables' will be run. See Info node | 860 | buffer 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. |
| 904 | See also `dired-enable-local-variables'.") | 862 | See 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. |
| 1434 | Uses `man.el' of \\[manual-entry] fame." | 1398 | Uses `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. |
| 1664 | Similarly for `dired-x-find-file-other-window' over `find-file-other-window'. | 1625 | Similarly for `dired-x-find-file-other-window' over `find-file-other-window'. |
| 1665 | If you change this variable after `dired-x.el' is loaded then do | 1626 | If you change this variable without using \\[customize] after `dired-x.el' |
| 1666 | \\[dired-x-bind-find-file].") | 1627 | is 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. | ||
| 1811 | Send report on `dired-x-file' version `dired-x-version', to | ||
| 1812 | `dired-x-maintainer' at address `dired-x-help-address' listing | ||
| 1813 | variables `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" "\ |
| 1082 | Combine LIST1 and LIST2 using a set-union operation. | 1082 | Combine LIST1 and LIST2 using a set-union operation. |
| 1083 | The result list contains all items that appear in either LIST1 or LIST2. | 1083 | The resulting list contains all items that appear in either LIST1 or LIST2. |
| 1084 | This is a non-destructive function; it makes a copy of the data if necessary | 1084 | This is a non-destructive function; it makes a copy of the data if necessary |
| 1085 | to avoid corrupting the original LIST1 and LIST2. | 1085 | to 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" "\ |
| 1092 | Combine LIST1 and LIST2 using a set-union operation. | 1092 | Combine LIST1 and LIST2 using a set-union operation. |
| 1093 | The result list contains all items that appear in either LIST1 or LIST2. | 1093 | The resulting list contains all items that appear in either LIST1 or LIST2. |
| 1094 | This is a destructive function; it reuses the storage of LIST1 and LIST2 | 1094 | This is a destructive function; it reuses the storage of LIST1 and LIST2 |
| 1095 | whenever possible. | 1095 | whenever 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" "\ |
| 1102 | Combine LIST1 and LIST2 using a set-intersection operation. | 1102 | Combine LIST1 and LIST2 using a set-intersection operation. |
| 1103 | The result list contains all items that appear in both LIST1 and LIST2. | 1103 | The resulting list contains all items that appear in both LIST1 and LIST2. |
| 1104 | This is a non-destructive function; it makes a copy of the data if necessary | 1104 | This is a non-destructive function; it makes a copy of the data if necessary |
| 1105 | to avoid corrupting the original LIST1 and LIST2. | 1105 | to 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" "\ |
| 1112 | Combine LIST1 and LIST2 using a set-intersection operation. | 1112 | Combine LIST1 and LIST2 using a set-intersection operation. |
| 1113 | The result list contains all items that appear in both LIST1 and LIST2. | 1113 | The resulting list contains all items that appear in both LIST1 and LIST2. |
| 1114 | This is a destructive function; it reuses the storage of LIST1 and LIST2 | 1114 | This is a destructive function; it reuses the storage of LIST1 and LIST2 |
| 1115 | whenever possible. | 1115 | whenever 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" "\ |
| 1122 | Combine LIST1 and LIST2 using a set-difference operation. | 1122 | Combine LIST1 and LIST2 using a set-difference operation. |
| 1123 | The result list contains all items that appear in LIST1 but not LIST2. | 1123 | The resulting list contains all items that appear in LIST1 but not LIST2. |
| 1124 | This is a non-destructive function; it makes a copy of the data if necessary | 1124 | This is a non-destructive function; it makes a copy of the data if necessary |
| 1125 | to avoid corrupting the original LIST1 and LIST2. | 1125 | to 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" "\ |
| 1132 | Combine LIST1 and LIST2 using a set-difference operation. | 1132 | Combine LIST1 and LIST2 using a set-difference operation. |
| 1133 | The result list contains all items that appear in LIST1 but not LIST2. | 1133 | The resulting list contains all items that appear in LIST1 but not LIST2. |
| 1134 | This is a destructive function; it reuses the storage of LIST1 and LIST2 | 1134 | This is a destructive function; it reuses the storage of LIST1 and LIST2 |
| 1135 | whenever possible. | 1135 | whenever 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" "\ |
| 1142 | Combine LIST1 and LIST2 using a set-exclusive-or operation. | 1142 | Combine LIST1 and LIST2 using a set-exclusive-or operation. |
| 1143 | The result list contains all items that appear in exactly one of LIST1, LIST2. | 1143 | The resulting list contains all items appearing in exactly one of LIST1, LIST2. |
| 1144 | This is a non-destructive function; it makes a copy of the data if necessary | 1144 | This is a non-destructive function; it makes a copy of the data if necessary |
| 1145 | to avoid corrupting the original LIST1 and LIST2. | 1145 | to 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" "\ |
| 1152 | Combine LIST1 and LIST2 using a set-exclusive-or operation. | 1152 | Combine LIST1 and LIST2 using a set-exclusive-or operation. |
| 1153 | The result list contains all items that appear in exactly one of LIST1, LIST2. | 1153 | The resulting list contains all items appearing in exactly one of LIST1, LIST2. |
| 1154 | This is a destructive function; it reuses the storage of LIST1 and LIST2 | 1154 | This is a destructive function; it reuses the storage of LIST1 and LIST2 |
| 1155 | whenever possible. | 1155 | whenever 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. |
| 773 | The result list contains all items that appear in either LIST1 or LIST2. | 773 | The resulting list contains all items that appear in either LIST1 or LIST2. |
| 774 | This is a non-destructive function; it makes a copy of the data if necessary | 774 | This is a non-destructive function; it makes a copy of the data if necessary |
| 775 | to avoid corrupting the original LIST1 and LIST2. | 775 | to 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. |
| 794 | The result list contains all items that appear in either LIST1 or LIST2. | 794 | The resulting list contains all items that appear in either LIST1 or LIST2. |
| 795 | This is a destructive function; it reuses the storage of LIST1 and LIST2 | 795 | This is a destructive function; it reuses the storage of LIST1 and LIST2 |
| 796 | whenever possible. | 796 | whenever 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. |
| 805 | The result list contains all items that appear in both LIST1 and LIST2. | 805 | The resulting list contains all items that appear in both LIST1 and LIST2. |
| 806 | This is a non-destructive function; it makes a copy of the data if necessary | 806 | This is a non-destructive function; it makes a copy of the data if necessary |
| 807 | to avoid corrupting the original LIST1 and LIST2. | 807 | to 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. |
| 828 | The result list contains all items that appear in both LIST1 and LIST2. | 828 | The resulting list contains all items that appear in both LIST1 and LIST2. |
| 829 | This is a destructive function; it reuses the storage of LIST1 and LIST2 | 829 | This is a destructive function; it reuses the storage of LIST1 and LIST2 |
| 830 | whenever possible. | 830 | whenever 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. |
| 838 | The result list contains all items that appear in LIST1 but not LIST2. | 838 | The resulting list contains all items that appear in LIST1 but not LIST2. |
| 839 | This is a non-destructive function; it makes a copy of the data if necessary | 839 | This is a non-destructive function; it makes a copy of the data if necessary |
| 840 | to avoid corrupting the original LIST1 and LIST2. | 840 | to 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. |
| 858 | The result list contains all items that appear in LIST1 but not LIST2. | 858 | The resulting list contains all items that appear in LIST1 but not LIST2. |
| 859 | This is a destructive function; it reuses the storage of LIST1 and LIST2 | 859 | This is a destructive function; it reuses the storage of LIST1 and LIST2 |
| 860 | whenever possible. | 860 | whenever 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. |
| 869 | The result list contains all items that appear in exactly one of LIST1, LIST2. | 869 | The resulting list contains all items appearing in exactly one of LIST1, LIST2. |
| 870 | This is a non-destructive function; it makes a copy of the data if necessary | 870 | This is a non-destructive function; it makes a copy of the data if necessary |
| 871 | to avoid corrupting the original LIST1 and LIST2. | 871 | to 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. |
| 882 | The result list contains all items that appear in exactly one of LIST1, LIST2. | 882 | The resulting list contains all items appearing in exactly one of LIST1, LIST2. |
| 883 | This is a destructive function; it reuses the storage of LIST1 and LIST2 | 883 | This is a destructive function; it reuses the storage of LIST1 and LIST2 |
| 884 | whenever possible. | 884 | whenever 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 | |||
| 1577 | is used. If nil or omitted, use the selected frame." | 1577 | is 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 @@ | |||
| 1 | 2011-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 | |||
| 7 | 2011-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 | |||
| 21 | 2011-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 | |||
| 26 | 2011-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 | |||
| 31 | 2011-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 | |||
| 36 | 2011-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 | |||
| 1 | 2011-02-10 Stefan Monnier <monnier@iro.umontreal.ca> | 41 | 2011-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 | ||
| 50 | 2011-02-10 Teodor Zlatanov <tzz@lifelogs.com> | ||
| 51 | |||
| 52 | * sieve-manage.el: Autoload `auth-source-search'. | ||
| 53 | (sieve-sasl-auth): Use it. | ||
| 54 | |||
| 55 | 2011-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 | |||
| 10 | 2011-02-09 Lars Ingebrigtsen <larsi@gnus.org> | 74 | 2011-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 | ||
| 84 | 2011-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 | |||
| 20 | 2011-02-08 Julien Danjou <julien@danjou.info> | 100 | 2011-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 | ||
| 107 | 2011-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 | |||
| 27 | 2011-02-07 Lars Ingebrigtsen <larsi@gnus.org> | 114 | 2011-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 | ||
| 125 | 2011-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 | |||
| 38 | 2011-02-06 Lars Ingebrigtsen <larsi@gnus.org> | 132 | 2011-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 | ||
| 139 | 2011-02-06 Teodor Zlatanov <tzz@lifelogs.com> | ||
| 140 | |||
| 141 | * auth-source.el (auth-source-secrets-search): Add examples. | ||
| 142 | |||
| 45 | 2011-02-06 Julien Danjou <julien@danjou.info> | 143 | 2011-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 | ||
| 148 | 2011-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 | |||
| 168 | 2011-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 | |||
| 50 | 2011-02-04 Lars Ingebrigtsen <larsi@gnus.org> | 191 | 2011-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. | ||
| 113 | Only 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 | ||
| 122 | The default will get login and password information from a .gpg | 159 | The default will get login and password information from |
| 123 | file, which you should set up with the EPA/EPG packages to be | 160 | \"~/.authinfo.gpg\", which you should set up with the EPA/EPG |
| 124 | encrypted. See the auth.info manual for details. | 161 | packages to be encrypted. If that file doesn't exist, it will |
| 162 | try the unencrypted version \"~/.authinfo\". | ||
| 163 | |||
| 164 | See the auth.info manual for details. | ||
| 125 | 165 | ||
| 126 | Each entry is the authentication type with optional properties. | 166 | Each entry is the authentication type with optional properties. |
| 127 | 167 | ||
| 128 | It's best to customize this with `M-x customize-variable' because the choices | 168 | It's best to customize this with `M-x customize-variable' because the choices |
| 129 | can get pretty complex." | 169 | can 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. |
| 164 | If the value is not a list, symmetric encryption will be used." | 215 | If 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) |
| 216 | If it is a Secret Service API, return the collection name, otherwise | 267 | ;; (auth-source-backend-parse "secrets:login") |
| 217 | the 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)) | |
| 230 | Common keys are :host, :protocol, and :user. A value of t in | 281 | (auth-source-backend-parse `(:source (:secrets ,(match-string 1 entry))))) |
| 231 | SPEC 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 |
| 232 | matched 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) | 333 | Using the plist ENTRY, get the :host, :protocol, and :user search |
| 283 | (string-equal | 334 | parameters. 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) | 356 | This function parses `auth-sources' for matches of the SPEC |
| 306 | (if (string-equal "password" m) | 357 | plist. It can optionally create or update an authentication |
| 307 | (or (apply | 358 | token if requested. A token is just a standard Emacs property |
| 308 | 'netrc-machine-user-or-password m search) | 359 | list with a :secret property that can be a function; all the |
| 309 | ;; When we do not find a password, we | 360 | other properties will always hold scalar values. |
| 310 | ;; return nil anyway. | 361 | |
| 311 | (throw 'no-password nil)) | 362 | Typically the :secret property, if present, contains a password. |
| 312 | (or (apply | 363 | |
| 313 | 'netrc-machine-user-or-password m search) | 364 | Common search keys are :max, :host, :protocol, and :user. In |
| 314 | user))) | 365 | addition, :create specifies how tokens will be or created. |
| 315 | (if (consp mode) mode (list mode))))) | 366 | Finally, :type can specify which backend types you want to check. |
| 316 | (if (consp mode) result (car result))))))) | 367 | |
| 317 | 368 | A string value is always matched literally. A symbol is matched | |
| 318 | (defun auth-source-create (mode entry &rest spec) | 369 | as its string value, literally. All the SPEC values can be |
| 319 | "Create interactively credentials according to SPEC in ENTRY. | 370 | single values (symbol or string) or lists thereof (in which case |
| 320 | Return structure as specified by MODE." | 371 | any 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)) | 375 | A new token will be created if no matching tokens were found. |
| 325 | (name (concat (if user (format "%s@" user)) | 376 | The new token will have only the keys the backend requires. For |
| 326 | host | 377 | the netrc backend, for instance, that's the user, host, and |
| 327 | (if prot (format ":%s" prot)))) | 378 | protocol keys. |
| 328 | result) | 379 | |
| 329 | (setq result | 380 | Here'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)))) | 388 | which 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)))) | 402 | The behavior is like :create t but if the list contains any |
| 352 | (t | 403 | parameter, that parameter will be required in the resulting |
| 353 | "unknownuser")))) | 404 | token. The value for that parameter will be obtained from the |
| 354 | (if (consp mode) mode (list mode)))) | 405 | search parameters or from user input. If any queries are needed, |
| 355 | ;; Allow the source to save the data. | 406 | the alist `auth-source-creation-defaults' will be checked for the |
| 356 | (cond | 407 | default prompt. |
| 357 | ((consp source) | 408 | |
| 358 | ;; Secret Service API -- not implemented. | 409 | Here'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 | 417 | which 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" | 431 | When multiple values are specified in the search parameter, the |
| 381 | host | 432 | first one is used for creation. So :host (X Y Z) would create a |
| 382 | (or user (cdr (assoc "login" result))) | 433 | token for host X, for instance. |
| 383 | (cdr (assoc "password" result)) | 434 | |
| 384 | prot)) | 435 | This creation can fail if the search was not specific enough to |
| 385 | (write-region (point-min) (point-max) source nil 'silent))))) | 436 | create a new token (it's up to the backend to decide that). You |
| 386 | (if (consp mode) | 437 | should `catch' the backend-specific error as usual. Some |
| 387 | (mapcar #'cdr result) | 438 | backends (netrc, at least) will prompt the user rather than throw |
| 388 | (cdar result)))) | 439 | an 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." | 442 | Use `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) | 449 | When 0 the function will return just t or nil to indicate if any |
| 399 | (let ((coll (auth-get-source entry))) | 450 | matches were found. More than N items may be returned, depending |
| 400 | ;; Loop over candidates with a matching host attribute. | 451 | on 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 | 454 | the 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 | 457 | the 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. | 460 | Defaults 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) | 463 | V3 (note the match rules above). |
| 413 | "Remove cached authentication token." | 464 | |
| 414 | (interactive "slogin/password: \nsHost: \nsProtocol: \n") ;for testing | 465 | The return value is a list with at most :max tokens. Each token |
| 415 | (remhash | 466 | is a plist with keys :backend :host :protocol :user, plus any other |
| 416 | (if username | 467 | keys provided by the backend (notably :secret). But note the |
| 417 | (format "%s %s:%s %s" mode host protocol username) | 468 | exception for :max 0, which see above. |
| 418 | (format "%s %s:%s" mode host protocol)) | 469 | |
| 419 | auth-source-cache)) | 470 | The token's :secret key can hold a function. In that case you |
| 471 | must 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. | ||
| 550 | Calls `auth-source-search' with the :delete property in SPEC set to t. | ||
| 551 | The backend may not actually delete the entries. | ||
| 552 | |||
| 553 | Returns 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 | |||
| 590 | This is the same SPEC you passed to `auth-source-search'. | ||
| 591 | Returns 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 | |||
| 605 | This is not a full `auth-source-search' spec but works similarly. | ||
| 606 | For instance, \(:host \"myhost\" \"yourhost\") would find all the | ||
| 607 | cached data that was found with a search for those two hosts, | ||
| 608 | while \(: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. | ||
| 642 | Note 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. | ||
| 781 | See `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 | |||
| 951 | The :label key specifies the item's label. It is the only key | ||
| 952 | that can specify a substring. Any :label value besides a string | ||
| 953 | will allow any label. | ||
| 954 | |||
| 955 | All other search keys must match exactly. If you need substring | ||
| 956 | matching, do a wider search and narrow it down yourself. | ||
| 957 | |||
| 958 | You'll get back all the properties of the token as a plist. | ||
| 959 | |||
| 960 | Here's an example that looks for the first item in the 'login' | ||
| 961 | Secrets collection: | ||
| 962 | |||
| 963 | \(let ((auth-sources '(\"secrets:login\"))) | ||
| 964 | (auth-source-search :max 1) | ||
| 965 | |||
| 966 | Here's another that looks for the first item in the 'login' | ||
| 967 | Secrets collection whose label contains 'gnus': | ||
| 968 | |||
| 969 | \(let ((auth-sources '(\"secrets:login\"))) | ||
| 970 | (auth-source-search :max 1 :label \"gnus\") | ||
| 971 | |||
| 972 | And this one looks for the first item in the 'login' Secrets | ||
| 973 | collection that's a Google Chrome entry for the git.gnus.org site | ||
| 974 | login: | ||
| 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 | ||
| 1058 | DEPRECATED in favor of `auth-source-search'! | ||
| 1059 | |||
| 437 | USERNAME is optional and will be used as \"login\" in a search | 1060 | USERNAME is optional and will be used as \"login\" in a search |
| 438 | across the Secret Service API (see secrets.el) if the resulting | 1061 | across the Secret Service API (see secrets.el) if the resulting |
| 439 | items don't have a username. This means that if you search for | 1062 | items 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 | ||
| 453 | MODE can be \"login\" or \"password\"." | 1076 | MODE 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. |
| 688 | See `gnus-summary-mode-line-format' for a closer description. | 688 | See `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) | |||
| 443 | TRAMP_SRC = $(lisp)/net/tramp.el $(lisp)/net/tramp-cache.el \ | 443 | TRAMP_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 | ||
| 3500 | 2003-05-08 Satyaki Das <satyakid@stanford.edu> | 3500 | 2003-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. | ||
| 71 | USER, PASSWORD and SSL are optional. | ||
| 72 | The 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. | ||
| 97 | Requires either `imap-hash-fetch' to be called beforehand | ||
| 98 | \(e.g. by `imap-hash-map'), or REFETCH to be t. | ||
| 99 | Returns a list of the headers (an alist, see `imap-hash-map') and | ||
| 100 | the body of the message as a string. | ||
| 101 | Also 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. | ||
| 114 | If KEY is given, removes it. | ||
| 115 | VALUE can be a list of the headers (an alist, see `imap-hash-map') | ||
| 116 | and the body of the message as a string. It can also be a uid, | ||
| 117 | in which case `imap-hash-get' will be called to get the value. | ||
| 118 | Also 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', | ||
| 136 | using `message-setup'. | ||
| 137 | Look 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. | ||
| 156 | Also see `imap-hash-test'. Requires `imap-hash-fetch' to have | ||
| 157 | been called and the imap-hash server buffer to be current, | ||
| 158 | so it's best to use it inside `imap-hash-map'. | ||
| 159 | The 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. | ||
| 168 | Also 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, | ||
| 188 | the headers (an alist, see `imap-hash-headers'), and the body | ||
| 189 | contents as a string. If HEADERS-ONLY is not nil, the body will be nil. | ||
| 190 | Returns results of evaluating, as would `mapcar'. | ||
| 191 | If MESSAGES are given, iterate only over those UIDs. | ||
| 192 | Also 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. | ||
| 222 | Also see `imap-hash-test'. It uses `imap-hash-map' so just use that | ||
| 223 | function 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. | ||
| 230 | When the test is t, any key will be a candidate. | ||
| 231 | When the test is a string, messages will be filtered on that string as a | ||
| 232 | regexp against the subject. | ||
| 233 | When the test is a function, messages will be filtered with it. | ||
| 234 | The 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. | ||
| 316 | Get 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. | ||
| 178 | Operations 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. | ||
| 206 | First arg specifies the OPERATION, second arg is a list of arguments to | ||
| 207 | pass 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. | ||
| 234 | OP must be `copy' or `rename' and indicates the operation to perform. | ||
| 235 | FILENAME specifies the file to copy or rename, NEWNAME is the name of | ||
| 236 | the new file (for copy) or the new name of the file (for rename). | ||
| 237 | OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists already. | ||
| 238 | KEEP-DATE means to make sure that NEWNAME has the same timestamp | ||
| 239 | as FILENAME. PRESERVE-UID-GID, when non-nil, instructs to keep | ||
| 240 | the uid and gid if both files are on the same host. | ||
| 241 | |||
| 242 | This function is invoked by `tramp-imap-handle-copy-file' and | ||
| 243 | `tramp-imap-handle-rename-file'. It is an error if OP is neither | ||
| 244 | of `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. | ||
| 337 | Result is a list of (LOCALNAME LINK COUNT UID GID ATIME MTIME CTIME | ||
| 338 | SIZE 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. | ||
| 581 | When 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 | ||
| 583 | forces the encoding of the buffer or file. SIZE, when available, indicates | ||
| 584 | the 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. | ||
| 637 | CONTEXT is the encryption/decryption EPG context. | ||
| 638 | HANDBACK is just carried through. | ||
| 639 | KEY-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. | ||
| 750 | With 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 | |||
| 111 | user again." | 111 | user 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 | |||
| 304 | until you use it in some other buffer which uses Compilation mode | 304 | until you use it in some other buffer which uses Compilation mode |
| 305 | or Compilation Minor mode. | 305 | or Compilation Minor mode. |
| 306 | 306 | ||
| 307 | See variables `compilation-parse-errors-function' and | 307 | To 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 | |||
| 417 | are two possible targets for applying the patch. | 417 | are two possible targets for applying the patch. |
| 418 | Both files seem to be plausible alternatives. | 418 | Both files seem to be plausible alternatives. |
| 419 | 419 | ||
| 420 | Please advice: | 420 | Please 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. | ||
| 188 | It is called by the command `log-view-toggle-entry-display' with | ||
| 189 | one arg, the revision tag (a string), and should return a string. | ||
| 190 | If 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)) | 313 | This is a list (BEG TAG), where BEG is a buffer position and TAG |
| 306 | (forward-line 1) | 314 | is a string. If POS is nil or omitted, it defaults to point. |
| 307 | (let ((pt (point))) | 315 | If 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))) | 317 | If optional arg MOVE is non-nil, move point to BEG if found. |
| 310 | (unless (re-search-forward log-view-file-re pt t) | 318 | Otherwise, 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. | ||
| 339 | if 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. | |||
| 318 | log entries." | 347 | log 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. | ||
| 415 | With ARG, do it that many times. Negative ARG means move forward | ||
| 416 | to the beginning of the ARGth following entry. | ||
| 417 | |||
| 418 | This is Log View mode's default `beginning-of-defun-function'. | ||
| 419 | It 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'. | ||
| 134 | This should be a list (FORMAT REGEXP KEYWORDS), where FORMAT is a | ||
| 135 | format string (which is passed to \"git log\" via the argument | ||
| 136 | \"--pretty=tformat:FORMAT\"), REGEXP is a regular expression | ||
| 137 | matching 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'. | ||
| 150 | This should be a list (TEMPLATE REGEXP KEYWORDS), where TEMPLATE | ||
| 151 | is the \"--template\" argument string to pass to Mercurial, | ||
| 152 | REGEXP is a regular expression matching the resulting Mercurial | ||
| 153 | output, and KEYWORDS is a list of `font-lock-keywords' for | ||
| 154 | highlighting 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) |