# HG changeset patch # User Miles Bader # Date 1184474866 0 # Node ID a66921565bcbc130bdb5d2efc4b4323385e21706 # Parent a37d5bf6cbb74df424e90ad7b4c62f8a01b5d7be# Parent e0cd45299f772887852fe669926670d99f495367 Merge from emacs--devo--0 Patches applied: * emacs--devo--0 (patch 806-813) - Merge from emacs--rel--22 - Update from CVS * emacs--rel--22 (patch 51-58) - Update from CVS - Merge from gnus--rel--5.10 * gnus--rel--5.10 (patch 233-236) - Merge from emacs--devo--0 - Update from CVS Revision: emacs@sv.gnu.org/emacs--unicode--0--patch-230 diff -r a37d5bf6cbb7 -r a66921565bcb etc/ChangeLog --- a/etc/ChangeLog Tue Jul 10 02:07:45 2007 +0000 +++ b/etc/ChangeLog Sun Jul 15 04:47:46 2007 +0000 @@ -1,3 +1,24 @@ +2007-07-15 Karl Fogel + + * NEWS: Revert revision 1.1509 (commitid xdXJjPiU1ZfI9Fps), which + documented bookmark keybinding changes that were later reverted. + +2007-07-14 Jan Dj,Ad(Brv + + * PROBLEMS: Mention gtk-engines-qt problem. + +2007-07-13 Karl Fogel + + * NEWS: Update for recent bookmark keybinding changes. + +2007-07-10 Michael Albinus + + * NEWS: Add Tramp and comint-mode changes. + +2007-07-08 Michael Albinus + + * NEWS: `file-remote-p' has a new optional parameter CONNECTED. + 2007-07-07 Michael Albinus * NEWS: New function `start-file-process'. diff -r a37d5bf6cbb7 -r a66921565bcb etc/NEWS --- a/etc/NEWS Tue Jul 10 02:07:45 2007 +0000 +++ b/etc/NEWS Sun Jul 15 04:47:46 2007 +0000 @@ -73,6 +73,9 @@ * Changes in Specialized Modes and Packages in Emacs 23.1 +** compilation-auto-jump-to-first-error tells `compile' to jump to +the first error encountered during compilations. + ** In the `copyright' package, you can specify your copyright holders's names. Only copyright lines with holders matching copyright-names-regexp will be considered for update. @@ -95,9 +98,49 @@ *** Command `bibtex-url' now allows multiple URLs per entry. ++++ +** Tramp + +*** New connection methods. +The new methods "plinkx", "plink2", "psftp", "sftp" and "fish" have +been introduced. There are also new so-called gateway methods +"tunnel" and "socks". + +*** Multihop syntax has been removed. +The pseudo-method "multi" has been removed. Instead of, multi hops +can be specified by the new variable `tramp-default-proxies-alist'. + +*** More default settings. +Default values can be set via the variables `tramp-default-user', +`tramp-default-user-alist' and `tramp-default-host'. + +*** Connection information is cached. +In order to reduce connection setup, information about used +connections are kept persistent in a file. The name of this file is +defined in the variable `tramp-persistency-file-name'. + +*** Control of remote processes. +Running processes on a remote host can be controlled by settings in +`tramp-remote-path' and `tramp-remote-process-environment'. + +*** Success of remote copy is checked. +When the variable `file-precious-flag' is set, the success of a remote +file copy is checked via the file's checksum. + +** comint-mode uses `start-file-process' now (see Lisp Changes). +If `default-directory' is a remote file name, subprocesses are started +on the corresponding remote system. + * Changes in Emacs 23.1 on non-free operating systems +--- +** IPv6 is supported on MS-Windows. +Emacs now supports IPv6 on Windows XP and later, and earlier versions +of Windows with third party IPv6 stacks installed. Previously IPv6 was +supported on other platforms, but not on Windows due to using the winsock +1.1 header file, even though Emacs was linking to the winsock 2 library. + * Incompatible Lisp Changes in Emacs 23.1 @@ -107,6 +150,16 @@ * Lisp Changes in Emacs 23.1 ++++ +** In `condition-case', a handler can specify "let the debugger run first". + +You do this by writing `debug' in the list of conditions to be handled, +like this: + + (condition-case nil + (foo bar) + ((debug error) nil)) + ** The `require-match' argument to `completing-read' accepts a new value `confirm-only'. @@ -126,6 +179,14 @@ but obeys file handlers. The file handler is chosen based on `default-directory'. ++++ +** `file-remote-p' has a new optional parameter CONNECTED. +With this paramter passed non-nil, it is checked whether a remote +connection has been established already. + +** The two new functions `looking-at-p' and `string-match-p' can do +the same matching as `looking-at' and `string-match' without changing +the match data. * New Packages for Lisp Programming in Emacs 23.1 diff -r a37d5bf6cbb7 -r a66921565bcb etc/NEWS.22 --- a/etc/NEWS.22 Tue Jul 10 02:07:45 2007 +0000 +++ b/etc/NEWS.22 Sun Jul 15 04:47:46 2007 +0000 @@ -46,6 +46,11 @@ than the window, the usual keys for moving the cursor cause the image to be scrolled horizontally or vertically instead. +** Scrollbars follow the system theme on Windows XP and later. +Windows XP introduced themed scrollbars, but applications have to take +special steps to use them. Emacs now has the appropriate resources linked +in to make it use the scrollbars from the system theme. + * New Modes and Packages in Emacs 22.2 ** The new package css-mode.el provides a major mode for editing CSS files. diff -r a37d5bf6cbb7 -r a66921565bcb etc/PROBLEMS --- a/etc/PROBLEMS Tue Jul 10 02:07:45 2007 +0000 +++ b/etc/PROBLEMS Sun Jul 15 04:47:46 2007 +0000 @@ -1164,6 +1164,10 @@ Emacs*Foreground Emacs*Background +It is also reported that a bug in the gtk-engines-qt engine can cause this if +Emacs is compiled with Gtk+. +The bug is fixed in version 0.7 or newer of gtk-engines-qt. + *** KDE: Emacs hangs on KDE when a large portion of text is killed. This is caused by a bug in the KDE applet `klipper' which periodically diff -r a37d5bf6cbb7 -r a66921565bcb etc/orgcard.tex --- a/etc/orgcard.tex Tue Jul 10 02:07:45 2007 +0000 +++ b/etc/orgcard.tex Sun Jul 15 04:47:46 2007 +0000 @@ -1,5 +1,5 @@ % Reference Card for Org Mode -\def\orgversionnumber{5.01} +\def\orgversionnumber{5.03} \def\versionyear{2007} % latest update \def\year{2007} % latest copyright year @@ -544,6 +544,24 @@ \key{create sparse tree with matching tags}{C-c \\} \key{globally (agenda) match tags at cursor}{C-c C-o} +\section{Properties and Column View} + +\key{special commands in property lines}{C-c C-c} +\key{next/previous allowed value}{S-left/right} +\key{turn on column view}{C-c C-x C-c} + +\key{quit column view}{q} +\key{next/previous allowed value}{S-left/right} +\key{next/previous allowed value}{n / p} +\key{edit value}{e} +\key{edit allowed values list}{a} +\key{show value}{v} +\key{make column wider/narrower}{> / <} +\key{move column left/right}{M-left/right} +\key{add new column}{M-S-right} +\key{Delete current column}{M-S-left} + + \section{Timestamps} \key{prompt for date and insert timestamp}{C-c .} @@ -566,6 +584,8 @@ %\key{... forward/backward one month}{M-S-LEFT/RIGT} \key{Toggle custom format display for dates/times}{C-c C-x C-t} +\newcolumn + {\bf Clocking time} \key{start clock on current item}{C-c C-x C-i} @@ -575,12 +595,6 @@ \key{remove displayed times}{C-c C-c} \key{insert/update table with clock report}{C-c C-x C-r} -\section{LaTeX and cdlatex-mode} - -\key{preview LaTeX fragment}{C-c C-x C-l} -\key{Expand abbreviation (cdlatex-mode)}{TAB} -\key{Insert/modify math symbol (cdlatex-mode)}{` / '} - \section{Agenda Views} \key{add/move current file to front of agenda}{C-c [} @@ -648,6 +662,7 @@ \key{change timestamp to today}{>} \key{insert new entry into diary}{i} +\newcolumn \key{start the clock on current item (clock-in)}{I} \key{stop the clock (clock-out)}{O} \key{cancel current clock}{X} @@ -656,7 +671,6 @@ \key{Open link in current line}{C-c C-o} -\newcolumn {\bf Calendar commands} \key{find agenda cursor date in calendar}{c} @@ -678,6 +692,12 @@ (setq org-agenda-include-diary t) \endexample +\section{LaTeX and cdlatex-mode} + +\key{preview LaTeX fragment}{C-c C-x C-l} +\key{Expand abbreviation (cdlatex-mode)}{TAB} +\key{Insert/modify math symbol (cdlatex-mode)}{` / '} + \section{Exporting and Publishing} Exporting creates files with extensions {\it .txt\/} and {\it .html\/} @@ -690,17 +710,17 @@ \key{insert template of export options}{C-c C-x t} \key{toggle fixed width for entry or region}{C-c :} -{\bf HTML formatting} +%{\bf HTML formatting} -\key{make words {\bf bold}}{*bold*} -\key{make words {\it italic}}{/italic/} -\key{make words \underbar{underlined}}{_underlined_} -\key{sub- and superscripts}{x\^{}3, J_dust} -\key{\TeX{}-like macros}{\\alpha, \\to} -\key{typeset lines in fixed width font}{start with :} -\key{tables are exported as HTML tables}{start with |} -\key{links become HTML links}{http:... etc} -\key{include html tags}{@...@} +%\key{make words {\bf bold}}{*bold*} +%\key{make words {\it italic}}{/italic/} +%\key{make words \underbar{underlined}}{_underlined_} +%\key{sub- and superscripts}{x\^{}3, J_dust} +%\key{\TeX{}-like macros}{\\alpha, \\to} +%\key{typeset lines in fixed width font}{start with :} +%\key{tables are exported as HTML tables}{start with |} +%\key{links become HTML links}{http:... etc} +%\key{include html tags}{@...@} %{\bf Export options} % diff -r a37d5bf6cbb7 -r a66921565bcb lisp/ChangeLog --- a/lisp/ChangeLog Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/ChangeLog Sun Jul 15 04:47:46 2007 +0000 @@ -1,3 +1,325 @@ +2007-07-15 Karl Fogel + + * bookmark.el: Revert revision 1.90 (commitid mWoPbju3pgNotDps), + thus restoring bookmark bindings to three slots under C-x r. See + http://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00705.html. + +2007-07-15 Jeff Miller (tiny change) + + * calendar/cal-bahai.el (calendar-goto-bahai-date): Add autoload + cookie. + +2007-07-15 Jason Rumney + + * w32-fns.el (set-default-process-coding-system): Use dos line ends + for input to cmdproxy on all versions of Windows. + Use dos line ends for input to plink. + + * comint.el (comint-simple-send): Concat newline before sending. + (comint-password-prompt-regexp): Recognize plink's passphrase prompt. + +2007-07-14 Stefan Monnier + + * emacs-lisp/autoload.el (generated-autoload-file): Autoload the + safe-local-variable setting. + +2007-07-14 David Kastrup + + * emacs-lisp/advice.el (defadvice): Doc fix. + +2007-07-14 Juanma Barranquero + + * subr.el (when, unless): Doc fix. + +2007-07-13 Dan Nicolaescu + + * replace.el (match): Use yellow1 instead of yellow. + + * progmodes/gdb-ui.el (breakpoint-enabled): Use red1 instead of + red. + + * pcvs-info.el (cvs-unknown): Likewise. + +2007-07-13 Eli Zaretskii + + * makefile.w32-in (install-lisp-SH, install-lisp-CMD): New targets. + (install): Use them to copy all *.el files before *.elc. + +2007-07-13 Drew Adams + + * bookmark.el (bookmark-jump-other-window): New function. + (bookmark-map): Bind it to "o". + + http://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00633.html + and its thread contains discussion about this change. + The original patch was slightly tweaked by Karl Fogel + before committing. + +2007-07-13 Karl Fogel + + * bookmark.el: Shorten some comments to fit within 80 lines. + +2007-07-13 Karl Fogel + + * bookmark.el: Don't define bookmark keys under the "C-xr" map; + instead, make "C-xp" a prefix for bookmark-map. Patch by Drew + Adams , mildly tweaked by me. See + http://lists.gnu.org/archive/html/emacs-devel/2007-07/msg00633.html. + +2007-07-13 Carsten Dominik + + * textmodes/org.el: Bug fixes. + (org-end-of-line): Move to end of line if in headline without tags. + +2007-07-13 Stefan Monnier + + * vc-hooks.el: Remove spurious * in docstrings. + (vc-handled-backends): Add BZR. + + * vc-hooks.el (vc-find-file-hook): Use with-demoted-errors. + +2007-07-12 Davis Herring + + * desktop.el (desktop-buffer-info, desktop-save): + Use `desktop-dirname' instead of `dirname'. + +2007-07-12 Paul Pogonyshev + + * progmodes/which-func.el (which-func-modes): Add `python-mode'. + + * progmodes/python.el (python-which-func-length-limit): New var. + (python-which-func): New function. + (python-current-defun): Add optional `length-limit' and try to fit + computed function name to that length. + (python-mode): Hook `python-which-func' up. + +2007-07-12 Sean O'Rourke (tiny change) + + * pcomplete.el (pcomplete-entries): Obey pcomplete-ignore-case. + + * comint.el (comint-dynamic-complete-as-filename): + Use read-file-name-completion-ignore-case. + +2007-07-12 Stefan Monnier + + * comint.el (comint-dynamic-list-filename-completions): + Use read-file-name-completion-ignore-case. + + * vc-cvs.el: Require CL. + (vc-cvs-revision-table, vc-cvs-revision-completion-table): + New functions to provide completion of revision names. + + * vc-cvs.el (vc-functions): Clear up the cache when reloading the file. + (vc-cvs-annotate-first-line-re): New const. + (vc-cvs-annotate-process-filter): New fun. + (vc-cvs-annotate-command): Use them and run the command asynchronously. + +2007-07-12 Paul Pogonyshev + + * emacs-lisp/eldoc.el (eldoc-last-data): Revise documentation. + (eldoc-print-current-symbol-info): Adjust for changed helper + function signatures. + (eldoc-get-fnsym-args-string): Add `args' argument. Use new + `eldoc-highlight-function-argument'. + (eldoc-highlight-function-argument): New function. + (eldoc-get-var-docstring): Format documentation with + `font-lock-variable-name-face'. + (eldoc-docstring-format-sym-doc): Add `face' argument and apply it + where suited. + (eldoc-fnsym-in-current-sexp): Return a list with argument index. + (eldoc-beginning-of-sexp): Return number of skipped sexps. + +2007-07-11 Michael Albinus + + * progmodes/compile.el (compilation-start): `start-process' must + still be redefined when calling `start-process-shell-command'. + + * progmodes/gud.el (gud-file-name): When `default-directory' is a + remote file name, prepend its remote part to the filename. + (gud-common-init): When `default-directory' is a remote file name, + make the filename relative to it. + Based on a patch by Nick Roberts . + +2007-07-11 Dan Nicolaescu + + * vc-hooks.el (vc-default-mode-line-string): Add a mouse face, + mouse binding and a tooltip. + +2007-07-11 Stefan Monnier + + * menu-bar.el (vc-menu-map): New defalias. + +2007-07-10 Richard Stallman + + * emacs-lisp/lisp-mode.el (eval-defun): + Explain special handling of `defface'. + +2007-07-10 Jim Meyering (tiny change) + + * emacs-lisp/copyright.el (copyright-current-gpl-version): Set to 3. + + * autoinsert.el (auto-insert-alist): s/2/3/ in the generated comment. + +2007-07-10 Stefan Monnier + + * emacs-lisp/cl.el: Load cl-loaddefs.el quietly. + + * vc-arch.el (vc-arch-complete): Remove. + (vc-arch-revision-completion-table): Use complete-with-action. + + * subr.el (condition-case-no-debug, with-demoted-errors): New macros. + (complete-with-action): New function. + (dynamic-completion-table): Use it. + +2007-07-10 Michael Albinus + + * comint.el (make-comint, make-comint-in-buffer) + (comint-exec-1): Replace `start-process' by `start-file-process'. + + * progmodes/compile.el (compilation-start): Revert redefining + `start-process'. + +2007-07-10 Stefan Monnier + + * emacs-lisp/autoload.el (autoload-generate-file-autoloads): Be careful + with EOLs when generating MD5 checksums. + + * follow.el: Don't change the global map from the follow-mode-map + defvar, but from the toplevel. Use easy-menu to unify the Emacs and + XEmacs code. + (turn-on-follow-mode, turn-off-follow-mode): Remove interactive spec + since `follow-mode' should be used instead for that. + + * emacs-lisp/easymenu.el (easy-menu-binding): New function. + (easy-menu-do-define): Use it. + (easy-menu-do-add-item): Inline into easy-menu-add-item and then remove. + + * progmodes/compile.el (compilation-auto-jump-to-first-error) + (compilation-auto-jump-to-next): New vars. + (compilation-auto-jump): New function. + (compilation-error-properties): Use them to jump to first error. + (compilation-start): Set the var if requested. + + * emacs-lisp/autoload.el (update-directory-autoloads): Remove + duplicates without also removing entries from other directories. + +2007-07-10 Carsten Dominik + + * textmodes/org.el (org-agenda-day-view, org-agenda-week-view): + Remember span as default. + (org-columns-edit-value): Rename from `org-column-edit'. + (org-columns-display-here-title): Rename from + `org-overlay-columns-title'. + (org-columns-remove-overlays): Rename from org-remove-column-overlays. + (org-columns-get-autowidth-alist): Rename from + `org-get-columns-autowidth-alist'. + (org-columns-display-here): Rename from `org-overlay-columns'. + (org-columns-new-overlay): Rename from `org-new-column-overlay'. + (org-columns-quit): Rename from `org-column-quit'. + (org-columns-show-value): Rename from `org-column-show-value'. + (org-columns-content, org-columns-widen) + (org-columns-next-allowed-value) + (org-columns-edit-allowed, org-columns-store-format) + (org-columns-uncompile-format, org-columns-redo) + (org-columns-edit-attributes, org-delete-property) + (org-set-property, org-columns-update) + (org-columns-compute, org-columns-eval) + (org-columns-not-in-agenda, org-columns-compute-all) + (org-property-next-allowed-value) + (org-columns-compile-format) + (org-fill-paragraph-experimental) + (org-string-to-number, org-property-action) + (org-columns-move-left, org-columns-new ) + (org-column-number-to-string) + (org-property-previous-allowed-value) + (org-at-property-p, org-columns-delete) + (org-columns-previous-allowed-value) + (org-columns-move-right, org-columns-narrow) + (org-property-get-allowed-values) + (org-verify-version, org-column-string-to-number) + (org-delete-property-globally): New functions. + (org-columns-current-fmt): Rename from `org-current-columns-fmt'. + (org-columns-overlays): Rename from `org-column-overlays'. + (org-columns-map): Rename from `org-column-map'. + (org-columns-current-maxwidths): Rename from + `org-current-columns-maxwidths'. + (org-columns-begin-marker, org-columns-current-fmt-compiled) + (org-previous-header-line-format) + (org-columns-inhibit-recalculation) + (org-columns-top-level-marker): New variables. + (org-columns-default-format): Rename from `org-default-columns-format'. + (org-property-re): New constant. + +2007-07-10 Guanpeng Xu + + * subr.el (looking-at-p, string-match-p): New functions. + +2007-07-09 Reiner Steib + + * textmodes/tex-mode.el (tex-fontify-script) + (tex-font-script-display): New variables to make display of + superscripts and subscripts customizable. + (tex-font-lock-suscript, tex-font-lock-match-suscript): Use them. + +2007-07-09 Richard Stallman + + * isearch.el (isearch-edit-string): Call to isearch-push-state + after the search. + +2007-07-09 Jan Dj,Ad(Brv + + * window.el (fit-window-to-buffer): Remove setting of window-min-height + to 1 as enlarge-window uses the value to resize/shrink windows other + than WINDOW if needed. + +2007-07-08 Katsumi Yamaoka + + * cus-start.el (file-coding-system-alist): Fix custom type. + +2007-07-08 Chong Yidong + + * longlines.el (longlines-wrap-region): Avoid marking buffer as + modified. + (longlines-auto-wrap, longlines-window-change-function): + Remove unnecessary calls to set-buffer-modified-p. + +2007-07-08 Katsumi Yamaoka + + * cus-start.el (file-coding-system-alist): Fix custom type. + +2007-07-08 Stefan Monnier + + * vc-cvs.el (vc-cvs-revert): Use vc-default-revert. + (vc-cvs-checkout): Remove last arg now unused; simplify. + +2007-07-08 Michael Albinus + + * files.el (file-remote-p): Introduce optional parameter CONNECTED. + + * net/tramp.el: + * net/tramp-ftp.el: + * net/tramp-smb.el: + * net/tramp-uu.el: + * net/trampver.el: Migrate to Tramp 2.1. + + * net/tramp-cache.el: + * net/tramp-fish.el: + * net/tramp-gw.el: New Tramp packages. + + * net/tramp-util.el: + * net/tramp-vc.el: Removed. + + * net/ange-ftp.el: Add ange-ftp property to 'start-file-process + (ange-ftp-file-remote-p): Handle optional parameter CONNECTED. + + * net/rcompile.el (remote-compile): Handle Tramp 2.1 arguments. + + * progmodes/compile.el (compilation-start): Redefine + `start-process' temporarily when `default-directory' is remote. + Remove case of synchronous compilation, this won't happen ever. + (compilation-setup): Make local variable `comint-file-name-prefix' + for remote compilation. + 2007-07-08 Martin Rudalics * novice.el (disabled-command-function): Fit window to buffer to @@ -16,20 +338,21 @@ (math-bignum-digit-power-of-two): Evaluate when compiled. * calc/calc-comb.el (math-small-factorial-table) - (math-init-random-base,math-prime-test): Remove unnecessary calls + (math-init-random-base, math-prime-test): Remove unnecessary calls to `math-read-number-simple'. - * calc/calc-ext.el (math-approx-pi,math-approx-sqrt-e) + * calc/calc-ext.el (math-approx-pi, math-approx-sqrt-e) (math-approx-gamma-const): Add docstrings. * calc/calc-forms.el (math-julian-date-beginning) - (math-julian-date-beginning-int) New constants. - (math-format-date-part,math-parse-standard-date,calcFunc-julian): + (math-julian-date-beginning-int): New constants. + (math-format-date-part, math-parse-standard-date, calcFunc-julian): Use the new constants. * calc/calc-funcs.el (math-gammap1-raw): Add docstring. - * calc/calc-math.el (math-approx-ln-10,math-approx-ln-2): Add docstrings. + * calc/calc-math.el (math-approx-ln-10, math-approx-ln-2): + Add docstrings. 2007-07-07 Tom Tromey @@ -106,8 +429,8 @@ * calc/calc-bin.el (math-bignum-logb-digit-size) (math-bignum-digit-power-of-two): New constants. - (math-and-bignum,math-or-bignum,math-xor-bignum,math-diff-bignum) - (math-not-bignum,math-clip-bignum): Use the constants + (math-and-bignum, math-or-bignum, math-xor-bignum, math-diff-bignum) + (math-not-bignum, math-clip-bignum): Use the constants `math-bignum-digit-power-of-two' and `math-bignum-logb-digit-size' instead of their values. (math-clip): Use math-small-integer-size instead of its value. @@ -186,6 +509,11 @@ * calculator.el (calculator-expt): Use more cases to determine the value. +2007-07-03 Dan Nicolaescu + + * progmodes/gud.el (auto-mode-alist): Match more valid gdb init + file names. + 2007-07-03 Jay Belanger * calculator.el (calculator-expt, calculator-integer-p): @@ -261,7 +589,7 @@ 2007-07-02 Martin Rudalics - * help-mode.el (help-make-xrefs): Skip spaces too when + * help-mode.el (help-make-xrefs): Skip spaces too when skipping tabs. * ffap.el (dired-at-point-prompter): Improve prompt in @@ -269,6 +597,10 @@ 2007-07-01 Richard Stallman + * files.el (find-file-visit-truename): Fix safe-local-variable value. + +2007-07-01 Richard Stallman + * cus-start.el (max-mini-window-height): Added. 2007-07-01 Sean O'Rourke (tiny change) @@ -918,7 +1250,7 @@ post-command-hook. (rcirc-window-configuration-change-1): Update mode-line and overlay arrows here. - (rcirc-authenticate): Fix chanserv identification. + (rcirc-authenticate): Fixc hanserv identification. (rcirc-default-server): Remove variable. (rcirc): Connect according to rcirc-connections. (rcirc-connections): Add variable. @@ -1536,7 +1868,7 @@ * files.el (auto-mode-alist): Open `.asd' files in lisp-mode. -2007-05-22 Katsumi Yamaoka (tiny change) +2007-05-22 Katsumi Yamaoka * mail/mail-extr.el (mail-extract-address-components): Recognize non-ASCII characters except for NBSP as words. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/ChangeLog.10 --- a/lisp/ChangeLog.10 Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/ChangeLog.10 Sun Jul 15 04:47:46 2007 +0000 @@ -1340,7 +1340,7 @@ (shell-directory-tracker): Make regexp used for skipping to next command correspond to one used for command itself. -2003-06-13 Katsumi Yamaoka (tiny change) +2003-06-13 Katsumi Yamaoka * textmodes/texinfmt.el (texinfo-format-scan): Silence `whitespace-cleanup'. @@ -11805,7 +11805,7 @@ * vc-hooks.el (vc-kill-buffer-hook): Add it to kill-buffer-hook again. -2002-08-22 Katsumi Yamaoka (tiny change) +2002-08-22 Katsumi Yamaoka * frame.el (select-frame-by-name, select-frame-set-input-focus): Always call x-focus-frame, if using x. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/ChangeLog.11 --- a/lisp/ChangeLog.11 Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/ChangeLog.11 Sun Jul 15 04:47:46 2007 +0000 @@ -5295,7 +5295,7 @@ (reb-lisp-syntax-p, reb-change-syntax): `rx' is a Lisp syntax. (reb-cook-regexp): Call `rx-to-string' when `re-reb-syntax' is `rx'. -2004-08-05 Katsumi Yamaoka (tiny change) +2004-08-05 Katsumi Yamaoka * mail/mail-extr.el (mail-extr-disable-voodoo): New variable. (mail-extr-voodoo): Check mail-extr-disable-voodoo. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/ChangeLog.12 --- a/lisp/ChangeLog.12 Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/ChangeLog.12 Sun Jul 15 04:47:46 2007 +0000 @@ -1092,8 +1092,8 @@ North American rule. Replace "daylight savings" with "daylight saving" in doc. - * calendar/cal-china.el,cal-dst.el,calendar.el,diary-lib.el: - * calendar/lunar.el,solar.el: Replace "daylight savings" with + * calendar/cal-china.el, cal-dst.el, calendar.el, diary-lib.el: + * calendar/lunar.el, solar.el: Replace "daylight savings" with "daylight saving" in text. * woman.el (woman-change-fonts): Tweak previous change by using @@ -8709,7 +8709,7 @@ * term.el (term-handle-scroll, term-delete-lines) (term-insert-lines): Fix off by one errors. -2006-06-15 Katsumi Yamaoka (tiny change) +2006-06-15 Katsumi Yamaoka * net/tramp.el (tramp-touch): Use UTC to express time. @@ -22969,7 +22969,7 @@ * menu-bar.el (menu-bar-showhide-menu): Add `showhide-battery'. -2005-08-09 Katsumi Yamaoka (tiny change) +2005-08-09 Katsumi Yamaoka * net/ange-ftp.el (ange-ftp-send-cmd): Make it work properly with uploading files. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/autoinsert.el --- a/lisp/autoinsert.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/autoinsert.el Sun Jul 15 04:47:46 2007 +0000 @@ -188,7 +188,7 @@ \;; This file is free software; you can redistribute it and/or modify \;; it under the terms of the GNU General Public License as published by -\;; the Free Software Foundation; either version 2, or (at your option) +\;; the Free Software Foundation; either version 3, or (at your option) \;; any later version. \;; This file is distributed in the hope that it will be useful, diff -r a37d5bf6cbb7 -r a66921565bcb lisp/bookmark.el --- a/lisp/bookmark.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/bookmark.el Sun Jul 15 04:47:46 2007 +0000 @@ -240,12 +240,13 @@ ;; Read the help on all of these functions for details... ;;;###autoload (define-key bookmark-map "x" 'bookmark-set) -;;;###autoload (define-key bookmark-map "m" 'bookmark-set) ; "m" for "mark" +;;;###autoload (define-key bookmark-map "m" 'bookmark-set) ;"m"ark ;;;###autoload (define-key bookmark-map "j" 'bookmark-jump) -;;;###autoload (define-key bookmark-map "g" 'bookmark-jump) ; "g" for "go" +;;;###autoload (define-key bookmark-map "g" 'bookmark-jump) ;"g"o +;;;###autoload (define-key bookmark-map "o" 'bookmark-jump-other-window) ;;;###autoload (define-key bookmark-map "i" 'bookmark-insert) ;;;###autoload (define-key bookmark-map "e" 'edit-bookmarks) -;;;###autoload (define-key bookmark-map "f" 'bookmark-insert-location) ; "f" for "find" +;;;###autoload (define-key bookmark-map "f" 'bookmark-insert-location) ;"f"ind ;;;###autoload (define-key bookmark-map "r" 'bookmark-rename) ;;;###autoload (define-key bookmark-map "d" 'bookmark-delete) ;;;###autoload (define-key bookmark-map "l" 'bookmark-load) @@ -1083,6 +1084,27 @@ (bookmark-show-annotation bookmark))))) +;;;###autoload +(defun bookmark-jump-other-window (bookmark) + "Jump to BOOKMARK (a point in some file) in another window. +See `bookmark-jump'." + (interactive + (let ((bkm (bookmark-completing-read "Jump to bookmark (in another window)" + bookmark-current-bookmark))) + (if (> emacs-major-version 21) + (list bkm) bkm))) + (when bookmark + (bookmark-maybe-historicize-string bookmark) + (let ((cell (bookmark-jump-noselect bookmark))) + (and cell + (switch-to-buffer-other-window (car cell)) + (goto-char (cdr cell)) + (if bookmark-automatically-show-annotations + ;; if there is an annotation for this bookmark, + ;; show it in a buffer. + (bookmark-show-annotation bookmark)))))) + + (defun bookmark-file-or-variation-thereof (file) "Return FILE (a string) if it exists, or return a reasonable variation of FILE if that exists. Reasonable variations are checked diff -r a37d5bf6cbb7 -r a66921565bcb lisp/calendar/cal-bahai.el --- a/lisp/calendar/cal-bahai.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/calendar/cal-bahai.el Sun Jul 15 04:47:46 2007 +0000 @@ -149,6 +149,7 @@ (message "Baha'i date: %s" (calendar-bahai-date-string (calendar-cursor-to-date t)))) +;;;###autoload (defun calendar-goto-bahai-date (date &optional noecho) "Move cursor to Baha'i date DATE. Echo Baha'i date unless NOECHO is t." diff -r a37d5bf6cbb7 -r a66921565bcb lisp/comint.el --- a/lisp/comint.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/comint.el Sun Jul 15 04:47:46 2007 +0000 @@ -333,12 +333,13 @@ ;; kinit prints a prompt like `Password for devnull@GNU.ORG: '. ;; ksu prints a prompt like `Kerberos password for devnull/root@GNU.ORG: '. ;; ssh-add prints a prompt like `Enter passphrase: '. +;; plink prints a prompt like `Passphrase for key "root@GNU.ORG": '. ;; Some implementations of passwd use "Password (again)" as the 2nd prompt. (defcustom comint-password-prompt-regexp "\\(\\([Oo]ld \\|[Nn]ew \\|'s \\|login \\|\ Kerberos \\|CVS \\|UNIX \\| SMB \\|^\\)\ \[Pp]assword\\( (again)\\)?\\|\ -pass phrase\\|\\(Enter\\|Repeat\\|Bad\\) passphrase\\)\ +pass phrase\\|\\(Enter \\|Repeat \\|Bad \\)?[Pp]assphrase\\)\ \\(?:, try again\\)?\\(?: for [^:]+\\)?:\\s *\\'" "*Regexp matching prompts for passwords in the inferior process. This is used by `comint-watch-for-password-prompt'." @@ -670,13 +671,13 @@ "Make a Comint process NAME in BUFFER, running PROGRAM. If BUFFER is nil, it defaults to NAME surrounded by `*'s. PROGRAM should be either a string denoting an executable program to create -via `start-process', or a cons pair of the form (HOST . SERVICE) denoting a TCP -connection to be opened via `open-network-stream'. If there is already a -running process in that buffer, it is not restarted. Optional fourth arg +via `start-file-process', or a cons pair of the form (HOST . SERVICE) denoting +a TCP connection to be opened via `open-network-stream'. If there is already +a running process in that buffer, it is not restarted. Optional fourth arg STARTFILE is the name of a file to send the contents of to the process. If PROGRAM is a string, any more args are arguments to PROGRAM." - (or (fboundp 'start-process) + (or (fboundp 'start-file-process) (error "Multi-processing is not supported for this system")) (setq buffer (get-buffer-create (or buffer (concat "*" name "*")))) ;; If no process, or nuked process, crank up a new one and put buffer in @@ -693,9 +694,9 @@ "Make a Comint process NAME in a buffer, running PROGRAM. The name of the buffer is made by surrounding NAME with `*'s. PROGRAM should be either a string denoting an executable program to create -via `start-process', or a cons pair of the form (HOST . SERVICE) denoting a TCP -connection to be opened via `open-network-stream'. If there is already a -running process in that buffer, it is not restarted. Optional third arg +via `start-file-process', or a cons pair of the form (HOST . SERVICE) denoting +a TCP connection to be opened via `open-network-stream'. If there is already +a running process in that buffer, it is not restarted. Optional third arg STARTFILE is the name of a file to send the contents of the process to. If PROGRAM is a string, any more args are arguments to PROGRAM." @@ -781,17 +782,17 @@ ;; If the command has slashes, make sure we ;; first look relative to the current directory. (cons default-directory exec-path) exec-path))) - (setq proc (apply 'start-process name buffer command switches))) + (setq proc (apply 'start-file-process name buffer command switches))) (let ((coding-systems (process-coding-system proc))) (setq decoding (car coding-systems) encoding (cdr coding-systems))) - ;; If start-process decided to use some coding system for decoding + ;; If start-file-process decided to use some coding system for decoding ;; data sent from the process and the coding system doesn't ;; specify EOL conversion, we had better convert CRLF to LF. (if (vectorp (coding-system-eol-type decoding)) (setq decoding (coding-system-change-eol-conversion decoding 'dos) changed t)) - ;; Even if start-process left the coding system for encoding data + ;; Even if start-file-process left the coding system for encoding data ;; sent from the process undecided, we had better use the same one ;; as what we use for decoding. But, we should suppress EOL ;; conversion. @@ -1953,11 +1954,16 @@ "Default function for sending to PROC input STRING. This just sends STRING plus a newline. To override this, set the hook `comint-input-sender'." - (comint-send-string proc string) - (if comint-input-sender-no-newline - (if (not (string-equal string "")) - (process-send-eof)) - (comint-send-string proc "\n"))) + (let ((send-string + (if comint-input-sender-no-newline + string + ;; Sending as two separate strings does not work + ;; on Windows, so concat the \n before sending. + (concat string "\n")))) + (comint-send-string proc send-string)) + (if (and comint-input-sender-no-newline + (not (string-equal string ""))) + (process-send-eof))) (defun comint-line-beginning-position () "Return the buffer position of the beginning of the line, after any prompt. @@ -2805,7 +2811,7 @@ (defun comint-dynamic-complete-as-filename () "Dynamically complete at point as a filename. See `comint-dynamic-complete-filename'. Returns t if successful." - (let* ((completion-ignore-case (memq system-type '(ms-dos windows-nt cygwin))) + (let* ((completion-ignore-case read-file-name-completion-ignore-case) (completion-ignored-extensions comint-completion-fignore) ;; If we bind this, it breaks remote directory tracking in rlogin.el. ;; I think it was originally bound to solve file completion problems, @@ -2934,7 +2940,7 @@ (defun comint-dynamic-list-filename-completions () "List in help buffer possible completions of the filename at point." (interactive) - (let* ((completion-ignore-case (memq system-type '(ms-dos windows-nt cygwin))) + (let* ((completion-ignore-case read-file-name-completion-ignore-case) ;; If we bind this, it breaks remote directory tracking in rlogin.el. ;; I think it was originally bound to solve file completion problems, ;; but subsequent changes may have made this unnecessary. sm. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/cus-start.el --- a/lisp/cus-start.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/cus-start.el Sun Jul 15 04:47:46 2007 +0000 @@ -125,8 +125,11 @@ :value (undecided . undecided) (coding-system :tag "Decoding") (coding-system :tag "Encoding")) - (coding-system :tag "Single coding system" - :value undecided) + (coding-system + :tag "Single coding system" + :value undecided + :match (lambda (widget value) + (and value (not (functionp value))))) (function :value ignore)))) (selection-coding-system mule coding-system) ;; dired.c diff -r a37d5bf6cbb7 -r a66921565bcb lisp/desktop.el --- a/lisp/desktop.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/desktop.el Sun Jul 15 04:47:46 2007 +0000 @@ -652,7 +652,7 @@ (set-buffer buffer) (list ;; basic information - (desktop-file-name (buffer-file-name) dirname) + (desktop-file-name (buffer-file-name) desktop-dirname) (buffer-name) major-mode ;; minor modes @@ -673,7 +673,7 @@ buffer-read-only ;; auxiliary information (when (functionp desktop-save-buffer) - (funcall desktop-save-buffer dirname)) + (funcall desktop-save-buffer desktop-dirname)) ;; local variables (let ((locals desktop-locals-to-save) (loclist (buffer-local-variables)) @@ -897,7 +897,7 @@ (insert "\n " (desktop-value-to-string e))) (insert ")\n\n"))) - (setq default-directory dirname) + (setq default-directory desktop-dirname) (let ((coding-system-for-write 'emacs-mule)) (write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage)) ;; We remember when it was modified (which is presumably just now). diff -r a37d5bf6cbb7 -r a66921565bcb lisp/emacs-lisp/advice.el --- a/lisp/emacs-lisp/advice.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/emacs-lisp/advice.el Sun Jul 15 04:47:46 2007 +0000 @@ -3759,7 +3759,7 @@ \(defadvice FUNCTION (CLASS NAME [POSITION] [ARGLIST] FLAG...) [DOCSTRING] [INTERACTIVE-FORM] - BODY... ) + BODY...) FUNCTION ::= Name of the function to be advised. CLASS ::= `before' | `around' | `after' | `activation' | `deactivation'. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/emacs-lisp/autoload.el --- a/lisp/emacs-lisp/autoload.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/emacs-lisp/autoload.el Sun Jul 15 04:47:46 2007 +0000 @@ -41,6 +41,7 @@ A `.el' file can set this in its local variables section to make its autoloads go somewhere else. The autoload file is assumed to contain a trailer starting with a FormFeed character.") +;;;###autoload (put 'generated-autoload-file 'safe-local-variable 'stringp) ;; This feels like it should be a defconst, but MH-E sets it to @@ -432,7 +433,10 @@ ;; checksum in secondary autoload files where we do ;; not need the time-stamp optimization because it is ;; already provided by the primary autoloads file. - (md5 secondary-autoloads-file-buf nil nil 'emacs-mule) + (md5 secondary-autoloads-file-buf + ;; We'd really want to just use + ;; `emacs-internal' instead. + nil nil 'emacs-mule-unix) (nth 5 (file-attributes relfile)))) (insert ";;; Generated autoloads from " relfile "\n")) (insert generate-autoload-section-trailer)))) @@ -559,6 +563,7 @@ (directory-files (expand-file-name dir) t files-re)) dirs))) + (done ()) (this-time (current-time)) ;; Files with no autoload cookies or whose autoloads go to other ;; files because of file-local autoload-generated-file settings. @@ -592,10 +597,10 @@ (push file no-autoloads) (setq files (delete file files))))))) ((not (stringp file))) - ((not (and (file-exists-p file) - ;; Remove duplicates as well, just in case. - (member file files))) - ;; Remove the obsolete section. + ((or (not (file-exists-p file)) + ;; Remove duplicates as well, just in case. + (member file done)) + ;; Remove the obsolete section. (autoload-remove-section (match-beginning 0))) ((not (time-less-p (nth 4 form) (nth 5 (file-attributes file)))) @@ -606,6 +611,7 @@ (if (autoload-generate-file-autoloads file (current-buffer) buffer-file-name) (push file no-autoloads)))) + (push file done) (setq files (delete file files))))) ;; Elements remaining in FILES have no existing autoload sections yet. (dolist (file files) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/emacs-lisp/cl-loaddefs.el --- a/lisp/emacs-lisp/cl-loaddefs.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/emacs-lisp/cl-loaddefs.el Sun Jul 15 04:47:46 2007 +0000 @@ -10,8 +10,7 @@ ;;;;;; ceiling* floor* isqrt lcm gcd cl-progv-before cl-set-frame-visible-p ;;;;;; cl-map-overlays cl-map-intervals cl-map-keymap-recursively ;;;;;; notevery notany every some mapcon mapcan mapl maplist map -;;;;;; cl-mapcar-many equalp coerce) "cl-extra" "cl-extra.el" (18050 -;;;;;; 46455)) +;;;;;; cl-mapcar-many equalp coerce) "cl-extra" "cl-extra.el" "47c92504dda976a632c2c10bedd4b6a4") ;;; Generated autoloads from cl-extra.el (autoload (quote coerce) "cl-extra" "\ @@ -284,7 +283,7 @@ ;;;;;; do* do loop return-from return block etypecase typecase ecase ;;;;;; case load-time-value eval-when destructuring-bind function* ;;;;;; defmacro* defun* gentemp gensym cl-compile-time-init) "cl-macs" -;;;;;; "cl-macs.el" (18051 52572)) +;;;;;; "cl-macs.el" "7ccc827d272482ca276937ca18a7895a") ;;; Generated autoloads from cl-macs.el (autoload (quote cl-compile-time-init) "cl-macs" "\ @@ -746,7 +745,7 @@ ;;;;;; find nsubstitute-if-not nsubstitute-if nsubstitute substitute-if-not ;;;;;; substitute-if substitute delete-duplicates remove-duplicates ;;;;;; delete-if-not delete-if delete* remove-if-not remove-if remove* -;;;;;; replace fill reduce) "cl-seq" "cl-seq.el" (18050 45841)) +;;;;;; replace fill reduce) "cl-seq" "cl-seq.el" "8805f76626399794931f5db36ddf855f") ;;; Generated autoloads from cl-seq.el (autoload (quote reduce) "cl-seq" "\ diff -r a37d5bf6cbb7 -r a66921565bcb lisp/emacs-lisp/cl.el --- a/lisp/emacs-lisp/cl.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/emacs-lisp/cl.el Sun Jul 15 04:47:46 2007 +0000 @@ -628,7 +628,7 @@ (fmakunbound 'dolist) (fmakunbound 'dotimes) (fmakunbound 'declare) -(load "cl-loaddefs") +(load "cl-loaddefs" nil 'quiet) ;; This goes here so that cl-macs can find it if it loads right now. (provide 'cl-19) ; usage: (require 'cl-19 "cl") diff -r a37d5bf6cbb7 -r a66921565bcb lisp/emacs-lisp/copyright.el --- a/lisp/emacs-lisp/copyright.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/emacs-lisp/copyright.el Sun Jul 15 04:47:46 2007 +0000 @@ -77,7 +77,7 @@ ;; when modifying this, also modify the comment generated by autoinsert.el -(defconst copyright-current-gpl-version "2" +(defconst copyright-current-gpl-version "3" "String representing the current version of the GPL or nil.") (defvar copyright-update t) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/emacs-lisp/easymenu.el --- a/lisp/emacs-lisp/easymenu.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/emacs-lisp/easymenu.el Sun Jul 15 04:47:46 2007 +0000 @@ -152,6 +152,21 @@ ,(if symbol `(defvar ,symbol nil ,doc)) (easy-menu-do-define (quote ,symbol) ,maps ,doc ,menu))) +(defun easy-menu-binding (menu &optional item-name) + "Return a binding suitable to pass to `define-key'. +This is expected to be bound to a mouse event." + ;; Under Emacs this is almost trivial, whereas under XEmacs this may + ;; involve defining a function that calls popup-menu. + (let ((props (if (symbolp menu) + (prog1 (get menu 'menu-prop) + (setq menu (symbol-function menu)))))) + (cons 'menu-item + (cons (or item-name + (if (keymapp menu) + (keymap-prompt menu)) + "") + (cons menu props))))) + ;;;###autoload (defun easy-menu-do-define (symbol maps doc menu) ;; We can't do anything that might differ between Emacs dialects in @@ -173,15 +188,10 @@ 'identity) (symbol-function ,symbol))) ,symbol))))) - (mapcar (lambda (map) - (define-key map (vector 'menu-bar (easy-menu-intern (car menu))) - (cons 'menu-item - (cons (car menu) - (if (not (symbolp keymap)) - (list keymap) - (cons (symbol-function keymap) - (get keymap 'menu-prop))))))) - (if (keymapp maps) (list maps) maps)))) + (dolist (map (if (keymapp maps) (list maps) maps)) + (define-key map + (vector 'menu-bar (easy-menu-intern (car menu))) + (easy-menu-binding keymap (car menu)))))) (defun easy-menu-filter-return (menu &optional name) "Convert MENU to the right thing to return from a menu filter. @@ -249,10 +259,6 @@ (defvar easy-menu-button-prefix '((radio . :radio) (toggle . :toggle))) -(defun easy-menu-do-add-item (menu item &optional before) - (setq item (easy-menu-convert-item item)) - (easy-menu-define-key menu (easy-menu-intern (car item)) (cdr item) before)) - (defvar easy-menu-converted-items-table (make-hash-table :test 'equal)) (defun easy-menu-convert-item (item) @@ -269,7 +275,7 @@ (defun easy-menu-convert-item-1 (item) "Parse an item description and convert it to a menu keymap element. ITEM defines an item as in `easy-menu-define'." - (let (name command label prop remove help) + (let (name command label prop remove) (cond ((stringp item) ; An item or separator. (setq label item)) @@ -536,7 +542,8 @@ (setq item (symbol-value item)))) ;; Item is a keymap, find the prompt string and use as item name. (setq item (cons (keymap-prompt item) item))) - (easy-menu-do-add-item map item before))) + (setq item (easy-menu-convert-item item)) + (easy-menu-define-key map (easy-menu-intern (car item)) (cdr item) before))) (defun easy-menu-item-present-p (map path name) "In submenu of MAP with path PATH, return non-nil iff item NAME is present. @@ -615,7 +622,8 @@ (catch 'found (if (and map (symbolp map) (not (keymapp map))) (setq map (symbol-value map))) - (let ((maps (if map (list map) (current-active-maps)))) + (let ((maps (if map (if (keymapp map) (list map) map) + (current-active-maps)))) ;; Look for PATH in each map. (unless map (push 'menu-bar path)) (dolist (name path) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/emacs-lisp/eldoc.el --- a/lisp/emacs-lisp/eldoc.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/emacs-lisp/eldoc.el Sun Jul 15 04:47:46 2007 +0000 @@ -124,8 +124,8 @@ (defconst eldoc-last-data (make-vector 3 nil) "Bookkeeping; elements are as follows: 0 - contains the last symbol read from the buffer. - 1 - contains the string last displayed in the echo area for that - symbol, so it can be printed again if necessary without reconsing. + 1 - contains the string last displayed in the echo area for variables, + or argument string for functions. 2 - 'function if function args, 'variable if variable documentation.") (defvar eldoc-last-message nil) @@ -249,12 +249,16 @@ (let* ((current-symbol (eldoc-current-symbol)) (current-fnsym (eldoc-fnsym-in-current-sexp)) (doc (cond - ((eq current-symbol current-fnsym) - (or (eldoc-get-fnsym-args-string current-fnsym) + ((null current-fnsym) + nil) + ((eq current-symbol (car current-fnsym)) + (or (apply 'eldoc-get-fnsym-args-string + current-fnsym) (eldoc-get-var-docstring current-symbol))) (t (or (eldoc-get-var-docstring current-symbol) - (eldoc-get-fnsym-args-string current-fnsym)))))) + (apply 'eldoc-get-fnsym-args-string + current-fnsym)))))) (eldoc-message doc)))) ;; This is run from post-command-hook or some idle timer thing, ;; so we need to be careful that errors aren't ignored. @@ -263,24 +267,62 @@ ;; Return a string containing the function parameter list, or 1-line ;; docstring if function is a subr and no arglist is obtainable from the ;; docstring or elsewhere. -(defun eldoc-get-fnsym-args-string (sym) +(defun eldoc-get-fnsym-args-string (sym argument-index) (let ((args nil) (doc nil)) (cond ((not (and sym (symbolp sym) (fboundp sym)))) ((and (eq sym (aref eldoc-last-data 0)) (eq 'function (aref eldoc-last-data 2))) - (setq doc (aref eldoc-last-data 1))) + (setq args (aref eldoc-last-data 1))) ((setq doc (help-split-fundoc (documentation sym t) sym)) (setq args (car doc)) (string-match "\\`[^ )]* ?" args) - (setq args (concat "(" (substring args (match-end 0))))) + (setq args (concat "(" (substring args (match-end 0)))) + (eldoc-last-data-store sym args 'function)) (t (setq args (eldoc-function-argstring sym)))) - (cond (args - (setq doc (eldoc-docstring-format-sym-doc sym args)) - (eldoc-last-data-store sym doc 'function))) + (when args + (setq doc (eldoc-highlight-function-argument sym args argument-index))) doc)) +;; Highlight argument INDEX in ARGS list for SYM. +(defun eldoc-highlight-function-argument (sym args index) + (let ((start nil) + (end 0) + (argument-face 'bold)) + ;; Find the current argument in the argument string. We need to + ;; handle `&rest' and informal `...' properly. + ;; + ;; FIXME: What to do with optional arguments, like in + ;; (defun NAME ARGLIST [DOCSTRING] BODY...) case? + ;; The problem is there is no robust way to determine if + ;; the current argument is indeed a docstring. + (while (>= index 1) + (if (string-match "[^ ()]+" args end) + (progn + (setq start (match-beginning 0) + end (match-end 0)) + (let ((argument (match-string 0 args))) + (cond ((string= argument "&rest") + ;; All the rest arguments are the same. + (setq index 1)) + ((string= argument "&optional")) + ((string-match "\\.\\.\\.$" argument) + (setq index 0)) + (t + (setq index (1- index)))))) + (setq end (length args) + start (1- end) + argument-face 'font-lock-warning-face + index 0))) + (let ((doc args)) + (when start + (setq doc (copy-sequence args)) + (add-text-properties start end (list 'face argument-face) doc)) + (setq doc (eldoc-docstring-format-sym-doc + sym doc 'font-lock-function-name-face)) + doc))) + ;; Return a string containing a brief (one-line) documentation string for ;; the variable. (defun eldoc-get-var-docstring (sym) @@ -292,7 +334,8 @@ (let ((doc (documentation-property sym 'variable-documentation t))) (cond (doc (setq doc (eldoc-docstring-format-sym-doc - sym (eldoc-docstring-first-line doc))) + sym (eldoc-docstring-first-line doc) + 'font-lock-variable-name-face)) (eldoc-last-data-store sym doc 'variable))) doc))))) @@ -316,7 +359,7 @@ ;; If the entire line cannot fit in the echo area, the symbol name may be ;; truncated or eliminated entirely from the output to make room for the ;; description. -(defun eldoc-docstring-format-sym-doc (sym doc) +(defun eldoc-docstring-format-sym-doc (sym doc face) (save-match-data (let* ((name (symbol-name sym)) (ea-multi eldoc-echo-area-use-multiline-p) @@ -328,7 +371,7 @@ (cond ((or (<= strip 0) (eq ea-multi t) (and ea-multi (> (length doc) ea-width))) - (format "%s: %s" sym doc)) + (format "%s: %s" (propertize name 'face face) doc)) ((> (length doc) ea-width) (substring (format "%s" doc) 0 ea-width)) ((>= strip (length name)) @@ -338,27 +381,44 @@ ;; than the beginning, since the former is more likely ;; to be unique given package namespace conventions. (setq name (substring name strip)) - (format "%s: %s" name doc)))))) + (format "%s: %s" (propertize name 'face face) doc)))))) +;; Return a list of current function name and argument index. (defun eldoc-fnsym-in-current-sexp () - (let ((p (point))) - (eldoc-beginning-of-sexp) - (prog1 - ;; Don't do anything if current word is inside a string. - (if (= (or (char-after (1- (point))) 0) ?\") - nil - (eldoc-current-symbol)) - (goto-char p)))) + (save-excursion + (let ((argument-index (1- (eldoc-beginning-of-sexp)))) + ;; If we are at the beginning of function name, this will be -1. + (when (< argument-index 0) + (setq argument-index 0)) + ;; Don't do anything if current word is inside a string. + (if (= (or (char-after (1- (point))) 0) ?\") + nil + (list (eldoc-current-symbol) argument-index))))) +;; Move to the beginnig of current sexp. Return the number of nested +;; sexp the point was over or after. (defun eldoc-beginning-of-sexp () - (let ((parse-sexp-ignore-comments t)) + (let ((parse-sexp-ignore-comments t) + (num-skipped-sexps 0)) (condition-case err - (while (progn - (forward-sexp -1) - (or (= (char-before) ?\") - (> (point) (point-min))))) - (error nil)))) + (progn + ;; First account for the case the point is directly over a + ;; beginning of a nested sexp. + (condition-case err + (let ((p (point))) + (forward-sexp -1) + (forward-sexp 1) + (when (< (point) p) + (setq num-skipped-sexps 1))) + (error)) + (while + (let ((p (point))) + (forward-sexp -1) + (when (< (point) p) + (setq num-skipped-sexps (1+ num-skipped-sexps)))))) + (error)) + num-skipped-sexps)) ;; returns nil unless current word is an interned symbol. (defun eldoc-current-symbol () diff -r a37d5bf6cbb7 -r a66921565bcb lisp/emacs-lisp/lisp-mode.el --- a/lisp/emacs-lisp/lisp-mode.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/emacs-lisp/lisp-mode.el Sun Jul 15 04:47:46 2007 +0000 @@ -730,7 +730,9 @@ evaluating it this way resets the variable using its initial value expression even if the variable already has some other value. \(Normally `defvar' and `defcustom' do not alter the value if there -already is one.) +already is one.) In an analogous way, evaluating a `defface' +overrides any customizations of the face, so that it becomes +defined exactly as the `defface' expression says. If `eval-expression-debug-on-error' is non-nil, which is the default, this command arranges for all errors to enter the debugger. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/files.el --- a/lisp/files.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/files.el Sun Jul 15 04:47:46 2007 +0000 @@ -162,7 +162,7 @@ both at the file level and at the levels of the containing directories." :type 'boolean :group 'find-file) -(put 'find-file-visit-truename 'safe-local-variable 'boolean) +(put 'find-file-visit-truename 'safe-local-variable 'booleanp) (defcustom revert-without-query nil "Specify which files should be reverted without query. @@ -727,17 +727,23 @@ (cons load-path (get-load-suffixes))))) (load library)) -(defun file-remote-p (file) +(defun file-remote-p (file &optional connected) "Test whether FILE specifies a location on a remote system. Return an identification of the system if the location is indeed remote. The identification of the system may comprise a method to access the system and its hostname, amongst other things. For example, the filename \"/user@host:/foo\" specifies a location -on the system \"/user@host:\"." +on the system \"/user@host:\". + +If CONNECTED is non-nil, the function returns an identification only +if FILE is located on a remote system, and a connection is established +to that remote system. + +`file-remote-p' will never open a connection on its own." (let ((handler (find-file-name-handler file 'file-remote-p))) (if handler - (funcall handler 'file-remote-p file) + (funcall handler 'file-remote-p file connected) nil))) (defun file-local-copy (file) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/follow.el --- a/lisp/follow.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/follow.el Sun Jul 15 04:47:46 2007 +0000 @@ -336,123 +336,45 @@ ;; the look and feel of Follow mode.) (define-key mainmap [remap end-of-buffer] 'follow-end-of-buffer) - ;; - ;; The menu. - ;; - - (if (not (featurep 'xemacs)) - - ;; - ;; Emacs - ;; - (let ((menumap (funcall (symbol-function 'make-sparse-keymap) - "Follow")) - (count 0) - id) - (mapcar - (function - (lambda (item) - (setq id - (or (cdr item) - (progn - (setq count (+ count 1)) - (intern (format "separator-%d" count))))) - (define-key menumap (vector id) item) - (or (eq id 'follow-mode) - (put id 'menu-enable 'follow-mode)))) - ;; In reverse order: - '(("Toggle Follow mode" . follow-mode) - ("--") - ("Recenter" . follow-recenter) - ("--") - ("Previous Window" . follow-previous-window) - ("Next Windows" . follow-next-window) - ("Last Window" . follow-last-window) - ("First Window" . follow-first-window) - ("--") - ("Switch To Buffer (all windows)" - . follow-switch-to-buffer-all) - ("Switch To Buffer" . follow-switch-to-buffer) - ("--") - ("Delete Other Windows and Split" - . follow-delete-other-windows-and-split) - ("--") - ("Scroll Down" . follow-scroll-down) - ("Scroll Up" . follow-scroll-up))) - - ;; If there is a `tools' menu, we use it. However, we can't add a - ;; minor-mode specific item to it (it's broken), so we make the - ;; contents ghosted when not in use, and add ourselves to the - ;; global map. If no `tools' menu is present, just make a - ;; top-level menu visible when the mode is activated. - - (let ((tools-map (lookup-key (current-global-map) [menu-bar tools])) - (last nil)) - (if (sequencep tools-map) - (progn - ;; Find the last entry in the menu and store it in `last'. - (mapcar (function - (lambda (x) - (setq last (or (cdr-safe - (cdr-safe - (cdr-safe x))) - last)))) - tools-map) - (if last - (progn - (funcall (symbol-function 'define-key-after) - tools-map [separator-follow] '("--") last) - (funcall (symbol-function 'define-key-after) - tools-map [follow] (cons "Follow" menumap) - 'separator-follow)) - ;; Didn't find the last item, Adding to the top of - ;; tools. (This will probably never happend...) - (define-key (current-global-map) [menu-bar tools follow] - (cons "Follow" menumap)))) - ;; No tools menu, add "Follow" to the menubar. - (define-key mainmap [menu-bar follow] - (cons "Follow" menumap))))) - - ;; - ;; XEmacs. - ;; - - ;; place the menu in the `Tools' menu. - (let ((menu '("Follow" - :filter follow-menu-filter - ["Scroll Up" follow-scroll-up t] - ["Scroll Down" follow-scroll-down t] - ["Delete Other Windows and Split" - follow-delete-other-windows-and-split t] - ["Switch To Buffer" follow-switch-to-buffer t] - ["Switch To Buffer (all windows)" - follow-switch-to-buffer-all t] - ["First Window" follow-first-window t] - ["Last Window" follow-last-window t] - ["Next Windows" follow-next-window t] - ["Previous Window" follow-previous-window t] - ["Recenter" follow-recenter t] - ["Deactivate" follow-mode t]))) - - ;; Why not just `(set-buffer-menubar current-menubar)'? The - ;; question is a very good question. The reason is that under - ;; Emacs, neither `set-buffer-menubar' nor - ;; `current-menubar' is defined, hence the byte-compiler will - ;; warn. - (funcall (symbol-function 'set-buffer-menubar) - (symbol-value 'current-menubar)) - (funcall (symbol-function 'add-submenu) '("Tools") menu)) - - ;; When the mode is not activated, only one item is visible: - ;; "Activate". - (defun follow-menu-filter (menu) - (if follow-mode - menu - '(["Activate " follow-mode t])))) - mainmap) "Minor mode keymap for Follow mode.") +;; When the mode is not activated, only one item is visible to activate +;; the mode. +(defun follow-menu-filter (menu) + (if (bound-and-true-p 'follow-mode) + menu + '(["Follow mode " follow-mode + :style toggle :selected follow-mode]))) + +;; If there is a `tools' menu, we use it. However, we can't add a +;; minor-mode specific item to it (it's broken), so we make the +;; contents ghosted when not in use, and add ourselves to the +;; global map. +(easy-menu-add-item nil '("Tools") + '("Follow" + ;; The Emacs code used to just grey out operations when follow-mode was + ;; not enabled, whereas the XEmacs code used to remove it altogether. + ;; Not sure which is preferable, but clearly the preference should not + ;; depend on the flavor. + :filter follow-menu-filter + ["Scroll Up" follow-scroll-up follow-mode] + ["Scroll Down" follow-scroll-down follow-mode] + "--" + ["Delete Other Windows and Split" follow-delete-other-windows-and-split follow-mode] + "--" + ["Switch To Buffer" follow-switch-to-buffer follow-mode] + ["Switch To Buffer (all windows)" follow-switch-to-buffer-all follow-mode] + "--" + ["First Window" follow-first-window follow-mode] + ["Last Window" follow-last-window follow-mode] + ["Next Window" follow-next-window follow-mode] + ["Previous Window" follow-previous-window follow-mode] + "--" + ["Recenter" follow-recenter follow-mode] + "--" + ["Follow mode" follow-mode :style toggle :selected follow-mode])) + ;;}}} (defcustom follow-mode-line-text " Follow" @@ -553,14 +475,12 @@ ;;;###autoload (defun turn-on-follow-mode () "Turn on Follow mode. Please see the function `follow-mode'." - (interactive) (follow-mode 1)) ;;;###autoload (defun turn-off-follow-mode () "Turn off Follow mode. Please see the function `follow-mode'." - (interactive) (follow-mode -1)) (put 'follow-mode 'permanent-local t) @@ -2084,8 +2004,8 @@ (defun follow-window-size-change (frame) "Redraw all windows in FRAME, when in Follow mode." - ;; Below, we call `post-command-hook'. This makes sure that we - ;; doesn't start a mutally recursive endless loop. + ;; Below, we call `post-command-hook'. This makes sure that we + ;; don't start a mutually recursive endless loop. (if follow-inside-post-command-hook nil (let ((buffers '()) @@ -2109,12 +2029,12 @@ (setq windows (follow-all-followers win)) (if (memq orig-window windows) (progn - ;; Make sure we're redrawing around the - ;; selected window. - ;; - ;; We must be really careful not to do this - ;; when we are (indirectly) called by - ;; `post-command-hook'. + ;; Make sure we're redrawing around the + ;; selected window. + ;; + ;; We must be really careful not to do this + ;; when we are (indirectly) called by + ;; `post-command-hook'. (select-window orig-window) (follow-post-command-hook) (setq orig-window (selected-window))) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/gnus/ChangeLog --- a/lisp/gnus/ChangeLog Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/gnus/ChangeLog Sun Jul 15 04:47:46 2007 +0000 @@ -1,3 +1,42 @@ +2007-07-14 David Kastrup + + * gnus-art.el (gnus-mime-delete-part): Don't go through article-edit + finishing actions if we did not edit the article. + +2007-07-13 Katsumi Yamaoka + + * gnus-srvr.el (gnus-server-agent-face, gnus-server-opened-face) + (gnus-server-closed-face, gnus-server-denied-face) + (gnus-server-offline-face): Remove variable. + (gnus-server-font-lock-keywords): Use faces that are not aliases. + + * mm-util.el (mm-decode-coding-string, mm-encode-coding-string) + (mm-decode-coding-region, mm-encode-coding-region): Don't modify string + if the coding-system argument is nil for XEmacs. + + * nnrss.el (nnrss-compatible-encoding-alist): Inherit the value of + mm-charset-override-alist. + + * rfc2047.el: Don't require base64; require rfc2045 for the function + rfc2045-encode-string. + (rfc2047-encode-parameter): Use rfc2045-encode-string to quote or not + to quote the parameter value. + +2007-07-04 Katsumi Yamaoka + + * gnus-sum.el (gnus-summary-catchup): Don't recognize cached articles + as unfetched articles. + +2007-07-02 Reiner Steib + + * gnus-start.el (gnus-level-unsubscribed): Improve doc string. + +2007-06-26 Katsumi Yamaoka + + * gnus-art.el (gnus-article-summary-command-nosave) + (gnus-article-read-summary-keys): Don't set the 3rd arg of + pop-to-buffer for XEmacs. + 2007-06-14 Katsumi Yamaoka * gnus-agent.el (gnus-agent-fetch-headers) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/gnus/gnus-art.el --- a/lisp/gnus/gnus-art.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/gnus/gnus-art.el Sun Jul 15 04:47:46 2007 +0000 @@ -4408,11 +4408,11 @@ (gnus-summary-edit-article-done ,(or (mail-header-references gnus-current-headers) "") ,(gnus-group-read-only-p) - ,gnus-summary-buffer no-highlight))))) - ;; Not in `gnus-mime-save-part-and-strip': - (gnus-article-edit-done) - (gnus-summary-expand-window) - (gnus-summary-show-article)) + ,gnus-summary-buffer no-highlight)))) + ;; Not in `gnus-mime-save-part-and-strip': + (gnus-article-edit-done) + (gnus-summary-expand-window) + (gnus-summary-show-article))) (defun gnus-mime-save-part () "Save the MIME part under point." @@ -5607,7 +5607,7 @@ "Execute the last keystroke in the summary buffer." (interactive) (let (func) - (pop-to-buffer gnus-article-current-summary nil 'norecord) + (pop-to-buffer gnus-article-current-summary nil (not (featurep 'xemacs))) (setq func (lookup-key (current-local-map) (this-command-keys))) (call-interactively func))) @@ -5646,7 +5646,8 @@ (member keys nosave-in-article)) (let (func) (save-window-excursion - (pop-to-buffer gnus-article-current-summary nil 'norecord) + (pop-to-buffer gnus-article-current-summary + nil (not (featurep 'xemacs))) ;; We disable the pick minor mode commands. (let (gnus-pick-mode) (setq func (lookup-key (current-local-map) keys)))) @@ -5658,14 +5659,16 @@ (call-interactively func) (setq new-sum-point (point))) (when (member keys nosave-but-article) - (pop-to-buffer gnus-article-buffer nil 'norecord))) + (pop-to-buffer gnus-article-buffer + nil (not (featurep 'xemacs))))) ;; These commands should restore window configuration. (let ((obuf (current-buffer)) (owin (current-window-configuration)) (opoint (point)) win func in-buffer selected new-sum-start new-sum-hscroll) (cond (not-restore-window - (pop-to-buffer gnus-article-current-summary nil 'norecord)) + (pop-to-buffer gnus-article-current-summary + nil (not (featurep 'xemacs)))) ((setq win (get-buffer-window gnus-article-current-summary)) (select-window win)) (t diff -r a37d5bf6cbb7 -r a66921565bcb lisp/gnus/gnus-srvr.el --- a/lisp/gnus/gnus-srvr.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/gnus/gnus-srvr.el Sun Jul 15 04:47:46 2007 +0000 @@ -214,43 +214,12 @@ ;; backward-compatibility alias (put 'gnus-server-offline-face 'face-alias 'gnus-server-offline) -(defcustom gnus-server-agent-face 'gnus-server-agent - "Face name to use on AGENTIZED servers." - :version "22.1" - :group 'gnus-server-visual - :type 'face) - -(defcustom gnus-server-opened-face 'gnus-server-opened - "Face name to use on OPENED servers." - :version "22.1" - :group 'gnus-server-visual - :type 'face) - -(defcustom gnus-server-closed-face 'gnus-server-closed - "Face name to use on CLOSED servers." - :version "22.1" - :group 'gnus-server-visual - :type 'face) - -(defcustom gnus-server-denied-face 'gnus-server-denied - "Face name to use on DENIED servers." - :version "22.1" - :group 'gnus-server-visual - :type 'face) - -(defcustom gnus-server-offline-face 'gnus-server-offline - "Face name to use on OFFLINE servers." - :version "22.1" - :group 'gnus-server-visual - :type 'face) - (defvar gnus-server-font-lock-keywords - (list - '("(\\(agent\\))" 1 gnus-server-agent-face) - '("(\\(opened\\))" 1 gnus-server-opened-face) - '("(\\(closed\\))" 1 gnus-server-closed-face) - '("(\\(offline\\))" 1 gnus-server-offline-face) - '("(\\(denied\\))" 1 gnus-server-denied-face))) + '(("(\\(agent\\))" 1 gnus-server-agent) + ("(\\(opened\\))" 1 gnus-server-opened) + ("(\\(closed\\))" 1 gnus-server-closed) + ("(\\(offline\\))" 1 gnus-server-offline) + ("(\\(denied\\))" 1 gnus-server-denied))) (defun gnus-server-mode () "Major mode for listing and editing servers. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/gnus/gnus-start.el --- a/lisp/gnus/gnus-start.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/gnus/gnus-start.el Sun Jul 15 04:47:46 2007 +0000 @@ -178,8 +178,13 @@ (defconst gnus-level-unsubscribed 7 "Groups with levels less than or equal to this variable are unsubscribed. -Groups with levels less than `gnus-level-subscribed', which should be -less than this variable, are subscribed.") + +Groups with levels less than `gnus-level-subscribed', which +should be less than this variable, are subscribed. Groups with +levels from `gnus-level-subscribed' (exclusive) upto this +variable (inclusive) are unsubscribed. See also +`gnus-level-zombie', `gnus-level-killed' and the Info node `Group +Levels' for details.") (defconst gnus-level-zombie 8 "Groups with this level are zombie groups.") diff -r a37d5bf6cbb7 -r a66921565bcb lisp/gnus/gnus-sum.el --- a/lisp/gnus/gnus-sum.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/gnus/gnus-sum.el Sun Jul 15 04:47:46 2007 +0000 @@ -10514,7 +10514,8 @@ (gnus-sorted-nunion (gnus-sorted-intersection gnus-newsgroup-unreads gnus-newsgroup-downloadable) - gnus-newsgroup-unfetched))) + (gnus-sorted-difference gnus-newsgroup-unfetched + gnus-newsgroup-cached)))) ;; We actually mark all articles as canceled, which we ;; have to do when using auto-expiry or adaptive scoring. (gnus-summary-show-all-threads) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/gnus/mm-util.el --- a/lisp/gnus/mm-util.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/gnus/mm-util.el Sun Jul 15 04:47:46 2007 +0000 @@ -36,11 +36,7 @@ (if (fboundp (car elem)) (defalias nfunc (car elem)) (defalias nfunc (cdr elem))))) - '((decode-coding-string . (lambda (s a) s)) - (encode-coding-string . (lambda (s a) s)) - (encode-coding-region . ignore) - (coding-system-list . ignore) - (decode-coding-region . ignore) + '((coding-system-list . ignore) (char-int . identity) (coding-system-equal . equal) (annotationp . ignore) @@ -97,6 +93,34 @@ (multibyte-char-to-unibyte . identity)))) (eval-and-compile + (if (featurep 'xemacs) + (if (featurep 'file-coding) + ;; Don't modify string if CODING-SYSTEM is nil. + (progn + (defun mm-decode-coding-string (str coding-system) + (if coding-system + (decode-coding-string str coding-system) + str)) + (defun mm-encode-coding-string (str coding-system) + (if coding-system + (encode-coding-string str coding-system) + str)) + (defun mm-decode-coding-region (start end coding-system) + (if coding-system + (decode-coding-region start end coding-system))) + (defun mm-encode-coding-region (start end coding-system) + (if coding-system + (encode-coding-region start end coding-system)))) + (defun mm-decode-coding-string (str coding-system) str) + (defun mm-encode-coding-string (str coding-system) str) + (defalias 'mm-decode-coding-region 'ignore) + (defalias 'mm-encode-coding-region 'ignore)) + (defalias 'mm-decode-coding-string 'decode-coding-string) + (defalias 'mm-encode-coding-string 'encode-coding-string) + (defalias 'mm-decode-coding-region 'decode-coding-region) + (defalias 'mm-encode-coding-region 'encode-coding-region))) + +(eval-and-compile (cond ((fboundp 'replace-in-string) (defalias 'mm-replace-in-string 'replace-in-string)) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/gnus/nnrss.el --- a/lisp/gnus/nnrss.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/gnus/nnrss.el Sun Jul 15 04:47:46 2007 +0000 @@ -85,7 +85,12 @@ (defvar nnrss-file-coding-system mm-universal-coding-system "Coding system used when reading and writing files.") -(defvar nnrss-compatible-encoding-alist '((iso-8859-1 . windows-1252)) +(defvar nnrss-compatible-encoding-alist + (delq nil (mapcar (lambda (elem) + (if (and (mm-coding-system-p (car elem)) + (mm-coding-system-p (cdr elem))) + elem)) + mm-charset-override-alist)) "Alist of encodings and those supersets. The cdr of each element is used to decode data if it is available when the car is what the data specify as the encoding. Or, the car is used diff -r a37d5bf6cbb7 -r a66921565bcb lisp/gnus/rfc2047.el --- a/lisp/gnus/rfc2047.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/gnus/rfc2047.el Sun Jul 15 04:47:46 2007 +0000 @@ -55,7 +55,7 @@ (require 'ietf-drums) ;; Fixme: Avoid this (used for mail-parse-charset) mm dependence on gnus. (require 'mail-prsvr) -(require 'base64) +(require 'rfc2045) ;; rfc2045-encode-string (autoload 'mm-body-7-or-8 "mm-bodies") (eval-and-compile @@ -834,12 +834,9 @@ \(defalias 'mail-header-encode-parameter 'rfc2047-encode-parameter) " - (let* ((rfc2047-encoding-type 'mime) - (rfc2047-encode-max-chars nil) - (string (rfc2047-encode-string value))) - (if (string-match (concat "[" ietf-drums-tspecials "]") string) - (format "%s=%S" param string) - (concat param "=" string)))) + (let ((rfc2047-encoding-type 'mime) + (rfc2047-encode-max-chars nil)) + (rfc2045-encode-string param (rfc2047-encode-string value)))) ;;; ;;; Functions for decoding RFC2047 messages diff -r a37d5bf6cbb7 -r a66921565bcb lisp/isearch.el --- a/lisp/isearch.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/isearch.el Sun Jul 15 04:47:46 2007 +0000 @@ -1065,6 +1065,7 @@ ;; Reinvoke the pending search. (isearch-search) + (isearch-push-state) (isearch-update) (if isearch-nonincremental (progn diff -r a37d5bf6cbb7 -r a66921565bcb lisp/makefile.w32-in --- a/lisp/makefile.w32-in Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/makefile.w32-in Sun Jul 15 04:47:46 2007 +0000 @@ -411,7 +411,7 @@ - $(DEL) "$(INSTALL_DIR)/same-dir.tst" echo SameDirTest > "$(INSTALL_DIR)/same-dir.tst" #ifdef COPY_LISP_SOURCE - $(IFNOTSAMEDIR) $(CP_DIR) . "$(INSTALL_DIR)/lisp" $(ENDIF) + $(IFNOTSAMEDIR) $(MAKE) $(MFLAGS) install-lisp-$(SHELLTYPE) $(ENDIF) #else # $(IFNOTSAMEDIR) $(CP_DIR) *.elc "$(INSTALL_DIR)/lisp" $(ENDIF) # $(IFNOTSAMEDIR) $(CP) cus-load.el "$(INSTALL_DIR)/lisp" $(ENDIF) @@ -428,6 +428,19 @@ - $(DEL) ../same-dir.tst - $(DEL) "$(INSTALL_DIR)/same-dir.tst" +# Need to copy *.el files first, to avoid "source file is newer" annoyance +# since cp does not preserve time stamps +install-lisp-SH: + cp -f *.el "$(INSTALL_DIR)/lisp" + for dir in $(WINS); do mkdir "$(INSTALL_DIR)/lisp/$$dir" && cp -f $$dir/*.el "$(INSTALL_DIR)/lisp/$$dir"; done + for dir in . $(WINS); do cp $$dir/*.elc "$(INSTALL_DIR)/lisp/$$dir"; done + +install-lisp-CMD: + cp -f *.el "$(INSTALL_DIR)/lisp" + for %%f in ($(WINS)) do mkdir "$(INSTALL_DIR)/lisp/%%f" + for %%f in ($(WINS)) do cp -f %%f/*.el "$(INSTALL_DIR)/lisp/%%f" + for %%f in (. $(WINS)) do cp -f %%f/*.elc "$(INSTALL_DIR)/lisp/%%f" + # # Maintenance # diff -r a37d5bf6cbb7 -r a66921565bcb lisp/menu-bar.el --- a/lisp/menu-bar.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/menu-bar.el Sun Jul 15 04:47:46 2007 +0000 @@ -1161,6 +1161,7 @@ '("--")) (defvar vc-menu-map (make-sparse-keymap "Version Control")) +(defalias 'vc-menu-map vc-menu-map) (define-key menu-bar-tools-menu [pcl-cvs] '(menu-item "PCL-CVS" cvs-global-menu)) (define-key menu-bar-tools-menu [vc] diff -r a37d5bf6cbb7 -r a66921565bcb lisp/mh-e/ChangeLog --- a/lisp/mh-e/ChangeLog Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/mh-e/ChangeLog Sun Jul 15 04:47:46 2007 +0000 @@ -1,3 +1,8 @@ +2007-07-11 Bill Wohler + + * mh-compat.el (mh-display-color-cells): Fix on XEmacs 21.5b28. + Thanks to Henrique Martins for the help (closes SF #1749774). + 2007-06-06 Juanma Barranquero * mh-mime.el (mh-mh-directive-present-p): diff -r a37d5bf6cbb7 -r a66921565bcb lisp/mh-e/mh-compat.el --- a/lisp/mh-e/mh-compat.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/mh-e/mh-compat.el Sun Jul 15 04:47:46 2007 +0000 @@ -77,13 +77,17 @@ 'cancel-timer 'delete-itimer)) -(defun-mh mh-display-color-cells display-color-cells (&optional display) +(defun mh-display-color-cells (&optional display) "Return the number of color cells supported by DISPLAY. -This function is used by XEmacs to return 2 when -`device-color-cells' returns nil. This happens when compiling or +This function is used by XEmacs to return 2 when `device-color-cells' +or `display-color-cells' returns nil. This happens when compiling or running on a tty and causes errors since `display-color-cells' is expected to return an integer." - (or (device-color-cells display) 2)) + (cond ((fboundp 'display-color-cells) ; GNU Emacs, XEmacs 21.5b28 + (or (display-color-cells display) 2)) + ((fboundp 'device-color-cells) ; XEmacs 21.4 + (or (device-color-cells display) 2)) + (t 2))) (defmacro mh-display-completion-list (completions &optional common-substring) "Display the list of COMPLETIONS. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/ange-ftp.el --- a/lisp/net/ange-ftp.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/net/ange-ftp.el Sun Jul 15 04:47:46 2007 +0000 @@ -4132,8 +4132,15 @@ (format "Getting %s" fn1)) tmp1)))) -(defun ange-ftp-file-remote-p (file) - (ange-ftp-replace-name-component file "")) +(defun ange-ftp-file-remote-p (file &optional connected) + (and (or (not connected) + (let* ((parsed (ange-ftp-ftp-name file)) + (host (nth 0 parsed)) + (user (nth 1 parsed)) + (proc (get-process (ange-ftp-ftp-process-buffer host user)))) + (and proc (processp proc) + (memq (process-status proc) '(run open))))) + (ange-ftp-replace-name-component file ""))) (defun ange-ftp-load (file &optional noerror nomessage nosuffix) (if (ange-ftp-ftp-name file) @@ -4360,7 +4367,10 @@ ;; This returns nil for any file name as argument. (put 'vc-registered 'ange-ftp 'null) +;; We can handle process-file in a restricted way (just for chown). +;; Nothing possible for start-file-process. (put 'process-file 'ange-ftp 'ange-ftp-process-file) +(put 'start-file-process 'ange-ftp 'ignore) (put 'shell-command 'ange-ftp 'ange-ftp-shell-command) ;;; Define ways of getting at unmodified Emacs primitives, diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/rcompile.el --- a/lisp/net/rcompile.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/net/rcompile.el Sun Jul 15 04:47:46 2007 +0000 @@ -188,8 +188,7 @@ (when (featurep 'tramp) (set (make-local-variable 'comint-file-name-prefix) (funcall (symbol-function 'tramp-make-tramp-file-name) - nil ;; multi-method. To be removed with Tramp 2.1. - nil + nil ;; method. remote-compile-user remote-compile-host "")))))) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp-cache.el --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/net/tramp-cache.el Sun Jul 15 04:47:46 2007 +0000 @@ -0,0 +1,317 @@ +;;; -*- mode: Emacs-Lisp; coding: iso-2022-7bit; -*- +;;; tramp-cache.el --- file information caching for Tramp + +;; Copyright (C) 2000, 2005, 2006, 2007 by Free Software Foundation, Inc. + +;; Author: Daniel Pittman +;; Michael Albinus +;; Keywords: comm, processes + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, see +;; . + +;;; Commentary: + +;; An implementation of information caching for remote files. + +;; Each connection, identified by a vector [method user host +;; localname] or by a process, has a unique cache. We distinguish 3 +;; kind of caches, depending on the key: +;; +;; - localname is NIL. This are reusable properties. Examples: +;; "remote-shell" identifies the POSIX shell to be called on the +;; remote host, or "perl" is the command to be called on the remote +;; host, when starting a Perl script. These properties are saved in +;; the file `tramp-persistency-file-name'. +;; +;; - localname is a string. This are temporary properties, which are +;; related to the file localname is referring to. Examples: +;; "file-exists-p" is t or nile, depending on the file existence, or +;; "file-attributes" caches the result of the function +;; `file-attributes'. +;; +;; - The key is a process. This are temporary properties related to +;; an open connection. Examples: "scripts" keeps shell script +;; definitions already sent to the remote shell, "last-cmd-time" is +;; the time stamp a command has been sent to the remote process. + +;;; Code: + +;; Pacify byte-compiler. +(eval-when-compile + (require 'cl) + (autoload 'tramp-message "tramp") + (autoload 'tramp-tramp-file-p "tramp") + ;; We cannot autoload macro `with-parsed-tramp-file-name', it + ;; results in problems of byte-compiled code. + (autoload 'tramp-dissect-file-name "tramp") + (autoload 'tramp-file-name-method "tramp") + (autoload 'tramp-file-name-user "tramp") + (autoload 'tramp-file-name-host "tramp") + (autoload 'tramp-file-name-localname "tramp") + (autoload 'time-stamp-string "time-stamp")) + +;;; -- Cache -- + +(defvar tramp-cache-data (make-hash-table :test 'equal) + "Hash table for remote files properties.") + +(defcustom tramp-persistency-file-name + (cond + ;; GNU Emacs. + ((and (boundp 'user-emacs-directory) + (stringp (symbol-value 'user-emacs-directory)) + (file-directory-p (symbol-value 'user-emacs-directory))) + (expand-file-name "tramp" (symbol-value 'user-emacs-directory))) + ((and (not (featurep 'xemacs)) (file-directory-p "~/.emacs.d/")) + "~/.emacs.d/tramp") + ;; XEmacs. + ((and (boundp 'user-init-directory) + (stringp (symbol-value 'user-init-directory)) + (file-directory-p (symbol-value 'user-init-directory))) + (expand-file-name "tramp" (symbol-value 'user-init-directory))) + ((and (featurep 'xemacs) (file-directory-p "~/.xemacs/")) + "~/.xemacs/tramp") + ;; For users without `~/.emacs.d/' or `~/.xemacs/'. + (t "~/.tramp")) + "File which keeps connection history for Tramp connections." + :group 'tramp + :type 'file) + +(defun tramp-get-file-property (vec file property default) + "Get the PROPERTY of FILE from the cache context of VEC. +Returns DEFAULT if not set." + ;; Unify localname. + (setq vec (copy-sequence vec)) + (aset vec 3 (directory-file-name file)) + (let* ((hash (or (gethash vec tramp-cache-data) + (puthash vec (make-hash-table :test 'equal) + tramp-cache-data))) + (value (if (hash-table-p hash) + (gethash property hash default) + default))) + (tramp-message vec 8 "%s %s %s" file property value) + value)) + +(defun tramp-set-file-property (vec file property value) + "Set the PROPERTY of FILE to VALUE, in the cache context of VEC. +Returns VALUE." + ;; Unify localname. + (setq vec (copy-sequence vec)) + (aset vec 3 (directory-file-name file)) + (let ((hash (or (gethash vec tramp-cache-data) + (puthash vec (make-hash-table :test 'equal) + tramp-cache-data)))) + (puthash property value hash) + (tramp-message vec 8 "%s %s %s" file property value) + value)) + +(defun tramp-flush-file-property (vec file) + "Remove all properties of FILE in the cache context of VEC." + ;; Unify localname. + (setq vec (copy-sequence vec)) + (aset vec 3 (directory-file-name file)) + (tramp-message vec 8 "%s" file) + (remhash vec tramp-cache-data)) + +(defun tramp-flush-directory-property (vec directory) + "Remove all properties of DIRECTORY in the cache context of VEC. +Remove also properties of all files in subdirectories." + (let ((directory (directory-file-name directory))) + (tramp-message vec 8 "%s" directory) + (maphash + '(lambda (key value) + (when (and (stringp key) + (string-match directory (tramp-file-name-localname key))) + (remhash key tramp-cache-data))) + tramp-cache-data))) + +(defun tramp-cache-print (table) + "Prints hash table TABLE." + (when (hash-table-p table) + (let (result tmp) + (maphash + '(lambda (key value) + (setq tmp (format + "(%s %s)" + (if (processp key) + (prin1-to-string (prin1-to-string key)) + (prin1-to-string key)) + (if (hash-table-p value) + (tramp-cache-print value) + (if (bufferp value) + (prin1-to-string (prin1-to-string value)) + (prin1-to-string value)))) + result (if result (concat result " " tmp) tmp))) + table) + result))) + +;; Reverting or killing a buffer should also flush file properties. +;; They could have been changed outside Tramp. +(defun tramp-flush-file-function () + "Flush all Tramp cache properties from buffer-file-name." + (let ((bfn (buffer-file-name))) + (when (and (stringp bfn) (tramp-tramp-file-p bfn)) + (let* ((v (tramp-dissect-file-name bfn)) + (localname (tramp-file-name-localname v))) + (tramp-flush-file-property v localname))))) + +(add-hook 'before-revert-hook 'tramp-flush-file-function) +(add-hook 'kill-buffer-hook 'tramp-flush-file-function) +(add-hook 'tramp-cache-unload-hook + '(lambda () + (remove-hook 'before-revert-hook + 'tramp-flush-file-function) + (remove-hook 'kill-buffer-hook + 'tramp-flush-file-function))) + +;;; -- Properties -- + +(defun tramp-get-connection-property (key property default) + "Get the named PROPERTY for the connection. +KEY identifies the connection, it is either a process or a vector. +If the value is not set for the connection, returns DEFAULT." + ;; Unify key by removing localname from vector. Work with a copy in + ;; order to avoid side effects. + (when (vectorp key) + (setq key (copy-sequence key)) + (aset key 3 nil)) + (let* ((hash (gethash key tramp-cache-data)) + (value (if (hash-table-p hash) + (gethash property hash default) + default))) + (tramp-message key 7 "%s %s" property value) + value)) + +(defun tramp-set-connection-property (key property value) + "Set the named PROPERTY of a connection to VALUE. +KEY identifies the connection, it is either a process or a vector. +PROPERTY is set persistent when KEY is a vector." + ;; Unify key by removing localname from vector. Work with a copy in + ;; order to avoid side effects. + (when (vectorp key) + (setq key (copy-sequence key)) + (aset key 3 nil)) + (let ((hash (or (gethash key tramp-cache-data) + (puthash key (make-hash-table :test 'equal) + tramp-cache-data)))) + (puthash property value hash) + ;; This function is called also during initialization of + ;; tramp-cache.el. `tramp-message´ is not defined yet at this + ;; time, so we ignore the corresponding error. + (condition-case nil + (tramp-message key 7 "%s %s" property value) + (error nil)) + value)) + +(defun tramp-flush-connection-property (key event) + "Remove all properties identified by KEY. +KEY identifies the connection, it is either a process or a +vector. EVENT is not used, it is just applied because this +function is intended to run also as process sentinel." + ;; Unify key by removing localname from vector. Work with a copy in + ;; order to avoid side effects. + (when (vectorp key) + (setq key (copy-sequence key)) + (aset key 3 nil)) +; (tramp-message key 7 "%s" event) + (remhash key tramp-cache-data)) + +(defun tramp-dump-connection-properties () +"Writes persistent connection properties into file +`tramp-persistency-file-name'." + ;; We shouldn't fail, otherwise (X)Emacs might not be able to be closed. + (condition-case nil + (when (and (hash-table-p tramp-cache-data) + (not (zerop (hash-table-count tramp-cache-data))) + (stringp tramp-persistency-file-name)) + (let ((cache (copy-hash-table tramp-cache-data))) + ;; Remove temporary data. + (maphash + '(lambda (key value) + (if (and (vectorp key) (not (tramp-file-name-localname key))) + (progn + (remhash "process-name" value) + (remhash "process-buffer" value)) + (remhash key cache))) + cache) + ;; Dump it. + (with-temp-buffer + (insert + ";; -*- emacs-lisp -*-" + ;; `time-stamp-string' might not exist in all (X)Emacs flavors. + (condition-case nil + (progn + (format + " <%s %s>\n" + (time-stamp-string "%02y/%02m/%02d %02H:%02M:%02S") + tramp-persistency-file-name)) + (error "\n")) + ";; Tramp connection history. Don't change this file.\n" + ";; You can delete it, forcing Tramp to reapply the checks.\n\n" + (with-output-to-string + (pp (read (format "(%s)" (tramp-cache-print cache)))))) + (write-region + (point-min) (point-max) tramp-persistency-file-name)))) + (error nil))) + +(add-hook 'kill-emacs-hook 'tramp-dump-connection-properties) +(add-hook 'tramp-cache-unload-hook + '(lambda () + (remove-hook 'kill-emacs-hook + 'tramp-dump-connection-properties))) + +(defun tramp-parse-connection-properties (method) + "Return a list of (user host) tuples allowed to access for METHOD. +This function is added always in `tramp-get-completion-function' +for all methods. Resulting data are derived from connection +history." + (let (res) + (maphash + '(lambda (key value) + (if (and (vectorp key) + (string-equal method (tramp-file-name-method key)) + (not (tramp-file-name-localname key))) + (push (list (tramp-file-name-user key) + (tramp-file-name-host key)) + res))) + tramp-cache-data) + res)) + +;; Read persistent connection history. Applied with +;; `load-in-progress', because it shall be evaluated only once. +(when load-in-progress + (condition-case err + (with-temp-buffer + (insert-file-contents tramp-persistency-file-name) + (let ((list (read (current-buffer))) + element key item) + (while (setq element (pop list)) + (setq key (pop element)) + (while (setq item (pop element)) + (tramp-set-connection-property key (pop item) (car item)))))) + (file-error + ;; Most likely because the file doesn't exist yet. No message. + (clrhash tramp-cache-data)) + (error + ;; File is corrupted. + (message "%s" (error-message-string err)) + (clrhash tramp-cache-data)))) + +(provide 'tramp-cache) + +;; arch-tag: ee1739b7-7628-408c-9b96-d11a74b05d26 +;;; tramp-cache.el ends here diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp-fish.el --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/net/tramp-fish.el Sun Jul 15 04:47:46 2007 +0000 @@ -0,0 +1,1178 @@ +;;; -*- coding: iso-8859-1; -*- +;;; tramp-fish.el --- Tramp access functions for FISH protocol + +;; Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +;; Author: Michael Albinus +;; Keywords: comm, processes + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, see +;; . + +;;; Commentary: + +;; Access functions for FIles transferred over SHell protocol from Tramp. + +;; FISH is a protocol developped for the GNU Midnight Commander +;; . A client connects to a +;; remote host via ssh (or rsh, shall be configurable), and starts +;; there a fish server via the command "start_fish_server". All +;; commands from the client have the form "#FISH_COMMAND\n" (always +;; one line), followed by equivalent shell commands in case there is +;; no fish server running. + +;; The fish server (or the equivalent shell commands) must return the +;; response, which is finished by a line "### xxx \n". +;; "xxx" stands for 3 digits, representing a return code. Return +;; codes "# 000" and "# 001" are reserved for fallback implementation +;; with native shell commands; they are not used inside the server. See +;; +;; for details of original specification. + +;; The GNU Midnight Commander implements the original fish protocol +;; version 0.0.2. The KDE Konqueror has its own implementation, which +;; can be found at +;; . It +;; implements an extended protocol version 0.0.3. Additionally, it +;; provides a fish server implementation in Perl (which is the only +;; implementation I've heard of). The following command reference is +;; based on that implementation. + +;; All commands return either "### 2xx\n" (OK) or "### 5xx \n" +;; (NOK). Return codes are mentioned only if they are different from this. +;; Spaces in any parameter must be escaped by "\ ". + +;; Command/Return Code Comment +;; +;; #FISH initial connection, not used +;; in .fishsrv.pl +;; ### 100 transfer fish server missing server, or wrong checksum +;; version 0.0.3 only + +;; #VER a.b.c +;; VER x.y.z .fishsrv.pl response is not uptodate + +;; #PWD +;; /path/to/file + +;; #CWD /some/path + +;; #COPY /path/a /path/b version 0.0.3 only + +;; #RENAME /path/a /path/b + +;; #SYMLINK /path/a /path/b + +;; #LINK /path/a /path/b + +;; #DELE /some/path + +;; #MKD /some/path + +;; #RMD /some/path + +;; #CHOWN user /file/name + +;; #CHGRP group /file/name + +;; #CHMOD 1234 file + +;; #READ /path/and/filename +;; ### 291 successful exit when reading +;; ended at eof +;; ### 292 successful exit when reading +;; did not end at eof + +;; #WRITE /path/and/filename + +;; #APPEND /path/and/filename version 0.0.3 only + +;; #LIST /directory +;; version 0.0.3 only +;; ### 100 version 0.0.3 only +;; P . +;; S +;; d<3-letters month name> +;; D [.1234] +;; E, +;; : +;; L +;; M version 0.0.3 only +;; + +;; #STAT /file version 0.0.3 only +;; like #LIST except for directories +;; +;; ### 100 +;; P . +;; S +;; d<3-letters month name> +;; D [.1234] +;; E, +;; : +;; L +;; + +;; #RETR /some/name +;; +;; ### 100 +;; exactly filesize bytes +;; ### 200 with no preceding newline + +;; #STOR /file/name +;; ### 100 +;; exactly size bytes +;; ### 001 partial success + +;; #EXEC version 0.0.3 only +;; must not exists. It contains the output of . +;; It can be retrieved afterwards. Last line is +;; ###RESULT: + +;; This implementation is meant as proof of the concept, whether there +;; is a better performance compared with the native ssh method. It +;; looks like the file information retrieval is slower, especially the +;; #LIST command. On the other hand, the file contents transmission +;; seems to perform better than other inline methods, because there is +;; no need for data encoding/decoding, and it supports the APPEND +;; parameter of `write-region'. Transfer of binary data fails due to +;; Emacs' process input/output handling. + + +;;; Code: + +(require 'tramp) +(require 'tramp-cache) + +;; Pacify byte-compiler +(eval-when-compile + (require 'cl) + (require 'custom)) + +;; Avoid byte-compiler warnings if the byte-compiler supports this. +;; Currently, XEmacs supports this. +(eval-when-compile + (when (featurep 'xemacs) + (byte-compiler-options (warnings (- unused-vars))))) + +;; `directory-sep-char' is an obsolete variable in Emacs. But it is +;; used in XEmacs, so we set it here and there. The following is needed +;; to pacify Emacs byte-compiler. +(eval-when-compile + (unless (boundp 'byte-compile-not-obsolete-var) + (defvar byte-compile-not-obsolete-var nil)) + (setq byte-compile-not-obsolete-var 'directory-sep-char)) + +;; Define FISH method ... +(defcustom tramp-fish-method "fish" + "*Method to connect via FISH protocol." + :group 'tramp + :type 'string) + +;; ... and add it to the method list. +(add-to-list 'tramp-methods (cons tramp-fish-method nil)) + +;; Add a default for `tramp-default-user-alist'. Default is the local user. +(add-to-list 'tramp-default-user-alist + `(,tramp-fish-method nil ,(user-login-name))) + +;; Add completion function for FISH method. +(tramp-set-completion-function + tramp-fish-method tramp-completion-function-alist-ssh) + +(defconst tramp-fish-continue-prompt-regexp "^### 100.*\n" + "FISH return code OK.") + +;; It cannot be a defconst, occasionally we bind it locally. +(defvar tramp-fish-ok-prompt-regexp "^### 200\n" + "FISH return code OK.") + +(defconst tramp-fish-error-prompt-regexp "^### \\(4\\|5\\)[0-9]+.*\n" + "Regexp for possible error strings of FISH servers. +Used instead of analyzing error codes of commands.") + +(defcustom tramp-fish-start-fish-server-command + (concat "stty intr \"\" quit \"\" erase \"\" kill \"\" eof \"\" eol \"\" eol2 \"\" swtch \"\" start \"\" stop \"\" susp \"\" rprnt \"\" werase \"\" lnext \"\" flush \"\"; " + "perl .fishsrv.pl " + "`grep 'ARGV\\[0\\]' .fishsrv.pl | " + "sed -e 's/^[^\"]*\"//' -e 's/\"[^\"]*$//'`; " + "exit") + "*Command to connect via FISH protocol." + :group 'tramp + :type 'string) + +;; New handlers should be added here. +(defconst tramp-fish-file-name-handler-alist + '( + ;; `access-file' performed by default handler + (add-name-to-file . tramp-fish-handle-add-name-to-file) + ;; `byte-compiler-base-file-name' performed by default handler + (copy-file . tramp-fish-handle-copy-file) + (delete-directory . tramp-fish-handle-delete-directory) + (delete-file . tramp-fish-handle-delete-file) + ;; `diff-latest-backup-file' performed by default handler + (directory-file-name . tramp-handle-directory-file-name) + (directory-files . tramp-handle-directory-files) + (directory-files-and-attributes . tramp-fish-handle-directory-files-and-attributes) + ;; `dired-call-process' performed by default handler + ;; `dired-compress-file' performed by default handler + ;; `dired-uncache' performed by default handler + (expand-file-name . tramp-fish-handle-expand-file-name) + ;; `file-accessible-directory-p' performed by default handler + (file-attributes . tramp-fish-handle-file-attributes) + (file-directory-p . tramp-fish-handle-file-directory-p) + (file-executable-p . tramp-fish-handle-file-executable-p) + (file-exists-p . tramp-fish-handle-file-exists-p) + (file-local-copy . tramp-fish-handle-file-local-copy) + (file-remote-p . tramp-handle-file-remote-p) + (file-modes . tramp-handle-file-modes) + (file-name-all-completions . tramp-fish-handle-file-name-all-completions) + ;; `file-name-as-directory' performed by default handler + (file-name-completion . tramp-handle-file-name-completion) + (file-name-directory . tramp-handle-file-name-directory) + (file-name-nondirectory . tramp-handle-file-name-nondirectory) + ;; `file-name-sans-versions' performed by default handler + (file-newer-than-file-p . tramp-fish-handle-file-newer-than-file-p) + (file-ownership-preserved-p . ignore) + (file-readable-p . tramp-fish-handle-file-readable-p) + (file-regular-p . tramp-handle-file-regular-p) + (file-symlink-p . tramp-handle-file-symlink-p) + ;; `file-truename' performed by default handler + (file-writable-p . tramp-fish-handle-file-writable-p) + (find-backup-file-name . tramp-handle-find-backup-file-name) + ;; `find-file-noselect' performed by default handler + ;; `get-file-buffer' performed by default handler + (insert-directory . tramp-fish-handle-insert-directory) + (insert-file-contents . tramp-fish-handle-insert-file-contents) + (load . tramp-handle-load) + (make-directory . tramp-fish-handle-make-directory) + (make-directory-internal . tramp-fish-handle-make-directory-internal) + (make-symbolic-link . tramp-fish-handle-make-symbolic-link) + (rename-file . tramp-fish-handle-rename-file) + (set-file-modes . tramp-fish-handle-set-file-modes) + (set-visited-file-modtime . ignore) + (shell-command . tramp-handle-shell-command) + (substitute-in-file-name . tramp-handle-substitute-in-file-name) + (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) + (vc-registered . ignore) + (verify-visited-file-modtime . ignore) + (write-region . tramp-fish-handle-write-region) + (executable-find . tramp-fish-handle-executable-find) + (start-process . ignore) + (call-process . tramp-fish-handle-call-process) + (process-file . tramp-handle-process-file) +) + "Alist of handler functions for Tramp FISH method. +Operations not mentioned here will be handled by the default Emacs primitives.") + +(defun tramp-fish-file-name-p (filename) + "Check if it's a filename for FISH protocol." + (let ((v (tramp-dissect-file-name filename))) + (string= (tramp-file-name-method v) tramp-fish-method))) + +(defun tramp-fish-file-name-handler (operation &rest args) + "Invoke the FISH related OPERATION. +First arg specifies the OPERATION, second arg is a list of arguments to +pass to the OPERATION." + (let ((fn (assoc operation tramp-fish-file-name-handler-alist))) + (if fn + (save-match-data (apply (cdr fn) args)) + (tramp-run-real-handler operation args)))) + +(add-to-list 'tramp-foreign-file-name-handler-alist + (cons 'tramp-fish-file-name-p 'tramp-fish-file-name-handler)) + + +;; File name primitives + +(defun tramp-fish-handle-add-name-to-file + (filename newname &optional ok-if-already-exists) + "Like `add-name-to-file' for Tramp files." + (unless (tramp-equal-remote filename newname) + (with-parsed-tramp-file-name + (if (tramp-tramp-file-p filename) filename newname) nil + (tramp-error + v 'file-error + "add-name-to-file: %s" + "only implemented for same method, same user, same host"))) + (with-parsed-tramp-file-name filename v1 + (with-parsed-tramp-file-name newname v2 + (when (and (not ok-if-already-exists) + (file-exists-p newname) + (not (numberp ok-if-already-exists)) + (y-or-n-p + (format + "File %s already exists; make it a new name anyway? " + newname))) + (tramp-error + v2 'file-error + "add-name-to-file: file %s already exists" newname)) + (tramp-flush-file-property v2 v2-localname) + (unless (tramp-fish-send-command-and-check + v1 (format "#LINK %s %s" v1-localname v2-localname)) + (tramp-error + v1 'file-error "Error with add-name-to-file %s" newname))))) + +(defun tramp-fish-handle-copy-file + (filename newname &optional ok-if-already-exists keep-date) + "Like `copy-file' for Tramp files." + (tramp-fish-do-copy-or-rename-file + 'copy filename newname ok-if-already-exists keep-date)) + +(defun tramp-fish-handle-delete-directory (directory) + "Like `delete-directory' for Tramp files." + (when (file-exists-p directory) + (with-parsed-tramp-file-name + (directory-file-name (expand-file-name directory)) nil + (tramp-flush-directory-property v localname) + (tramp-fish-send-command-and-check v (format "#RMD %s" localname))))) + +(defun tramp-fish-handle-delete-file (filename) + "Like `delete-file' for Tramp files." + (when (file-exists-p filename) + (with-parsed-tramp-file-name (expand-file-name filename) nil + (tramp-flush-file-property v localname) + (tramp-fish-send-command-and-check v (format "#DELE %s" localname))))) + +(defun tramp-fish-handle-directory-files-and-attributes + (directory &optional full match nosort id-format) + "Like `directory-files-and-attributes' for Tramp files." + (mapcar + (lambda (x) + ;; We cannot call `file-attributes' for backward compatibility reasons. + ;; Its optional parameter ID-FORMAT is introduced with Emacs 22. + (cons x (tramp-fish-handle-file-attributes + (if full x (expand-file-name x directory)) id-format))) + (directory-files directory full match nosort))) + +(defun tramp-fish-handle-expand-file-name (name &optional dir) + "Like `expand-file-name' for Tramp files." + ;; If DIR is not given, use DEFAULT-DIRECTORY or "/". + (setq dir (or dir default-directory "/")) + ;; Unless NAME is absolute, concat DIR and NAME. + (unless (file-name-absolute-p name) + (setq name (concat (file-name-as-directory dir) name))) + ;; If NAME is not a tramp file, run the real handler + (if (or (tramp-completion-mode) (not (tramp-tramp-file-p name))) + (tramp-drop-volume-letter + (tramp-run-real-handler 'expand-file-name (list name nil))) + ;; Dissect NAME. + (with-parsed-tramp-file-name name nil + (unless (file-name-absolute-p localname) + (setq localname (concat "~/" localname))) + ;; Tilde expansion if necessary. + (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname) + (let ((uname (match-string 1 localname)) + (fname (match-string 2 localname))) + ;; We cannot apply "~user/", because this is not supported + ;; by the FISH protocol. + (unless (string-equal uname "~") + (tramp-error + v 'file-error "Tilde expansion not supported for %s" name)) + (setq uname + (with-connection-property v uname + (tramp-fish-send-command-and-check v "#PWD") + (with-current-buffer (tramp-get-buffer v) + (goto-char (point-min)) + (buffer-substring (point) (tramp-line-end-position))))) + (setq localname (concat uname fname)))) + ;; There might be a double slash, for example when "~/" + ;; expands to "/". Remove this. + (while (string-match "//" localname) + (setq localname (replace-match "/" t t localname))) + ;; No tilde characters in file name, do normal + ;; expand-file-name (this does "/./" and "/../"). We bind + ;; `directory-sep-char' here for XEmacs on Windows, which + ;; would otherwise use backslash. `default-directory' is + ;; bound, because on Windows there would be problems with UNC + ;; shares or Cygwin mounts. + (tramp-let-maybe directory-sep-char ?/ + (let ((default-directory (tramp-temporary-file-directory))) + (tramp-make-tramp-file-name + method user host + (tramp-drop-volume-letter + (tramp-run-real-handler 'expand-file-name + (list localname))))))))) + +(defun tramp-fish-handle-file-attributes (filename &optional id-format) + "Like `file-attributes' for Tramp files." + (with-parsed-tramp-file-name (expand-file-name filename) nil + (with-file-property v localname (format "file-attributes-%s" id-format) + (cdr (car (tramp-fish-get-file-entries v localname nil)))))) + +(defun tramp-fish-handle-file-directory-p (filename) + "Like `file-directory-p' for Tramp files." + (let ((attributes (file-attributes filename))) + (and attributes + (or (string-match "d" (nth 8 attributes)) + (and (file-symlink-p filename) + (with-parsed-tramp-file-name filename nil + (file-directory-p + (tramp-make-tramp-file-name + method user host (nth 0 attributes)))))) + t))) + +(defun tramp-fish-handle-file-exists-p (filename) + "Like `file-exists-p' for Tramp files." + (and (file-attributes filename) t)) + +(defun tramp-fish-handle-file-executable-p (filename) + "Like `file-executable-p' for Tramp files." + (with-parsed-tramp-file-name (expand-file-name filename) nil + (with-file-property v localname "file-executable-p" + (when (file-exists-p filename) + (let ((mode-chars (string-to-vector (nth 8 (file-attributes filename)))) + (home-directory + (tramp-make-tramp-file-name + method user host + (tramp-get-connection-property v "home-directory" nil)))) + (or (and (char-equal (aref mode-chars 3) ?x) + (equal (nth 2 (file-attributes filename)) + (nth 2 (file-attributes home-directory)))) + (and (char-equal (aref mode-chars 6) ?x) + (equal (nth 3 (file-attributes filename)) + (nth 3 (file-attributes home-directory)))) + (char-equal (aref mode-chars 9) ?x))))))) + +(defun tramp-fish-handle-file-readable-p (filename) + "Like `file-readable-p' for Tramp files." + (with-parsed-tramp-file-name (expand-file-name filename) nil + (with-file-property v localname "file-readable-p" + (when (file-exists-p filename) + (let ((mode-chars (string-to-vector (nth 8 (file-attributes filename)))) + (home-directory + (tramp-make-tramp-file-name + method user host + (tramp-get-connection-property v "home-directory" nil)))) + (or (and (char-equal (aref mode-chars 1) ?r) + (equal (nth 2 (file-attributes filename)) + (nth 2 (file-attributes home-directory)))) + (and (char-equal (aref mode-chars 4) ?r) + (equal (nth 3 (file-attributes filename)) + (nth 3 (file-attributes home-directory)))) + (char-equal (aref mode-chars 7) ?r))))))) + +(defun tramp-fish-handle-file-writable-p (filename) + "Like `file-writable-p' for Tramp files." + (with-parsed-tramp-file-name (expand-file-name filename) nil + (with-file-property v localname "file-writable-p" + (if (not (file-exists-p filename)) + ;; If file doesn't exist, check if directory is writable. + (and (file-directory-p (file-name-directory filename)) + (file-writable-p (file-name-directory filename))) + ;; Existing files must be writable. + (let ((mode-chars (string-to-vector (nth 8 (file-attributes filename)))) + (home-directory + (tramp-make-tramp-file-name + method user host + (tramp-get-connection-property v "home-directory" nil)))) + (or (and (char-equal (aref mode-chars 2) ?w) + (equal (nth 2 (file-attributes filename)) + (nth 2 (file-attributes home-directory)))) + (and (char-equal (aref mode-chars 5) ?w) + (equal (nth 3 (file-attributes filename)) + (nth 3 (file-attributes home-directory)))) + (char-equal (aref mode-chars 8) ?w))))))) + +(defun tramp-fish-handle-file-local-copy (filename) + "Like `file-local-copy' for Tramp files." + (with-parsed-tramp-file-name (expand-file-name filename) nil + (unless (file-exists-p filename) + (tramp-error + v 'file-error + "Cannot make local copy of non-existing file `%s'" filename)) + (let ((tmpfil (tramp-make-temp-file filename))) + (tramp-message v 4 "Fetching %s to tmp file %s..." filename tmpfil) + (when (tramp-fish-retrieve-data v) + ;; Save file + (with-current-buffer (tramp-get-buffer v) + (write-region (point-min) (point-max) tmpfil)) + (tramp-message v 4 "Fetching %s to tmp file %s...done" filename tmpfil) + tmpfil)))) + +;; This function should return "foo/" for directories and "bar" for +;; files. +(defun tramp-fish-handle-file-name-all-completions (filename directory) + "Like `file-name-all-completions' for Tramp files." + (all-completions + filename + (with-parsed-tramp-file-name (expand-file-name directory) nil + (with-file-property v localname "file-name-all-completions" + (save-match-data + (let ((entries + (with-file-property v localname "file-entries" + (tramp-fish-get-file-entries v localname t)))) + (mapcar + (lambda (x) + (list + (if (string-match "d" (nth 9 x)) + (file-name-as-directory (nth 0 x)) + (nth 0 x)))) + entries))))))) + +(defun tramp-fish-handle-file-newer-than-file-p (file1 file2) + "Like `file-newer-than-file-p' for Tramp files." + (cond + ((not (file-exists-p file1)) nil) + ((not (file-exists-p file2)) t) + (t (tramp-time-less-p (nth 5 (file-attributes file2)) + (nth 5 (file-attributes file1)))))) + +(defun tramp-fish-handle-insert-directory + (filename switches &optional wildcard full-directory-p) + "Like `insert-directory' for Tramp files. +WILDCARD and FULL-DIRECTORY-P are not handled." + (setq filename (expand-file-name filename)) + (when (file-directory-p filename) + ;; This check is a little bit strange, but in `dired-add-entry' + ;; this function is called with a non-directory ... + (setq filename (file-name-as-directory filename))) + + (with-parsed-tramp-file-name filename nil + (tramp-flush-file-property v localname) + (save-match-data + (let ((entries + (with-file-property v localname "file-entries" + (tramp-fish-get-file-entries v localname t)))) + + ;; Sort entries + (setq entries + (sort + entries + (lambda (x y) + (if (string-match "t" switches) + ;; Sort by date. + (tramp-time-less-p (nth 6 y) (nth 6 x)) + ;; Sort by name. + (string-lessp (nth 0 x) (nth 0 y)))))) + + ;; Print entries. + (mapcar + (lambda (x) + (insert + (format + "%10s %3d %-8s %-8s %8s %s %s%s\n" + (nth 9 x) ; mode + 1 ; hardlinks + (nth 3 x) ; uid + (nth 4 x) ; gid + (nth 8 x) ; size + (format-time-string + (if (tramp-time-less-p + (tramp-time-subtract (current-time) (nth 6 x)) + tramp-half-a-year) + "%b %e %R" + "%b %e %Y") + (nth 6 x)) ; date + (nth 0 x) ; file name + (if (stringp (nth 1 x)) (format " -> %s" (nth 1 x)) ""))) + (forward-line) + (beginning-of-line)) + entries))))) + +(defun tramp-fish-handle-insert-file-contents + (filename &optional visit beg end replace) + "Like `insert-file-contents' for Tramp files." + (barf-if-buffer-read-only) + (when visit + (setq buffer-file-name (expand-file-name filename)) + (set-visited-file-modtime) + (set-buffer-modified-p nil)) + + (with-parsed-tramp-file-name filename nil + (if (not (file-exists-p filename)) + (tramp-error + v 'file-error "File %s not found on remote host" filename) + + (let ((point (point)) + size) + (tramp-message v 4 "Fetching file %s..." filename) + (when (tramp-fish-retrieve-data v) + ;; Insert file + (insert + (with-current-buffer (tramp-get-buffer v) + (let ((beg (or beg (point-min))) + (end (min (or end (point-max)) (point-max)))) + (setq size (- end beg)) + (buffer-substring beg end)))) + (goto-char point)) + (tramp-message v 4 "Fetching file %s...done" filename) + + (list (expand-file-name filename) size))))) + +(defun tramp-fish-handle-make-directory (dir &optional parents) + "Like `make-directory' for Tramp files." + (setq dir (directory-file-name (expand-file-name dir))) + (unless (file-name-absolute-p dir) + (setq dir (expand-file-name dir default-directory))) + (with-parsed-tramp-file-name dir nil + (save-match-data + (let ((ldir (file-name-directory dir))) + ;; Make missing directory parts + (when (and parents (not (file-directory-p ldir))) + (make-directory ldir parents)) + ;; Just do it + (when (file-directory-p ldir) + (make-directory-internal dir)) + (unless (file-directory-p dir) + (tramp-error v 'file-error "Couldn't make directory %s" dir)))))) + +(defun tramp-fish-handle-make-directory-internal (directory) + "Like `make-directory-internal' for Tramp files." + (setq directory (directory-file-name (expand-file-name directory))) + (unless (file-name-absolute-p directory) + (setq directory (expand-file-name directory default-directory))) + (when (file-directory-p (file-name-directory directory)) + (with-parsed-tramp-file-name directory nil + (save-match-data + (unless + (tramp-fish-send-command-and-check v (format "#MKD %s" localname)) + (tramp-error + v 'file-error "Couldn't make directory %s" directory)))))) + +(defun tramp-fish-handle-make-symbolic-link + (filename linkname &optional ok-if-already-exists) + "Like `make-symbolic-link' for Tramp files. +If LINKNAME is a non-Tramp file, it is used verbatim as the target of +the symlink. If LINKNAME is a Tramp file, only the localname component is +used as the target of the symlink. + +If LINKNAME is a Tramp file and the localname component is relative, then +it is expanded first, before the localname component is taken. Note that +this can give surprising results if the user/host for the source and +target of the symlink differ." + (with-parsed-tramp-file-name linkname nil + ;; Do the 'confirm if exists' thing. + (when (file-exists-p linkname) + ;; What to do? + (if (or (null ok-if-already-exists) ; not allowed to exist + (and (numberp ok-if-already-exists) + (not (yes-or-no-p + (format + "File %s already exists; make it a link anyway? " + localname))))) + (tramp-error + v 'file-already-exists "File %s already exists" localname) + (delete-file linkname))) + + ;; If FILENAME is a Tramp name, use just the localname component. + (when (tramp-tramp-file-p filename) + (setq filename (tramp-file-name-localname + (tramp-dissect-file-name (expand-file-name filename))))) + + ;; Right, they are on the same host, regardless of user, method, etc. + ;; We now make the link on the remote machine. This will occur as the user + ;; that FILENAME belongs to. + (unless + (tramp-fish-send-command-and-check + v (format "#SYMLINK %s %s" filename localname)) + (tramp-error v 'file-error "Error creating symbolic link %s" linkname)))) + +(defun tramp-fish-handle-rename-file + (filename newname &optional ok-if-already-exists) + "Like `rename-file' for Tramp files." + (tramp-fish-do-copy-or-rename-file + 'rename filename newname ok-if-already-exists t)) + +(defun tramp-fish-handle-set-file-modes (filename mode) + "Like `set-file-modes' for Tramp files." + (with-parsed-tramp-file-name filename nil + (tramp-flush-file-property v localname) + (unless (tramp-fish-send-command-and-check + v (format "#CHMOD %s %s" + (tramp-decimal-to-octal mode) + (tramp-shell-quote-argument localname))) + (tramp-error + v 'file-error "Error while changing file's mode %s" filename)))) + +(defun tramp-fish-handle-write-region + (start end filename &optional append visit lockname confirm) + "Like `write-region' for Tramp files." + (setq filename (expand-file-name filename)) + (with-parsed-tramp-file-name filename nil + ;; XEmacs takes a coding system as the seventh argument, not `confirm' + (when (and (not (featurep 'xemacs)) + confirm (file-exists-p filename)) + (unless (y-or-n-p (format "File %s exists; overwrite anyway? " + filename)) + (tramp-error v 'file-error "File not overwritten"))) + + (tramp-flush-file-property v localname) + + ;; Send command + (let ((tramp-fish-ok-prompt-regexp + (concat + tramp-fish-ok-prompt-regexp "\\|" + tramp-fish-continue-prompt-regexp))) + (tramp-fish-send-command + v (format "%s %d %s\n### 100" + (if append "#APPEND" "#STOR") (- end start) localname))) + + ;; Send data, if there are any. + (when (> end start) + (tramp-fish-send-command v (buffer-substring-no-properties start end))) + + (when (eq visit t) + (set-visited-file-modtime)))) + +(defun tramp-fish-handle-executable-find (command) + "Like `executable-find' for Tramp files." + (with-temp-buffer + (if (zerop (call-process "which" nil t nil command)) + (progn + (goto-char (point-min)) + (buffer-substring (point-min) (tramp-line-end-position)))))) + +(defun tramp-fish-handle-call-process + (program &optional infile destination display &rest args) + "Like `call-process' for Tramp files." + ;; The implementation is not complete yet. + (when (and (numberp destination) (zerop destination)) + (error "Implementation does not handle immediate return")) + + (with-parsed-tramp-file-name default-directory nil + (let ((temp-name-prefix (tramp-make-tramp-temp-file v)) + command input output stderr outbuf tmpfil ret) + ;; Compute command. + (setq command (mapconcat 'tramp-shell-quote-argument + (cons program args) " ")) + ;; Determine input. + (if (null infile) + (setq input "/dev/null") + (setq infile (expand-file-name infile)) + (if (tramp-equal-remote default-directory infile) + ;; INFILE is on the same remote host. + (setq input (with-parsed-tramp-file-name infile nil localname)) + ;; INFILE must be copied to remote host. + (setq input (concat temp-name-prefix ".in")) + (copy-file + infile + (tramp-make-tramp-file-name method user host input) + t))) + (when input (setq command (format "%s <%s" command input))) + + ;; Determine output. + (setq output (concat temp-name-prefix ".out")) + (cond + ;; Just a buffer + ((bufferp destination) + (setq outbuf destination)) + ;; A buffer name + ((stringp destination) + (setq outbuf (get-buffer-create destination))) + ;; (REAL-DESTINATION ERROR-DESTINATION) + ((consp destination) + ;; output + (cond + ((bufferp (car destination)) + (setq outbuf (car destination))) + ((stringp (car destination)) + (setq outbuf (get-buffer-create (car destination))))) + ;; stderr + (cond + ((stringp (cadr destination)) + (setcar (cdr destination) (expand-file-name (cadr destination))) + (if (tramp-equal-remote default-directory (cadr destination)) + ;; stderr is on the same remote host. + (setq stderr (with-parsed-tramp-file-name + (cadr destination) nil localname)) + ;; stderr must be copied to remote host. The temporary + ;; file must be deleted after execution. + (setq stderr (concat temp-name-prefix ".err")))) + ;; stderr to be discarded + ((null (cadr destination)) + (setq stderr "/dev/null")))) + ;; 't + (destination + (setq outbuf (current-buffer)))) + (when stderr (setq command (format "%s 2>%s" command stderr))) + + ;; If we have a temporary file, it must be removed after operation. + (when (and input (string-match temp-name-prefix input)) + (setq command (format "%s; rm %s" command input))) + ;; Goto working directory. + (unless + (tramp-fish-send-command-and-check + v (format "#CWD %s" (tramp-shell-quote-argument localname))) + (tramp-error v 'file-error "No such directory: %s" default-directory)) + ;; Send the command. It might not return in time, so we protect it. + (condition-case nil + (unwind-protect + (unless (tramp-fish-send-command-and-check + v (format + "#EXEC %s %s" + (tramp-shell-quote-argument command) output)) + (error)) + ;; Check return code. + (setq tmpfil (file-local-copy + (tramp-make-tramp-file-name method user host output))) + (with-temp-buffer + (insert-file-contents tmpfil) + (goto-char (point-max)) + (forward-line -1) + (looking-at "^###RESULT: \\([0-9]+\\)") + (setq ret (string-to-number (match-string 1))) + (delete-region (point) (point-max)) + (write-region (point-min) (point-max) tmpfil)) + ;; We should show the output anyway. + (when outbuf + (with-current-buffer outbuf (insert-file-contents tmpfil)) + (when display (display-buffer outbuf))) + ;; Remove output file. + (delete-file (tramp-make-tramp-file-name method user host output))) + ;; When the user did interrupt, we should do it also. + (error (setq ret 1))) + (unless ret + ;; Provide error file. + (when (and stderr (string-match temp-name-prefix stderr)) + (rename-file (tramp-make-tramp-file-name method user host stderr) + (cadr destination) t))) + ;; Return exit status. + ret))) + + +;; Internal file name functions + +(defun tramp-fish-do-copy-or-rename-file + (op filename newname &optional ok-if-already-exists keep-date) + "Copy or rename a remote file. +OP must be `copy' or `rename' and indicates the operation to +perform. FILENAME specifies the file to copy or rename, NEWNAME +is the name of the new file (for copy) or the new name of the +file (for rename). OK-IF-ALREADY-EXISTS means don't barf if +NEWNAME exists already. KEEP-DATE means to make sure that +NEWNAME has the same timestamp as FILENAME. + +This function is invoked by `tramp-fish-handle-copy-file' and +`tramp-fish-handle-rename-file'. It is an error if OP is neither +of `copy' and `rename'. FILENAME and NEWNAME must be absolute +file names." + (unless (memq op '(copy rename)) + (error "Unknown operation `%s', must be `copy' or `rename'" op)) + (let ((t1 (tramp-tramp-file-p filename)) + (t2 (tramp-tramp-file-p newname))) + + (unless ok-if-already-exists + (when (and t2 (file-exists-p newname)) + (with-parsed-tramp-file-name newname nil + (tramp-error + v 'file-already-exists "File %s already exists" newname)))) + + (prog1 + (cond + ;; Both are Tramp files. + ((and t1 t2) + (cond + ;; Shortcut: if method, host, user are the same for both + ;; files, we invoke `cp' or `mv' on the remote host + ;; directly. + ((tramp-equal-remote filename newname) + (tramp-fish-do-copy-or-rename-file-directly + op filename newname keep-date)) + ;; No shortcut was possible. So we copy the + ;; file first. If the operation was `rename', we go + ;; back and delete the original file (if the copy was + ;; successful). The approach is simple-minded: we + ;; create a new buffer, insert the contents of the + ;; source file into it, then write out the buffer to + ;; the target file. The advantage is that it doesn't + ;; matter which filename handlers are used for the + ;; source and target file. + (t + (tramp-do-copy-or-rename-file-via-buffer + op filename newname keep-date)))) + + ;; One file is a Tramp file, the other one is local. + ((or t1 t2) + ;; Use the generic method via a Tramp buffer. + (tramp-do-copy-or-rename-file-via-buffer + op filename newname keep-date)) + + (t + ;; One of them must be a Tramp file. + (error "Tramp implementation says this cannot happen"))) + ;; When newname did exist, we have wrong cached values. + (when t2 + (with-parsed-tramp-file-name newname nil + (tramp-flush-file-property v localname) + (tramp-flush-file-property v (file-name-directory localname))))))) + +(defun tramp-fish-do-copy-or-rename-file-directly + (op filename newname keep-date) + "Invokes `COPY' or `RENAME' on the remote system. +OP must be one of `copy' or `rename', indicating `cp' or `mv', +respectively. VEC specifies the connection. LOCALNAME1 and +LOCALNAME2 specify the two arguments of `cp' or `mv'. If +KEEP-DATE is non-nil, preserve the time stamp when copying." + (with-parsed-tramp-file-name filename v1 + (with-parsed-tramp-file-name newname v2 + (tramp-fish-send-command + v1 + (format "%s %s %s" + (if (eq op 'copy) "#COPY" "#RENAME") + (tramp-shell-quote-argument v1-localname) + (tramp-shell-quote-argument v2-localname))))) + ;; KEEP-DATE handling. + (when keep-date + (let ((modtime (nth 5 (file-attributes filename)))) + (when (and (not (null modtime)) + (not (equal modtime '(0 0)))) + (tramp-touch newname modtime)))) + ;; Set the mode. + (set-file-modes newname (file-modes filename))) + +(defun tramp-fish-get-file-entries (vec localname list) + "Read entries returned by FISH server. +When LIST is true, a #LIST command will be sent, including all entries +of a directory. Otherwise, #STAT is sent for just one entry. +Result is a list of (LOCALNAME LINK COUNT UID GID ATIME MTIME CTIME +SIZE MODE WEIRD INODE DEVICE)." + (block nil + (with-current-buffer (tramp-get-buffer vec) + ;; #LIST does not work properly with trailing "/", at least in .fishsrv.pl + (when (string-match "/$" localname) + (setq localname (concat localname "."))) + + (let ((command (format "%s %s" (if list "#LIST" "#STAT") localname)) + buffer-read-only num res) + + ;; Send command + (tramp-fish-send-command vec command) + + ;; Read number of entries + (goto-char (point-min)) + (condition-case nil + (unless (integerp (setq num (read (current-buffer)))) (error)) + (error (return nil))) + (forward-line) + (delete-region (point-min) (point)) + + ;; Read return code + (goto-char (point-min)) + (condition-case nil + (unless (looking-at tramp-fish-continue-prompt-regexp) (error)) + (error (return nil))) + (forward-line) + (delete-region (point-min) (point)) + + ;; Loop the listing + (dotimes (i num) + (let ((item (tramp-fish-read-file-entry))) + ;; Add inode and device. + (add-to-list + 'res (append item + (list (tramp-get-inode (car item)) + (tramp-get-device vec)))))) + + ;; Read return code + (goto-char (point-min)) + (condition-case nil + (unless (looking-at tramp-fish-ok-prompt-regexp) (error)) + (error (tramp-error + vec 'file-error + "`%s' does not return a valid Lisp expression: `%s'" + command (buffer-string)))) + (forward-line) + (delete-region (point-min) (point)) + + res)))) + +(defun tramp-fish-read-file-entry () + "Parse entry in output buffer. +Result is the list (LOCALNAME LINK COUNT UID GID ATIME MTIME CTIME +SIZE MODE WEIRD)." + ;; We are called from `tramp-fish-get-file-entries', which sets the + ;; current buffer. + (let (buffer-read-only localname link uid gid mtime size mode) + (block nil + (while t + (cond + ;; P . + ((looking-at "^P\\(.+\\)\\s-\\(.+\\)\\.\\(.+\\)$") + (setq mode (match-string 1)) + (setq uid (match-string 2)) + (setq gid (match-string 3)) + (when (string-match "^d" mode) (setq link t))) + ;; S + ((looking-at "^S\\([0-9]+\\)$") + (setq size (string-to-number (match-string 1)))) + ;; D [.1234] + ((looking-at + "^D\\([0-9]+\\)\\s-\\([0-9]+\\)\\s-\\([0-9]+\\)\\s-\\([0-9]+\\)\\s-\\([0-9]+\\)\\s-\\(\\S-+\\)$") + (setq mtime + (encode-time + (string-to-number (match-string 6)) + (string-to-number (match-string 5)) + (string-to-number (match-string 4)) + (string-to-number (match-string 3)) + (string-to-number (match-string 2)) + (string-to-number (match-string 1))))) + ;; d<3-letters month name> + ((looking-at "^d") nil) + ;; E, + ((looking-at "^E") nil) + ;; : + ((looking-at "^:\\(.+\\)$") + (setq localname (match-string 1))) + ;; L + ((looking-at "^L\\(.+\\)$") + (setq link (match-string 1))) + ;; M + ((looking-at "^M\\(.+\\)$") nil) + ;; last line + ((looking-at "^$") + (return))) + ;; delete line + (forward-line) + (delete-region (point-min) (point)))) + + ;; delete trailing empty line + (forward-line) + (delete-region (point-min) (point)) + + ;; Return entry in file-attributes format + (list localname link -1 uid gid '(0 0) mtime '(0 0) size mode nil))) + +(defun tramp-fish-retrieve-data (vec) + "Reads remote data for FISH protocol. +The data are left in the connection buffer of VEC for further processing. +Returns the size of the data." + (block nil + (with-current-buffer (tramp-get-buffer vec) + ;; The retrieved data might be in binary format, without + ;; trailing newline. Therefore, the OK prompt might not start + ;; at the beginning of a line. + (let ((tramp-fish-ok-prompt-regexp "### 200\n") + size) + + ;; Send command + (tramp-fish-send-command + vec (format "#RETR %s" (tramp-file-name-localname vec))) + + ;; Read filesize + (goto-char (point-min)) + (condition-case nil + (unless (integerp (setq size (read (current-buffer)))) (error)) + (error (return nil))) + (forward-line) + (delete-region (point-min) (point)) + + ;; Read return code + (goto-char (point-min)) + (condition-case nil + (unless (looking-at tramp-fish-continue-prompt-regexp) (error)) + (error (return nil))) + (forward-line) + (delete-region (point-min) (point)) + + ;; The received data might contain the OK prompt already, so + ;; there might be outstanding data. + (while (/= (+ size (length tramp-fish-ok-prompt-regexp)) + (- (point-max) (point-min))) + (tramp-wait-for-regexp + (tramp-get-connection-process vec) nil + (concat tramp-fish-ok-prompt-regexp "$"))) + + ;; Read return code + (goto-char (+ (point-min) size)) + (condition-case nil + (unless (looking-at tramp-fish-ok-prompt-regexp) (error)) + (error (return nil))) + (delete-region (+ (point-min) size) (point-max)) + size)))) + + +;; Connection functions + +(defun tramp-fish-maybe-open-connection (vec) + "Maybe open a connection VEC. +Does not do anything if a connection is already open, but re-opens the +connection if a previous connection has died for some reason." + (let ((process-connection-type tramp-process-connection-type) + (p (get-buffer-process (tramp-get-buffer vec)))) + + ;; New connection must be opened. + (unless (and p (processp p) (memq (process-status p) '(run open))) + + ;; Set variables for computing the prompt for reading password. + (setq tramp-current-method (tramp-file-name-method vec) + tramp-current-user (tramp-file-name-user vec) + tramp-current-host (tramp-file-name-host vec)) + + ;; Start new process. + (when (and p (processp p)) + (delete-process p)) + (setenv "TERM" tramp-terminal-type) + (setenv "PS1" "$ ") + (tramp-message + vec 3 "Opening connection for %s@%s using %s..." + tramp-current-user tramp-current-host tramp-current-method) + + (let* ((process-connection-type tramp-process-connection-type) + (inhibit-eol-conversion nil) + (coding-system-for-read 'binary) + (coding-system-for-write 'binary) + ;; This must be done in order to avoid our file name handler. + (p (let ((default-directory (tramp-temporary-file-directory))) + (start-process + (or (tramp-get-connection-property vec "process-name" nil) + (tramp-buffer-name vec)) + (tramp-get-connection-buffer vec) + "ssh" "-l" + (tramp-file-name-user vec) + (tramp-file-name-host vec))))) + (tramp-message vec 6 "%s" (mapconcat 'identity (process-command p) " ")) + + ;; Check whether process is alive. + (set-process-sentinel p 'tramp-flush-connection-property) + (tramp-set-process-query-on-exit-flag p nil) + + (tramp-process-actions p vec tramp-actions-before-shell 60) + (tramp-fish-send-command vec tramp-fish-start-fish-server-command) + (tramp-message + vec 3 + "Found remote shell prompt on `%s'" (tramp-file-name-host vec)))))) + +(defun tramp-fish-send-command (vec command) + "Send the COMMAND to connection VEC." + (tramp-fish-maybe-open-connection vec) + (tramp-message vec 6 "%s" command) + (tramp-send-string vec command) + (tramp-wait-for-regexp + (tramp-get-connection-process vec) nil + (concat tramp-fish-ok-prompt-regexp "\\|" tramp-fish-error-prompt-regexp))) + +(defun tramp-fish-send-command-and-check (vec command) + "Send the COMMAND to connection VEC. +Returns nil if there has been an error message." + + ;; Send command. + (tramp-fish-send-command vec command) + + ;; Read return code. + (with-current-buffer (tramp-get-buffer vec) + (goto-char (point-min)) + (looking-at tramp-fish-ok-prompt-regexp))) + +(provide 'tramp-fish) +; +;;;; TODO: +; +;; * Evaluate the MIME information with #LIST or #STAT. +; + +;; arch-tag: a66df7df-5f29-42a7-a921-643ceb29db49 +;;;; tramp-fish.el ends here diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp-ftp.el --- a/lisp/net/tramp-ftp.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/net/tramp-ftp.el Sun Jul 15 04:47:46 2007 +0000 @@ -10,8 +10,8 @@ ;; GNU Emacs is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,9 +19,8 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs; see the file COPYING. If not, see +;; . ;;; Commentary: @@ -110,10 +109,13 @@ (list "" "\\`\\(anonymous\\|ftp\\)\\'" tramp-ftp-method)) ;; Add completion function for FTP method. -(unless (memq system-type '(windows-nt)) - (tramp-set-completion-function - tramp-ftp-method - '((tramp-parse-netrc "~/.netrc")))) +(tramp-set-completion-function + tramp-ftp-method + '((tramp-parse-netrc "~/.netrc"))) + +;; If there is URL syntax, `substitute-in-file-name' needs special +;; handling. +(put 'substitute-in-file-name 'ange-ftp 'tramp-handle-substitute-in-file-name) (defun tramp-ftp-file-name-handler (operation &rest args) "Invoke the Ange-FTP handler for OPERATION. @@ -152,13 +154,7 @@ (defun tramp-ftp-file-name-p (filename) "Check if it's a filename that should be forwarded to Ange-FTP." (let ((v (tramp-dissect-file-name filename))) - (string= - (tramp-find-method - (tramp-file-name-multi-method v) - (tramp-file-name-method v) - (tramp-file-name-user v) - (tramp-file-name-host v)) - tramp-ftp-method))) + (string= (tramp-file-name-method v) tramp-ftp-method))) (add-to-list 'tramp-foreign-file-name-handler-alist (cons 'tramp-ftp-file-name-p 'tramp-ftp-file-name-handler)) @@ -172,8 +168,6 @@ ;; pretended in `tramp-file-name-handler' otherwise. ;; Furthermore, there are no backup files on FTP hosts. ;; Worth further investigations. -;; * Map /multi:ssh:out@gate:ftp:kai@real.host:/path/to.file -;; on Ange-FTP gateways. ;;; arch-tag: 759fb338-5c63-4b99-bd36-b4d59db91cff ;;; tramp-ftp.el ends here diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp-gw.el --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/net/tramp-gw.el Sun Jul 15 04:47:46 2007 +0000 @@ -0,0 +1,324 @@ +;;; -*- coding: iso-8859-1; -*- +;;; tramp-gw.el --- Tramp utility functions for HTTP tunnels and SOCKS gateways + +;; Copyright (C) 2007 Free Software Foundation, Inc. + +;; Author: Michael Albinus +;; Keywords: comm, processes + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, see +;; . + +;;; Commentary: + +;; Access functions for HTTP tunnels and SOCKS gateways from Tramp. +;; SOCKS functionality is implemented by socks.el from the w3 package. +;; HTTP tunnels are partly implemented in socks.el and url-http.el; +;; both implementations are not complete. Therefore, it is +;; implemented in this package. + +;;; Code: + +(require 'tramp) + +;; Pacify byte-compiler +(eval-when-compile + (require 'cl) + (require 'custom)) + +;; Autoload the socks library. It is used only when we access a SOCKS server. +(autoload 'socks-open-network-stream "socks") +(defvar socks-username (user-login-name)) +(defvar socks-server (list "Default server" "socks" 1080 5)) + +;; Avoid byte-compiler warnings if the byte-compiler supports this. +;; Currently, XEmacs supports this. +(eval-when-compile + (when (featurep 'xemacs) + (byte-compiler-options (warnings (- unused-vars))))) + +;; Define HTTP tunnel method ... +(defvar tramp-gw-tunnel-method "tunnel" + "*Method to connect HTTP gateways.") + +;; ... and port. +(defvar tramp-gw-default-tunnel-port 8080 + "*Default port for HTTP gateways.") + +;; Define SOCKS method ... +(defvar tramp-gw-socks-method "socks" + "*Method to connect SOCKS servers.") + +;; ... and port. +(defvar tramp-gw-default-socks-port 1080 + "*Default port for SOCKS servers.") + +;; Add a default for `tramp-default-user-alist'. Default is the local user. +(add-to-list 'tramp-default-user-alist + `(,tramp-gw-tunnel-method nil ,(user-login-name))) +(add-to-list 'tramp-default-user-alist + `(,tramp-gw-socks-method nil ,(user-login-name))) + +;; Internal file name functions and variables. + +(defvar tramp-gw-vector nil + "Keeps the remote host identification. Needed for Tramp messages.") + +(defvar tramp-gw-gw-vector nil + "Current gateway identification vector.") + +(defvar tramp-gw-gw-proc nil + "Current gateway process.") + +;; This variable keeps the listening process, in order to reuse it for +;; new processes. +(defvar tramp-gw-aux-proc nil + "Process listening on local port, as mediation between SSH and the gateway.") + +(defun tramp-gw-gw-proc-sentinel (proc event) + "Delete auxiliary process when we are deleted." + (unless (memq (process-status proc) '(run open)) + (tramp-message + tramp-gw-vector 4 "Deleting auxiliary process `%s'" tramp-gw-gw-proc) + (let* (tramp-verbose + (p (tramp-get-connection-property proc "process" nil))) + (when (processp p) (delete-process p))))) + +(defun tramp-gw-aux-proc-sentinel (proc event) + "Activate the different filters for involved gateway and auxiliary processes." + (when (memq (process-status proc) '(run open)) + ;; A new process has been spawned from `tramp-gw-aux-proc'. + (tramp-message + tramp-gw-vector 4 + "Opening auxiliary process `%s', speaking with process `%s'" + proc tramp-gw-gw-proc) + (tramp-set-process-query-on-exit-flag proc nil) + ;; We don't want debug messages, because the corresponding debug + ;; buffer might be undecided. + (let (tramp-verbose) + (tramp-set-connection-property tramp-gw-gw-proc "process" proc) + (tramp-set-connection-property proc "process" tramp-gw-gw-proc)) + ;; Set the process-filter functions for both processes. + (set-process-filter proc 'tramp-gw-process-filter) + (set-process-filter tramp-gw-gw-proc 'tramp-gw-process-filter) + ;; There might be already some output from the gateway process. + (with-current-buffer (process-buffer tramp-gw-gw-proc) + (unless (= (point-min) (point-max)) + (let ((s (buffer-string))) + (delete-region (point) (point-max)) + (tramp-gw-process-filter tramp-gw-gw-proc s)))))) + +(defun tramp-gw-process-filter (proc string) + (let (tramp-verbose) + (process-send-string + (tramp-get-connection-property proc "process" nil) string))) + +(defun tramp-gw-open-connection (vec gw-vec target-vec) + "Open a remote connection to VEC (see `tramp-file-name' structure). +Take GW-VEC as SOCKS or HTTP gateway, i.e. its method must be a +gateway method. TARGET-VEC identifies where to connect to via +the gateway, it can be different from VEC when there are more +hops to be applied. + +It returns a string like \"localhost#port\", which must be used +instead of the host name declared in TARGET-VEC." + + ;; Remember vectors for property retrieval. + (setq tramp-gw-vector vec + tramp-gw-gw-vector gw-vec) + + ;; Start listening auxiliary process. + (unless (and (processp tramp-gw-aux-proc) + (memq (process-status tramp-gw-aux-proc) '(listen))) + (let ((aux-vec + (vector "aux" (tramp-file-name-user gw-vec) + (tramp-file-name-host gw-vec) nil))) + (setq tramp-gw-aux-proc + (make-network-process + :name (tramp-buffer-name aux-vec) :buffer nil :host 'local + :server t :noquery t :service t :coding 'binary)) + (set-process-sentinel tramp-gw-aux-proc 'tramp-gw-aux-proc-sentinel) + (tramp-set-process-query-on-exit-flag tramp-gw-aux-proc nil) + (tramp-message + vec 4 "Opening auxiliary process `%s', listening on port %d" + tramp-gw-aux-proc (process-contact tramp-gw-aux-proc :service)))) + + (let* ((gw-method + (intern + (tramp-find-method + (tramp-file-name-method gw-vec) + (tramp-file-name-user gw-vec) + (tramp-file-name-host gw-vec)))) + (socks-username + (tramp-find-user + (tramp-file-name-method gw-vec) + (tramp-file-name-user gw-vec) + (tramp-file-name-host gw-vec))) + ;; Declare the SOCKS server to be used. + (socks-server + (list "Tramp tempory socks server list" + ;; Host name. + (tramp-file-name-real-host gw-vec) + ;; Port number. + (or (tramp-file-name-port gw-vec) + (case gw-method + (tunnel tramp-gw-default-tunnel-port) + (socks tramp-gw-default-socks-port))) + ;; Type. We support only http and socks5, NO socks4. + ;; 'http could be used when HTTP tunnel works in socks.el. + 5)) + ;; The function to be called. + (socks-function + (case gw-method + (tunnel 'tramp-gw-open-network-stream) + (socks 'socks-open-network-stream))) + socks-noproxy) + + ;; Open SOCKS process. + (setq tramp-gw-gw-proc + (funcall + socks-function + (tramp-buffer-name gw-vec) + (tramp-get-buffer gw-vec) + (tramp-file-name-real-host target-vec) + (tramp-file-name-port target-vec))) + (set-process-sentinel tramp-gw-gw-proc 'tramp-gw-gw-proc-sentinel) + (tramp-set-process-query-on-exit-flag tramp-gw-gw-proc nil) + (tramp-message + vec 4 "Opened %s process `%s'" + (case gw-method ('tunnel "HTTP tunnel") ('socks "SOCKS")) + tramp-gw-gw-proc) + + ;; Return the new host for gateway access. + (format "localhost#%d" (process-contact tramp-gw-aux-proc :service)))) + +(defun tramp-gw-open-network-stream (name buffer host service) + "Open stream to proxy server HOST:SERVICE. +Resulting process has name NAME and buffer BUFFER. If +authentication is requested from proxy server, provide it." + (let ((command (format (concat + "CONNECT %s:%d HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "Connection: keep-alive\r\n" + "User-Agent: Tramp/%s\r\n") + host service host service tramp-version)) + (authentication "") + (first t) + found proc) + + (while (not found) + ;; Clean up. + (when (processp proc) (delete-process proc)) + (with-current-buffer buffer (erase-buffer)) + ;; Open network stream. + (setq proc (open-network-stream + name buffer (nth 1 socks-server) (nth 2 socks-server))) + (set-process-coding-system proc 'binary 'binary) + (tramp-set-process-query-on-exit-flag proc nil) + ;; Send CONNECT command. + (process-send-string proc (format "%s%s\r\n" command authentication)) + (tramp-message + tramp-gw-vector 6 "\n%s" + (format + "%s%s\r\n" command + (replace-regexp-in-string ;; no password in trace! + "Basic [^\r\n]+" "Basic xxxxx" authentication t))) + (with-current-buffer buffer + ;; Trap errors to be traced in the right trace buffer. Often, + ;; proxies have a timeout of 60". We wait 65" in order to + ;; receive an answer this case. + (condition-case nil + (let (tramp-verbose) + (tramp-wait-for-regexp proc 65 "\r?\n\r?\n")) + (error nil)) + ;; Check return code. + (goto-char (point-min)) + (narrow-to-region + (point-min) + (or (search-forward-regexp "\r?\n\r?\n" nil t) (point-max))) + (tramp-message tramp-gw-vector 6 "\n%s" (buffer-string)) + (goto-char (point-min)) + (search-forward-regexp "^HTTP/[1-9]\\.[0-9]" nil t) + (case (condition-case nil (read (current-buffer)) (error)) + ;; Connected. + (200 (setq found t)) + ;; We need basic authentication. + (401 (setq authentication (tramp-gw-basic-authentication nil first))) + ;; Target host not found. + (404 (tramp-error-with-buffer + (current-buffer) tramp-gw-vector 'file-error + "Host %s not found." host)) + ;; We need basic proxy authentication. + (407 (setq authentication (tramp-gw-basic-authentication t first))) + ;; Connection failed. + (503 (tramp-error-with-buffer + (current-buffer) tramp-gw-vector 'file-error + "Connection to %s:%d failed." host service)) + ;; That doesn't work at all. + (t (tramp-error-with-buffer + (current-buffer) tramp-gw-vector 'file-error + "Access to HTTP server %s:%d failed." + (nth 1 socks-server) (nth 2 socks-server)))) + ;; Remove HTTP headers. + (delete-region (point-min) (point-max)) + (widen) + (setq first nil))) + ;; Return the process. + proc)) + +(defun tramp-gw-basic-authentication (proxy pw-cache) + "Return authentication header for CONNECT, based on server request. +PROXY is an indication whether we need a Proxy-Authorization header +or an Authorization header. If PW-CACHE is non-nil, check for +password in password cache. This is done for the first try only." + + ;; `tramp-current-*' must be set for `tramp-read-passwd' and + ;; `tramp-clear-passwd'. + (let ((tramp-current-method (tramp-file-name-method tramp-gw-gw-vector)) + (tramp-current-user (tramp-file-name-user tramp-gw-gw-vector)) + (tramp-current-host (tramp-file-name-host tramp-gw-gw-vector))) + (unless pw-cache (tramp-clear-passwd)) + ;; We are already in the right buffer. + (tramp-message + tramp-gw-vector 5 "%s required" + (if proxy "Proxy authentication" "Authentication")) + ;; Search for request header. We accept only basic authentication. + (goto-char (point-min)) + (search-forward-regexp + "^\\(Proxy\\|WWW\\)-Authenticate:\\s-*Basic\\s-+realm=") + ;; Return authentication string. + (format + "%s: Basic %s\r\n" + (if proxy "Proxy-Authorization" "Authorization") + (base64-encode-string + (format + "%s:%s" + socks-username + (tramp-read-passwd + proc + (format + "Password for %s@[%s]: " socks-username (read (current-buffer))))))))) + + +(provide 'tramp-gw) + +;;; TODO: + +;; * Provide descriptive Commentary. +;; * Enable it for several gateway processes in parallel. + +;; arch-tag: 277e3a81-fdee-40cf-9e6b-59626292a5e0 +;;; tramp-gw.el ends here diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp-smb.el --- a/lisp/net/tramp-smb.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/net/tramp-smb.el Sun Jul 15 04:47:46 2007 +0000 @@ -1,6 +1,7 @@ ;;; tramp-smb.el --- Tramp access functions for SMB servers -*- coding: iso-8859-1; -*- -;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. +;; Copyright (C) 2002, 2003, 2004, 2005, 2006, +;; 2007 Free Software Foundation, Inc. ;; Author: Michael Albinus ;; Keywords: comm, processes @@ -9,8 +10,8 @@ ;; GNU Emacs is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -18,9 +19,8 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs; see the file COPYING. If not, see +;; . ;;; Commentary: @@ -29,6 +29,7 @@ ;;; Code: (require 'tramp) +(require 'tramp-cache) ;; Pacify byte-compiler (eval-when-compile (require 'custom)) @@ -36,10 +37,8 @@ ;; Avoid byte-compiler warnings if the byte-compiler supports this. ;; Currently, XEmacs supports this. (eval-when-compile - (when (fboundp 'byte-compiler-options) - (let (unused-vars) ; Pacify Emacs byte-compiler - (defalias 'warnings 'identity) ; Pacify Emacs byte-compiler - (byte-compiler-options (warnings (- unused-vars)))))) + (when (featurep 'xemacs) + (byte-compiler-options (warnings (- unused-vars))))) ;; Define SMB method ... (defcustom tramp-smb-method "smb" @@ -53,7 +52,12 @@ ;; Add a default for `tramp-default-method-alist'. Rule: If there is ;; a domain in USER, it must be the SMB method. (add-to-list 'tramp-default-method-alist - (list "" "%" tramp-smb-method)) + `(nil "%" ,tramp-smb-method)) + +;; Add a default for `tramp-default-user-alist'. Rule: For the SMB method, +;; the anonymous user is chosen. +(add-to-list 'tramp-default-user-alist + `(,tramp-smb-method nil "")) ;; Add completion function for SMB method. (tramp-set-completion-function @@ -69,11 +73,13 @@ "Regexp used as prompt in smbclient.") (defconst tramp-smb-errors + ;; `regexp-opt' not possible because of first string. (mapconcat 'identity - '(; Connection error + '(;; Connection error / timeout "Connection to \\S-+ failed" - ; Samba + "Read from server failed, maybe it closed the connection" + ;; Samba "ERRDOS" "ERRSRV" "ERRbadfile" @@ -82,34 +88,48 @@ "ERRnoaccess" "ERRnomem" "ERRnosuchshare" - ; Windows NT 4.0, Windows 5.0 (Windows 2000), Windows 5.1 (Windows XP) + ;; Windows 4.0 (Windows NT), Windows 5.0 (Windows 2000), + ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 2003) "NT_STATUS_ACCESS_DENIED" "NT_STATUS_ACCOUNT_LOCKED_OUT" "NT_STATUS_BAD_NETWORK_NAME" "NT_STATUS_CANNOT_DELETE" + "NT_STATUS_DIRECTORY_NOT_EMPTY" + "NT_STATUS_DUPLICATE_NAME" + "NT_STATUS_FILE_IS_A_DIRECTORY" "NT_STATUS_LOGON_FAILURE" "NT_STATUS_NETWORK_ACCESS_DENIED" "NT_STATUS_NO_SUCH_FILE" + "NT_STATUS_OBJECT_NAME_COLLISION" "NT_STATUS_OBJECT_NAME_INVALID" "NT_STATUS_OBJECT_NAME_NOT_FOUND" "NT_STATUS_SHARING_VIOLATION" + "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE" "NT_STATUS_WRONG_PASSWORD") "\\|") "Regexp for possible error strings of SMB servers. Used instead of analyzing error codes of commands.") -(defvar tramp-smb-share nil - "Holds the share name for the current buffer. -This variable is local to each buffer.") -(make-variable-buffer-local 'tramp-smb-share) +(defconst tramp-smb-actions-with-share + '((tramp-smb-prompt tramp-action-succeed) + (tramp-password-prompt-regexp tramp-action-password) + (tramp-wrong-passwd-regexp tramp-action-permission-denied) + (tramp-smb-errors tramp-action-permission-denied) + (tramp-process-alive-regexp tramp-action-process-alive)) + "List of pattern/action pairs. +This list is used for login to SMB servers. -(defvar tramp-smb-share-cache nil - "Caches the share names accessible to host related to the current buffer. -This variable is local to each buffer.") -(make-variable-buffer-local 'tramp-smb-share-cache) +See `tramp-actions-before-shell' for more info.") -(defvar tramp-smb-inodes nil - "Keeps virtual inodes numbers for SMB files.") +(defconst tramp-smb-actions-without-share + '((tramp-password-prompt-regexp tramp-action-password) + (tramp-wrong-passwd-regexp tramp-action-permission-denied) + (tramp-smb-errors tramp-action-permission-denied) + (tramp-process-alive-regexp tramp-action-out-of-band)) + "List of pattern/action pairs. +This list is used for login to SMB servers. + +See `tramp-actions-before-shell' for more info.") ;; New handlers should be added here. (defconst tramp-smb-file-name-handler-alist @@ -124,8 +144,8 @@ (directory-file-name . tramp-handle-directory-file-name) (directory-files . tramp-smb-handle-directory-files) (directory-files-and-attributes . tramp-smb-handle-directory-files-and-attributes) - (dired-call-process . tramp-smb-not-handled) - (dired-compress-file . tramp-smb-not-handled) + (dired-call-process . ignore) + (dired-compress-file . ignore) ;; `dired-uncache' performed by default handler ;; `expand-file-name' not necessary because we cannot expand "~/" (file-accessible-directory-p . tramp-smb-handle-file-directory-p) @@ -143,10 +163,10 @@ (file-name-nondirectory . tramp-handle-file-name-nondirectory) ;; `file-name-sans-versions' performed by default handler (file-newer-than-file-p . tramp-smb-handle-file-newer-than-file-p) - (file-ownership-preserved-p . tramp-smb-not-handled) + (file-ownership-preserved-p . ignore) (file-readable-p . tramp-smb-handle-file-exists-p) (file-regular-p . tramp-handle-file-regular-p) - (file-symlink-p . tramp-smb-not-handled) + (file-symlink-p . tramp-handle-file-symlink-p) ;; `file-truename' performed by default handler (file-writable-p . tramp-smb-handle-file-writable-p) (find-backup-file-name . tramp-handle-find-backup-file-name) @@ -157,15 +177,15 @@ (load . tramp-handle-load) (make-directory . tramp-smb-handle-make-directory) (make-directory-internal . tramp-smb-handle-make-directory-internal) - (make-symbolic-link . tramp-smb-not-handled) + (make-symbolic-link . ignore) (rename-file . tramp-smb-handle-rename-file) - (set-file-modes . tramp-smb-not-handled) - (set-visited-file-modtime . tramp-smb-not-handled) - (shell-command . tramp-smb-not-handled) + (set-file-modes . ignore) + (set-visited-file-modtime . ignore) + (shell-command . ignore) (substitute-in-file-name . tramp-smb-handle-substitute-in-file-name) (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) - (vc-registered . tramp-smb-not-handled) - (verify-visited-file-modtime . tramp-smb-not-handled) + (vc-registered . ignore) + (verify-visited-file-modtime . ignore) (write-region . tramp-smb-handle-write-region) ) "Alist of handler functions for Tramp SMB method. @@ -174,13 +194,7 @@ (defun tramp-smb-file-name-p (filename) "Check if it's a filename for SMB servers." (let ((v (tramp-dissect-file-name filename))) - (string= - (tramp-find-method - (tramp-file-name-multi-method v) - (tramp-file-name-method v) - (tramp-file-name-user v) - (tramp-file-name-host v)) - tramp-smb-method))) + (string= (tramp-file-name-method v) tramp-smb-method))) (defun tramp-smb-file-name-handler (operation &rest args) "Invoke the SMB related OPERATION. @@ -188,9 +202,7 @@ pass to the OPERATION." (let ((fn (assoc operation tramp-smb-file-name-handler-alist))) (if fn - (if (eq (cdr fn) 'tramp-smb-not-handled) - (apply (cdr fn) operation args) - (save-match-data (apply (cdr fn) args))) + (save-match-data (apply (cdr fn) args)) (tramp-run-real-handler operation args)))) (add-to-list 'tramp-foreign-file-name-handler-alist @@ -199,14 +211,9 @@ ;; File name primitives -(defun tramp-smb-not-handled (operation &rest args) - "Default handler for all functions which are disrecarded." - (tramp-message 10 "Won't be handled: %s %s" operation args) - nil) - (defun tramp-smb-handle-copy-file (filename newname &optional ok-if-already-exists keep-date) - "Like `copy-file' for tramp files. + "Like `copy-file' for Tramp files. KEEP-DATE is not handled in case NEWNAME resides on an SMB server." (setq filename (expand-file-name filename) newname (expand-file-name newname)) @@ -214,199 +221,187 @@ (let ((tmpfile (file-local-copy filename))) (if tmpfile - ;; remote filename + ;; Remote filename. (rename-file tmpfile newname ok-if-already-exists) - ;; remote newname + ;; Remote newname. (when (file-directory-p newname) (setq newname (expand-file-name (file-name-nondirectory filename) newname))) - (when (and (not ok-if-already-exists) - (file-exists-p newname)) - (error "copy-file: file %s already exists" newname)) (with-parsed-tramp-file-name newname nil - (save-excursion - (let ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname t))) - (unless share - (error "Target `%s' must contain a share name" filename)) - (tramp-smb-maybe-open-connection user host share) - (tramp-message-for-buffer - nil tramp-smb-method user host - 5 "Copying file %s to file %s..." filename newname) - (if (tramp-smb-send-command - user host (format "put %s \"%s\"" filename file)) - (tramp-message-for-buffer - nil tramp-smb-method user host - 5 "Copying file %s to file %s...done" filename newname) - (error "Cannot copy `%s'" filename)))))))) + (when (and (not ok-if-already-exists) + (file-exists-p newname)) + (tramp-error v 'file-already-exists newname)) + + ;; We must also flush the cache of the directory, because + ;; file-attributes reads the values from there. + (tramp-flush-file-property v (file-name-directory localname)) + (tramp-flush-file-property v localname) + (let ((share (tramp-smb-get-share localname)) + (file (tramp-smb-get-localname localname t))) + (unless share + (tramp-error + v 'file-error "Target `%s' must contain a share name" newname)) + (tramp-message v 0 "Copying file %s to file %s..." filename newname) + (if (tramp-smb-send-command + v (format "put %s \"%s\"" filename file)) + (tramp-message + v 0 "Copying file %s to file %s...done" filename newname) + (tramp-error v 'file-error "Cannot copy `%s'" filename))))))) (defun tramp-smb-handle-delete-directory (directory) - "Like `delete-directory' for tramp files." + "Like `delete-directory' for Tramp files." (setq directory (directory-file-name (expand-file-name directory))) (when (file-exists-p directory) (with-parsed-tramp-file-name directory nil - (save-excursion - (let ((share (tramp-smb-get-share localname)) - (dir (tramp-smb-get-localname (file-name-directory localname) t)) - (file (file-name-nondirectory localname))) - (tramp-smb-maybe-open-connection user host share) - (if (and - (tramp-smb-send-command user host (format "cd \"%s\"" dir)) - (tramp-smb-send-command user host (format "rmdir \"%s\"" file))) - ;; Go Home - (tramp-smb-send-command user host (format "cd \\")) - ;; Error - (tramp-smb-send-command user host (format "cd \\")) - (error "Cannot delete directory `%s'" directory))))))) + ;; We must also flush the cache of the directory, because + ;; file-attributes reads the values from there. + (tramp-flush-file-property v (file-name-directory localname)) + (tramp-flush-directory-property v localname) + (let ((dir (tramp-smb-get-localname (file-name-directory localname) t)) + (file (file-name-nondirectory localname))) + (unwind-protect + (unless (and + (tramp-smb-send-command v (format "cd \"%s\"" dir)) + (tramp-smb-send-command v (format "rmdir \"%s\"" file))) + ;; Error + (with-current-buffer (tramp-get-connection-buffer v) + (goto-char (point-min)) + (search-forward-regexp tramp-smb-errors nil t) + (tramp-error + v 'file-error "%s `%s'" (match-string 0) directory))) + ;; Always go home + (tramp-smb-send-command v (format "cd \\"))))))) (defun tramp-smb-handle-delete-file (filename) - "Like `delete-file' for tramp files." + "Like `delete-file' for Tramp files." (setq filename (expand-file-name filename)) (when (file-exists-p filename) (with-parsed-tramp-file-name filename nil - (save-excursion - (let ((share (tramp-smb-get-share localname)) - (dir (tramp-smb-get-localname (file-name-directory localname) t)) - (file (file-name-nondirectory localname))) - (tramp-smb-maybe-open-connection user host share) - (if (and - (tramp-smb-send-command user host (format "cd \"%s\"" dir)) - (tramp-smb-send-command user host (format "rm \"%s\"" file))) - ;; Go Home - (tramp-smb-send-command user host (format "cd \\")) - ;; Error - (tramp-smb-send-command user host (format "cd \\")) - (error "Cannot delete file `%s'" filename))))))) + ;; We must also flush the cache of the directory, because + ;; file-attributes reads the values from there. + (tramp-flush-file-property v (file-name-directory localname)) + (tramp-flush-file-property v localname) + (let ((dir (tramp-smb-get-localname (file-name-directory localname) t)) + (file (file-name-nondirectory localname))) + (unwind-protect + (unless (and + (tramp-smb-send-command v (format "cd \"%s\"" dir)) + (tramp-smb-send-command v (format "rm \"%s\"" file))) + ;; Error + (with-current-buffer (tramp-get-connection-buffer v) + (goto-char (point-min)) + (search-forward-regexp tramp-smb-errors nil t) + (tramp-error + v 'file-error "%s `%s'" (match-string 0) filename))) + ;; Always go home + (tramp-smb-send-command v (format "cd \\"))))))) (defun tramp-smb-handle-directory-files (directory &optional full match nosort) - "Like `directory-files' for tramp files." - (setq directory (directory-file-name (expand-file-name directory))) - (with-parsed-tramp-file-name directory nil - (save-excursion - (let* ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname nil)) - (entries (tramp-smb-get-file-entries user host share file))) - ;; Just the file names are needed - (setq entries (mapcar 'car entries)) - ;; Discriminate with regexp - (when match - (setq entries - (delete nil - (mapcar (lambda (x) (when (string-match match x) x)) - entries)))) - ;; Make absolute localnames if necessary - (when full - (setq entries - (mapcar (lambda (x) - (concat (file-name-as-directory directory) x)) - entries))) - ;; Sort them if necessary - (unless nosort (setq entries (sort entries 'string-lessp))) - ;; That's it - entries)))) + "Like `directory-files' for Tramp files." + (let ((result (mapcar 'directory-file-name + (file-name-all-completions "" directory)))) + ;; Discriminate with regexp + (when match + (setq result + (delete nil + (mapcar (lambda (x) (when (string-match match x) x)) + result)))) + ;; Append directory + (when full + (setq result + (mapcar + (lambda (x) (expand-file-name x directory)) + result))) + ;; Sort them if necessary + (unless nosort (setq result (sort result 'string-lessp))) + ;; That's it + result)) (defun tramp-smb-handle-directory-files-and-attributes (directory &optional full match nosort id-format) - "Like `directory-files-and-attributes' for tramp files." + "Like `directory-files-and-attributes' for Tramp files." (mapcar (lambda (x) ;; We cannot call `file-attributes' for backward compatibility reasons. ;; Its optional parameter ID-FORMAT is introduced with Emacs 22. (cons x (tramp-smb-handle-file-attributes - (if full x (concat (file-name-as-directory directory) x)) id-format))) + (if full x (expand-file-name x directory)) id-format))) (directory-files directory full match nosort))) (defun tramp-smb-handle-file-attributes (filename &optional id-format) - "Like `file-attributes' for tramp files." + "Like `file-attributes' for Tramp files." + ;; Reading just the filename entry via "dir localname" is not + ;; possible, because when filename is a directory, some smbclient + ;; versions return the content of the directory, and other versions + ;; don't. Therefore, the whole content of the upper directory is + ;; retrieved, and the entry of the filename is extracted from. (with-parsed-tramp-file-name filename nil - (save-excursion - (let* ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname nil)) - (entries (tramp-smb-get-file-entries user host share file)) + (with-file-property v localname (format "file-attributes-%s" id-format) + (let* ((entries (tramp-smb-get-file-entries + (file-name-directory filename))) (entry (and entries - (assoc (file-name-nondirectory file) entries))) + (assoc (file-name-nondirectory filename) entries))) (uid (if (and id-format (equal id-format 'string)) "nobody" -1)) (gid (if (and id-format (equal id-format 'string)) "nogroup" -1)) - (inode (tramp-smb-get-inode share file)) - (device (tramp-get-device nil tramp-smb-method user host))) + (inode (tramp-get-inode filename)) + (device (tramp-get-device v))) - ; check result + ;; Check result. (when entry (list (and (string-match "d" (nth 1 entry)) - t) ;0 file type - -1 ;1 link count - uid ;2 uid - gid ;3 gid - '(0 0) ;4 atime - (nth 3 entry) ;5 mtime - '(0 0) ;6 ctime - (nth 2 entry) ;7 size - (nth 1 entry) ;8 mode - nil ;9 gid weird - inode ;10 inode number - device)))))) ;11 file system number + t) ;0 file type + -1 ;1 link count + uid ;2 uid + gid ;3 gid + '(0 0) ;4 atime + (nth 3 entry) ;5 mtime + '(0 0) ;6 ctime + (nth 2 entry) ;7 size + (nth 1 entry) ;8 mode + nil ;9 gid weird + inode ;10 inode number + device)))))) ;11 file system number (defun tramp-smb-handle-file-directory-p (filename) - "Like `file-directory-p' for tramp files." - (with-parsed-tramp-file-name filename nil - (save-excursion - (let* ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname nil)) - (entries (tramp-smb-get-file-entries user host share file)) - (entry (and entries - (assoc (file-name-nondirectory file) entries)))) - (and entry - (string-match "d" (nth 1 entry)) - t))))) + "Like `file-directory-p' for Tramp files." + (and (file-exists-p filename) + (eq ?d (aref (nth 8 (file-attributes filename)) 0)))) (defun tramp-smb-handle-file-exists-p (filename) - "Like `file-exists-p' for tramp files." - (with-parsed-tramp-file-name filename nil - (save-excursion - (let* ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname nil)) - (entries (tramp-smb-get-file-entries user host share file))) - (and entries - (member (file-name-nondirectory file) (mapcar 'car entries)) - t))))) + "Like `file-exists-p' for Tramp files." + (not (null (file-attributes filename)))) (defun tramp-smb-handle-file-local-copy (filename) - "Like `file-local-copy' for tramp files." + "Like `file-local-copy' for Tramp files." (with-parsed-tramp-file-name filename nil - (save-excursion - (let ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname t)) - (tmpfil (tramp-make-temp-file filename))) - (unless (file-exists-p filename) - (error "Cannot make local copy of non-existing file `%s'" filename)) - (tramp-message-for-buffer - nil tramp-smb-method user host - 5 "Fetching %s to tmp file %s..." filename tmpfil) - (tramp-smb-maybe-open-connection user host share) - (if (tramp-smb-send-command - user host (format "get \"%s\" %s" file tmpfil)) - (tramp-message-for-buffer - nil tramp-smb-method user host - 5 "Fetching %s to tmp file %s...done" filename tmpfil) - (error "Cannot make local copy of file `%s'" filename)) - tmpfil)))) + (let ((file (tramp-smb-get-localname localname t)) + (tmpfil (tramp-make-temp-file filename))) + (unless (file-exists-p filename) + (tramp-error + v 'file-error + "Cannot make local copy of non-existing file `%s'" filename)) + (tramp-message v 4 "Fetching %s to tmp file %s..." filename tmpfil) + (if (tramp-smb-send-command v (format "get \"%s\" %s" file tmpfil)) + (tramp-message + v 4 "Fetching %s to tmp file %s...done" filename tmpfil) + (tramp-error + v 'file-error + "Cannot make local copy of file `%s'" filename)) + tmpfil))) ;; This function should return "foo/" for directories and "bar" for ;; files. (defun tramp-smb-handle-file-name-all-completions (filename directory) - "Like `file-name-all-completions' for tramp files." - (with-parsed-tramp-file-name directory nil - (save-match-data - (save-excursion - (let* ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname nil)) - (entries (tramp-smb-get-file-entries user host share file))) - - (all-completions - filename + "Like `file-name-all-completions' for Tramp files." + (all-completions + filename + (with-parsed-tramp-file-name directory nil + (with-file-property v localname "file-name-all-completions" + (save-match-data + (let ((entries (tramp-smb-get-file-entries directory))) (mapcar (lambda (x) (list @@ -416,51 +411,59 @@ entries))))))) (defun tramp-smb-handle-file-newer-than-file-p (file1 file2) - "Like `file-newer-than-file-p' for tramp files." + "Like `file-newer-than-file-p' for Tramp files." (cond ((not (file-exists-p file1)) nil) ((not (file-exists-p file2)) t) - (t (tramp-smb-time-less-p (file-attributes file2) - (file-attributes file1))))) + (t (tramp-time-less-p (nth 5 (file-attributes file2)) + (nth 5 (file-attributes file1)))))) (defun tramp-smb-handle-file-writable-p (filename) - "Like `file-writable-p' for tramp files." - (if (not (file-exists-p filename)) - (let ((dir (file-name-directory filename))) - (and (file-exists-p dir) - (file-writable-p dir))) - (with-parsed-tramp-file-name filename nil - (save-excursion - (let* ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname nil)) - (entries (tramp-smb-get-file-entries user host share file)) - (entry (and entries - (assoc (file-name-nondirectory file) entries)))) - (and share entry - (string-match "w" (nth 1 entry)) - t)))))) + "Like `file-writable-p' for Tramp files." + (if (file-exists-p filename) + (string-match "w" (or (nth 8 (file-attributes filename)) "")) + (let ((dir (file-name-directory filename))) + (and (file-exists-p dir) + (file-writable-p dir))))) (defun tramp-smb-handle-insert-directory (filename switches &optional wildcard full-directory-p) - "Like `insert-directory' for tramp files. -WILDCARD and FULL-DIRECTORY-P are not handled." + "Like `insert-directory' for Tramp files." (setq filename (expand-file-name filename)) - (when (file-directory-p filename) - ;; This check is a little bit strange, but in `dired-add-entry' - ;; this function is called with a non-directory ... + (when full-directory-p + ;; Called from `dired-add-entry'. (setq filename (file-name-as-directory filename))) (with-parsed-tramp-file-name filename nil + (tramp-flush-file-property v (file-name-directory localname)) (save-match-data - (let* ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname nil)) - (entries (tramp-smb-get-file-entries user host share file))) + (let ((base (file-name-nondirectory filename)) + ;; We should not destroy the cache entry. + (entries (copy-sequence + (tramp-smb-get-file-entries + (file-name-directory filename))))) - ;; Delete dummy "" entry, useless entries + (when wildcard + (string-match "\\." base) + (setq base (replace-match "\\\\." nil nil base)) + (string-match "\\*" base) + (setq base (replace-match ".*" nil nil base)) + (string-match "\\?" base) + (setq base (replace-match ".?" nil nil base))) + + ;; Filter entries. (setq entries - (if (file-directory-p filename) - (delq (assoc "" entries) entries) - ;; We just need the only and only entry FILENAME. - (list (assoc (file-name-nondirectory filename) entries)))) + (delq + nil + (if (or wildcard (zerop (length base))) + ;; Check for matching entries. + (mapcar + (lambda (x) + (when (string-match + (format "^%s" base) (nth 0 x)) + x)) + entries) + ;; We just need the only and only entry FILENAME. + (list (assoc base entries))))) ;; Sort entries (setq entries @@ -468,37 +471,38 @@ entries (lambda (x y) (if (string-match "t" switches) - ; sort by date - (tramp-smb-time-less-p (nth 3 y) (nth 3 x)) - ; sort by name + ;; Sort by date. + (tramp-time-less-p (nth 3 y) (nth 3 x)) + ;; Sort by name. (string-lessp (nth 0 x) (nth 0 y)))))) - ;; Print entries + ;; Print entries. (mapcar (lambda (x) - (insert - (format - "%10s %3d %-8s %-8s %8s %s %s\n" - (nth 1 x) ; mode - 1 "nobody" "nogroup" - (nth 2 x) ; size - (format-time-string - (if (tramp-smb-time-less-p - (tramp-smb-time-subtract (current-time) (nth 3 x)) - tramp-smb-half-a-year) - "%b %e %R" - "%b %e %Y") - (nth 3 x)) ; date - (nth 0 x))) ; file name - (forward-line) - (beginning-of-line)) - entries))))) + (when (not (zerop (length (nth 0 x)))) + (insert + (format + "%10s %3d %-8s %-8s %8s %s %s\n" + (nth 1 x) ; mode + 1 "nobody" "nogroup" + (nth 2 x) ; size + (format-time-string + (if (tramp-time-less-p + (tramp-time-subtract (current-time) (nth 3 x)) + tramp-half-a-year) + "%b %e %R" + "%b %e %Y") + (nth 3 x)) ; date + (nth 0 x))) ; file name + (forward-line) + (beginning-of-line))) + entries))))) (defun tramp-smb-handle-make-directory (dir &optional parents) - "Like `make-directory' for tramp files." + "Like `make-directory' for Tramp files." (setq dir (directory-file-name (expand-file-name dir))) (unless (file-name-absolute-p dir) - (setq dir (concat default-directory dir))) + (setq dir (expand-file-name dir default-directory))) (with-parsed-tramp-file-name dir nil (save-match-data (let* ((share (tramp-smb-get-share localname)) @@ -510,26 +514,28 @@ (when (file-directory-p ldir) (make-directory-internal dir)) (unless (file-directory-p dir) - (error "Couldn't make directory %s" dir)))))) + (tramp-error v 'file-error "Couldn't make directory %s" dir)))))) (defun tramp-smb-handle-make-directory-internal (directory) - "Like `make-directory-internal' for tramp files." + "Like `make-directory-internal' for Tramp files." (setq directory (directory-file-name (expand-file-name directory))) (unless (file-name-absolute-p directory) - (setq directory (concat default-directory directory))) + (setq directory (expand-file-name directory default-directory))) (with-parsed-tramp-file-name directory nil (save-match-data - (let* ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname nil))) + (let* ((file (tramp-smb-get-localname localname t))) (when (file-directory-p (file-name-directory directory)) - (tramp-smb-maybe-open-connection user host share) - (tramp-smb-send-command user host (format "mkdir \"%s\"" file))) + (tramp-smb-send-command v (format "mkdir \"%s\"" file)) + ;; We must also flush the cache of the directory, because + ;; file-attributes reads the values from there. + (tramp-flush-file-property v (file-name-directory localname))) (unless (file-directory-p directory) - (error "Couldn't make directory %s" directory)))))) + (tramp-error + v 'file-error "Couldn't make directory %s" directory)))))) (defun tramp-smb-handle-rename-file (filename newname &optional ok-if-already-exists) - "Like `rename-file' for tramp files." + "Like `rename-file' for Tramp files." (setq filename (expand-file-name filename) newname (expand-file-name newname)) @@ -543,29 +549,26 @@ (when (file-directory-p newname) (setq newname (expand-file-name (file-name-nondirectory filename) newname))) - (when (and (not ok-if-already-exists) - (file-exists-p newname)) - (error "rename-file: file %s already exists" newname)) (with-parsed-tramp-file-name newname nil - (save-excursion - (let ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname t))) - (tramp-smb-maybe-open-connection user host share) - (tramp-message-for-buffer - nil tramp-smb-method user host - 5 "Copying file %s to file %s..." filename newname) - (if (tramp-smb-send-command - user host (format "put %s \"%s\"" filename file)) - (tramp-message-for-buffer - nil tramp-smb-method user host - 5 "Copying file %s to file %s...done" filename newname) - (error "Cannot rename `%s'" filename))))))) + (when (and (not ok-if-already-exists) + (file-exists-p newname)) + (tramp-error v 'file-already-exists newname)) + ;; We must also flush the cache of the directory, because + ;; file-attributes reads the values from there. + (tramp-flush-file-property v (file-name-directory localname)) + (tramp-flush-file-property v localname) + (let ((file (tramp-smb-get-localname localname t))) + (tramp-message v 0 "Copying file %s to file %s..." filename newname) + (if (tramp-smb-send-command v (format "put %s \"%s\"" filename file)) + (tramp-message + v 0 "Copying file %s to file %s...done" filename newname) + (tramp-error v 'file-error "Cannot rename `%s'" filename)))))) (delete-file filename)) (defun tramp-smb-handle-substitute-in-file-name (filename) - "Like `handle-substitute-in-file-name' for tramp files. + "Like `handle-substitute-in-file-name' for Tramp files. Catches errors for shares like \"C$/\", which are common in Microsoft Windows." (condition-case nil (tramp-run-real-handler 'substitute-in-file-name (list filename)) @@ -573,50 +576,49 @@ (defun tramp-smb-handle-write-region (start end filename &optional append visit lockname confirm) - "Like `write-region' for tramp files." - (unless (eq append nil) - (error "Cannot append to file using tramp (`%s')" filename)) + "Like `write-region' for Tramp files." (setq filename (expand-file-name filename)) - ;; XEmacs takes a coding system as the seventh argument, not `confirm' - (when (and (not (featurep 'xemacs)) - confirm (file-exists-p filename)) - (unless (y-or-n-p (format "File %s exists; overwrite anyway? " - filename)) - (error "File not overwritten"))) (with-parsed-tramp-file-name filename nil - (save-excursion - (let ((share (tramp-smb-get-share localname)) - (file (tramp-smb-get-localname localname t)) - (curbuf (current-buffer)) - tmpfil) - ;; Write region into a tmp file. - (setq tmpfil (tramp-make-temp-file filename)) - ;; We say `no-message' here because we don't want the visited file - ;; modtime data to be clobbered from the temp file. We call - ;; `set-visited-file-modtime' ourselves later on. - (tramp-run-real-handler - 'write-region - (if confirm ; don't pass this arg unless defined for backward compat. - (list start end tmpfil append 'no-message lockname confirm) - (list start end tmpfil append 'no-message lockname))) + (unless (eq append nil) + (tramp-error + v 'file-error "Cannot append to file using tramp (`%s')" filename)) + ;; XEmacs takes a coding system as the seventh argument, not `confirm' + (when (and (not (featurep 'xemacs)) + confirm (file-exists-p filename)) + (unless (y-or-n-p (format "File %s exists; overwrite anyway? " + filename)) + (tramp-error v 'file-error "File not overwritten"))) + ;; We must also flush the cache of the directory, because + ;; file-attributes reads the values from there. + (tramp-flush-file-property v (file-name-directory localname)) + (tramp-flush-file-property v localname) + (let ((file (tramp-smb-get-localname localname t)) + (curbuf (current-buffer)) + tmpfil) + ;; Write region into a tmp file. + (setq tmpfil (tramp-make-temp-file filename)) + ;; We say `no-message' here because we don't want the visited file + ;; modtime data to be clobbered from the temp file. We call + ;; `set-visited-file-modtime' ourselves later on. + (tramp-run-real-handler + 'write-region + (if confirm ; don't pass this arg unless defined for backward compat. + (list start end tmpfil append 'no-message lockname confirm) + (list start end tmpfil append 'no-message lockname))) - (tramp-smb-maybe-open-connection user host share) - (tramp-message-for-buffer - nil tramp-smb-method user host - 5 "Writing tmp file %s to file %s..." tmpfil filename) - (if (tramp-smb-send-command - user host (format "put %s \"%s\"" tmpfil file)) - (tramp-message-for-buffer - nil tramp-smb-method user host - 5 "Writing tmp file %s to file %s...done" tmpfil filename) - (error "Cannot write `%s'" filename)) + (tramp-message v 5 "Writing tmp file %s to file %s..." tmpfil filename) + (if (tramp-smb-send-command v (format "put %s \"%s\"" tmpfil file)) + (tramp-message + v 5 "Writing tmp file %s to file %s...done" tmpfil filename) + (tramp-error v 'file-error "Cannot write `%s'" filename)) - (delete-file tmpfil) - (unless (equal curbuf (current-buffer)) - (error "Buffer has changed from `%s' to `%s'" - curbuf (current-buffer))) - (when (eq visit t) - (set-visited-file-modtime)))))) + (delete-file tmpfil) + (unless (equal curbuf (current-buffer)) + (tramp-error + v 'file-error + "Buffer has changed from `%s' to `%s'" curbuf (current-buffer))) + (when (eq visit t) + (set-visited-file-modtime))))) ;; Internal file name functions @@ -652,51 +654,53 @@ ;; Share names of a host are cached. It is very unlikely that the ;; shares do change during connection. -(defun tramp-smb-get-file-entries (user host share localname) - "Read entries which match LOCALNAME. +(defun tramp-smb-get-file-entries (directory) + "Read entries which match DIRECTORY. Either the shares are listed, or the `dir' command is executed. -Only entries matching the localname are returned. Result is a list of (LOCALNAME MODE SIZE MONTH DAY TIME YEAR)." - (save-excursion - (save-match-data - (let ((base (or (and (> (length localname) 0) - (string-match "\\([^/]+\\)$" localname) - (regexp-quote (match-string 1 localname))) - "")) - res entry) - (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) - (if (and (not share) tramp-smb-share-cache) - ;; Return cached shares - (setq res tramp-smb-share-cache) - ;; Read entries - (tramp-smb-maybe-open-connection user host share) - (when share - (tramp-smb-send-command - user host - (format "dir %s" - (if (zerop (length localname)) "" (concat "\"" localname "*\""))))) - (goto-char (point-min)) - ;; Loop the listing - (unless (re-search-forward tramp-smb-errors nil t) - (while (not (eobp)) - (setq entry (tramp-smb-read-file-entry share)) - (forward-line) - (when entry (add-to-list 'res entry)))) - (unless share + (with-parsed-tramp-file-name directory nil + (setq localname (or localname "/")) + (with-file-property v localname "file-entries" + (with-current-buffer (tramp-get-buffer v) + (let* ((share (tramp-smb-get-share localname)) + (file (tramp-smb-get-localname localname nil)) + (cache (tramp-get-connection-property v "share-cache" nil)) + res entry) + + (if (and (not share) cache) + ;; Return cached shares + (setq res cache) + + ;; Read entries + (setq file (file-name-as-directory file)) + (when (string-match "^\\./" file) + (setq file (substring file 1))) + (if share + (tramp-smb-send-command v (format "dir \"%s*\"" file)) + ;; `tramp-smb-maybe-open-connection' lists also the share names + (tramp-smb-maybe-open-connection v)) + + ;; Loop the listing + (goto-char (point-min)) + (unless (re-search-forward tramp-smb-errors nil t) + (while (not (eobp)) + (setq entry (tramp-smb-read-file-entry share)) + (forward-line) + (when entry (add-to-list 'res entry)))) + ;; Cache share entries - (setq tramp-smb-share-cache res))) + (unless share + (tramp-set-connection-property v "share-cache" res))) - ;; Add directory itself - (add-to-list 'res '("" "drwxrwxrwx" 0 (0 0))) + ;; Add directory itself + (add-to-list 'res '("" "drwxrwxrwx" 0 (0 0))) - ;; There's a very strange error (debugged with XEmacs 21.4.14) - ;; If there's no short delay, it returns nil. No idea about - (when (featurep 'xemacs) (sleep-for 0.01)) + ;; There's a very strange error (debugged with XEmacs 21.4.14) + ;; If there's no short delay, it returns nil. No idea about. + (when (featurep 'xemacs) (sleep-for 0.01)) - ;; Check for matching entries - (delq nil (mapcar - (lambda (x) (and (string-match base (nth 0 x)) x)) - res)))))) + ;; Return entries + (delq nil res)))))) ;; Return either a share name (if SHARE is nil), or a file name ;; @@ -721,7 +725,7 @@ ;; \s- - space delimeter ;; \w\{3,3\} - month ;; \s- - space delimeter -;; [ 19][0-9] - day +;; [ 12][0-9] - day ;; \s- - space delimeter ;; [0-9]\{2,2\}:[0-9]\{2,2\}:[0-9]\{2,2\} - time ;; \s- - space delimeter @@ -756,18 +760,20 @@ "Parse entry in SMB output buffer. If SHARE is result, entries are of type dir. Otherwise, shares are listed. Result is the list (LOCALNAME MODE SIZE MTIME)." - (let ((line (buffer-substring (point) (tramp-point-at-eol))) +;; We are called from `tramp-smb-get-file-entries', which sets the +;; current buffer. + (let ((line (buffer-substring (point) (tramp-line-end-position))) localname mode size month day hour min sec year mtime) (if (not share) - ; Read share entries - (when (string-match "^\\s-+\\(\\S-+\\)\\s-+Disk" line) + ;; Read share entries. + (when (string-match "^\\s-+\\(\\S-\\(.*\\S-\\)?\\)\\s-+Disk" line) (setq localname (match-string 1 line) mode "dr-xr-xr-x" size 0)) - ; Real listing + ;; Real listing. (block nil ;; year @@ -833,219 +839,186 @@ (if (and sec min hour day month year) (encode-time sec min hour day - (cdr (assoc (downcase month) tramp-smb-parse-time-months)) + (cdr (assoc (downcase month) tramp-parse-time-months)) year) '(0 0))) (list localname mode size mtime)))) -;; Inodes don't exist for SMB files. Therefore we must generate virtual ones. -;; Used in `find-buffer-visiting'. -;; The method applied might be not so efficient (Ange-FTP uses hashes). But -;; performance isn't the major issue given that file transfer will take time. - -(defun tramp-smb-get-inode (share file) - "Returns the virtual inode number. -If it doesn't exist, generate a new one." - (let ((string (concat share "/" (directory-file-name file)))) - (unless (assoc string tramp-smb-inodes) - (add-to-list 'tramp-smb-inodes - (list string (length tramp-smb-inodes)))) - (nth 1 (assoc string tramp-smb-inodes)))) - ;; Connection functions -(defun tramp-smb-send-command (user host command) - "Send the COMMAND to USER at HOST (logged into an SMB session). -Erases temporary buffer before sending the command. Returns nil if -there has been an error message from smbclient." - (save-excursion - (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) - (erase-buffer) - (tramp-send-command nil tramp-smb-method user host command nil t) - (tramp-smb-wait-for-output user host))) +(defun tramp-smb-send-command (vec command) + "Send the COMMAND to connection VEC. +Returns nil if there has been an error message from smbclient." + (tramp-smb-maybe-open-connection vec) + (tramp-message vec 6 "%s" command) + (tramp-send-string vec command) + (tramp-smb-wait-for-output vec)) -(defun tramp-smb-maybe-open-connection (user host share) - "Maybe open a connection to HOST, logging in as USER, using `tramp-smb-program'. +(defun tramp-smb-maybe-open-connection (vec) + "Maybe open a connection to HOST, log in as USER, using `tramp-smb-program'. Does not do anything if a connection is already open, but re-opens the connection if a previous connection has died for some reason." - (let ((process-connection-type tramp-process-connection-type) - (p (get-buffer-process - (tramp-get-buffer nil tramp-smb-method user host)))) - (save-excursion - (set-buffer (tramp-get-buffer nil tramp-smb-method user host)) - ;; Check whether it is still the same share - (unless (and p (processp p) (string-equal tramp-smb-share share)) - (when (and p (processp p)) - (delete-process p) - (setq p nil))) - ;; If too much time has passed since last command was sent, look - ;; whether process is still alive. If it isn't, kill it. - (when (and tramp-last-cmd-time - (> (tramp-time-diff (current-time) tramp-last-cmd-time) 60) - p (processp p) (memq (process-status p) '(run open))) - (unless (and p (processp p) (memq (process-status p) '(run open))) - (delete-process p) - (setq p nil)))) - (unless (and p (processp p) (memq (process-status p) '(run open))) - (when (and p (processp p)) - (delete-process p)) - (tramp-smb-open-connection user host share)))) + (let* ((share (tramp-smb-get-share (tramp-file-name-localname vec))) + (buf (tramp-get-buffer vec)) + (p (get-buffer-process buf))) + + ;; If too much time has passed since last command was sent, look + ;; whether has been an error message; maybe due to connection timeout. + (with-current-buffer buf + (goto-char (point-min)) + (when (and (> (tramp-time-diff + (current-time) + (tramp-get-connection-property + p "last-cmd-time" '(0 0 0))) + 60) + p (processp p) (memq (process-status p) '(run open)) + (re-search-forward tramp-smb-errors nil t)) + (delete-process p) + (setq p nil))) + + ;; Check whether it is still the same share. + (unless + (and p (processp p) (memq (process-status p) '(run open)) + (string-equal + share + (tramp-get-connection-property p "smb-share" ""))) -(defun tramp-smb-open-connection (user host share) - "Open a connection using `tramp-smb-program'. -This starts the command `smbclient //HOST/SHARE -U USER', then waits -for a remote password prompt. It queries the user for the password, -then sends the password to the remote host. + (save-match-data + ;; There might be unread output from checking for share names. + (when buf (with-current-buffer buf (erase-buffer))) + (when (and p (processp p)) (delete-process p)) -Domain names in USER and port numbers in HOST are acknowledged." + (unless (let ((default-directory + (tramp-temporary-file-directory))) + (executable-find tramp-smb-program)) + (error "Cannot find command %s in %s" tramp-smb-program exec-path)) - (when (and (fboundp 'executable-find) - (not (funcall 'executable-find tramp-smb-program))) - (error "Cannot find command %s in %s" tramp-smb-program exec-path)) + (let* ((user (tramp-file-name-user vec)) + (host (tramp-file-name-host vec)) + (real-user user) + (real-host host) + domain port args) - (save-match-data - (let* ((buffer (tramp-get-buffer nil tramp-smb-method user host)) - (real-user user) - (real-host host) - domain port args) + ;; Check for domain ("user%domain") and port ("host#port"). + (when (and user (string-match "\\(.+\\)%\\(.+\\)" user)) + (setq real-user (or (match-string 1 user) user) + domain (match-string 2 user))) - ; Check for domain ("user%domain") and port ("host#port") - (when (and user (string-match "\\(.+\\)%\\(.+\\)" user)) - (setq real-user (or (match-string 1 user) user) - domain (match-string 2 user))) + (when (and host (string-match "\\(.+\\)#\\(.+\\)" host)) + (setq real-host (or (match-string 1 host) host) + port (match-string 2 host))) - (when (and host (string-match "\\(.+\\)#\\(.+\\)" host)) - (setq real-host (or (match-string 1 host) host) - port (match-string 2 host))) + (if share + (setq args (list (concat "//" real-host "/" share))) + (setq args (list "-L" real-host ))) - (if share - (setq args (list (concat "//" real-host "/" share))) - (setq args (list "-L" real-host ))) + (if (not (zerop (length real-user))) + (setq args (append args (list "-U" real-user))) + (setq args (append args (list "-N")))) + + (when domain (setq args (append args (list "-W" domain)))) + (when port (setq args (append args (list "-p" port)))) + (setq args (append args (list "-s" "/dev/null"))) - (if real-user - (setq args (append args (list "-U" real-user))) - (setq args (append args (list "-N")))) + ;; OK, let's go. + (tramp-message + vec 3 "Opening connection for //%s%s/%s..." + (if (not (zerop (length user))) (concat user "@") "") + host (or share "")) - (when domain (setq args (append args (list "-W" domain)))) - (when port (setq args (append args (list "-p" port)))) - - ; OK, let's go - (tramp-pre-connection nil tramp-smb-method user host tramp-chunksize) - (tramp-message 7 "Opening connection for //%s@%s/%s..." - user host (or share "")) + (let* ((coding-system-for-read nil) + (process-connection-type tramp-process-connection-type) + (p (let ((default-directory (tramp-temporary-file-directory))) + (apply #'start-process + (tramp-buffer-name vec) (tramp-get-buffer vec) + tramp-smb-program args)))) - (let* ((default-directory (tramp-temporary-file-directory)) - ;; If we omit the conditional here, then we would use - ;; `undecided-dos' in some cases. With the conditional, - ;; we use nil in these cases. Which one is right? - (coding-system-for-read (unless (and (not (featurep 'xemacs)) - (> emacs-major-version 20)) - tramp-dos-coding-system)) - (p (apply #'start-process (buffer-name buffer) buffer - tramp-smb-program args))) + (tramp-message + vec 6 "%s" (mapconcat 'identity (process-command p) " ")) + (set-process-sentinel p 'tramp-flush-connection-property) + (tramp-set-process-query-on-exit-flag p nil) + (tramp-set-connection-property p "smb-share" share) + + ;; Set variables for computing the prompt for reading password. + (setq tramp-current-method tramp-smb-method + tramp-current-user user + tramp-current-host host) - (tramp-message 9 "Started process %s" (process-command p)) - (tramp-set-process-query-on-exit-flag p nil) - (set-buffer buffer) - (setq tramp-smb-share share) + ;; Set chunksize. Otherwise, `tramp-send-string' might + ;; try it itself. + (tramp-set-connection-property p "chunksize" tramp-chunksize) - ; send password - (when real-user - (let ((pw-prompt "Password:")) - (tramp-message 9 "Sending password") - (tramp-enter-password p pw-prompt user host))) + ;; Play login scenario. + (tramp-process-actions + p vec + (if share + tramp-smb-actions-with-share + tramp-smb-actions-without-share)) - (unless (tramp-smb-wait-for-output user host) - (tramp-clear-passwd user host) - (error "Cannot open connection //%s@%s/%s" - user host (or share ""))))))) + (tramp-message + vec 3 "Opening connection for //%s%s/%s...done" + (if (not (zerop (length user))) (concat user "@") "") + host (or share "")))))))) ;; We don't use timeouts. If needed, the caller shall wrap around. -(defun tramp-smb-wait-for-output (user host) +(defun tramp-smb-wait-for-output (vec) "Wait for output from smbclient command. Returns nil if an error message has appeared." - (let ((proc (get-buffer-process (current-buffer))) - (found (progn (goto-char (point-min)) - (re-search-forward tramp-smb-prompt nil t))) - (err (progn (goto-char (point-min)) - (re-search-forward tramp-smb-errors nil t)))) - - ;; Algorithm: get waiting output. See if last line contains - ;; tramp-smb-prompt sentinel or tramp-smb-errors strings. - ;; If not, wait a bit and again get waiting output. - (while (not found) - - ;; Accept pending output. - (tramp-accept-process-output proc) + (with-current-buffer (tramp-get-buffer vec) + (let ((p (get-buffer-process (current-buffer))) + (found (progn (goto-char (point-min)) + (re-search-forward tramp-smb-prompt nil t))) + (err (progn (goto-char (point-min)) + (re-search-forward tramp-smb-errors nil t)))) - ;; Search for prompt. - (goto-char (point-min)) - (setq found (re-search-forward tramp-smb-prompt nil t)) + ;; Algorithm: get waiting output. See if last line contains + ;; tramp-smb-prompt sentinel or tramp-smb-errors strings. + ;; If not, wait a bit and again get waiting output. + (while (and (not found) (not err)) - ;; Search for errors. - (goto-char (point-min)) - (setq err (re-search-forward tramp-smb-errors nil t))) - - ;; Add output to debug buffer if appropriate. - (when tramp-debug-buffer - (append-to-buffer - (tramp-get-debug-buffer nil tramp-smb-method user host) - (point-min) (point-max))) + ;; Accept pending output. + (tramp-accept-process-output p) - ;; Return value is whether no error message has appeared. - (not err))) - - -;; Snarfed code from time-date.el and parse-time.el + ;; Search for prompt. + (goto-char (point-min)) + (setq found (re-search-forward tramp-smb-prompt nil t)) -(defconst tramp-smb-half-a-year '(241 17024) -"Evaluated by \"(days-to-time 183)\".") + ;; Search for errors. + (goto-char (point-min)) + (setq err (re-search-forward tramp-smb-errors nil t))) -(defconst tramp-smb-parse-time-months '(("jan" . 1) ("feb" . 2) ("mar" . 3) - ("apr" . 4) ("may" . 5) ("jun" . 6) - ("jul" . 7) ("aug" . 8) ("sep" . 9) - ("oct" . 10) ("nov" . 11) ("dec" . 12)) -"Alist mapping month names to integers.") + ;; When the process is still alive, read pending output. + (while (and (not found) (memq (process-status p) '(run open))) -(defun tramp-smb-time-less-p (t1 t2) - "Say whether time value T1 is less than time value T2." - (unless t1 (setq t1 '(0 0))) - (unless t2 (setq t2 '(0 0))) - (or (< (car t1) (car t2)) - (and (= (car t1) (car t2)) - (< (nth 1 t1) (nth 1 t2))))) + ;; Accept pending output. + (tramp-accept-process-output p) -(defun tramp-smb-time-subtract (t1 t2) - "Subtract two time values. -Return the difference in the format of a time value." - (unless t1 (setq t1 '(0 0))) - (unless t2 (setq t2 '(0 0))) - (let ((borrow (< (cadr t1) (cadr t2)))) - (list (- (car t1) (car t2) (if borrow 1 0)) - (- (+ (if borrow 65536 0) (cadr t1)) (cadr t2))))) + ;; Search for prompt. + (goto-char (point-min)) + (setq found (re-search-forward tramp-smb-prompt nil t))) + + ;; Return value is whether no error message has appeared. + (tramp-message vec 6 "\n%s" (buffer-string)) + (not err)))) (provide 'tramp-smb) ;;; TODO: -;; * Provide a local smb.conf. The default one might not be readable. ;; * Error handling in case password is wrong. ;; * Read password from "~/.netrc". ;; * Return more comprehensive file permission string. Think whether it is ;; possible to implement `set-file-modes'. -;; * Handle WILDCARD and FULL-DIRECTORY-P in -;; `tramp-smb-handle-insert-directory'. ;; * Handle links (FILENAME.LNK). ;; * Maybe local tmp files should have the same extension like the original ;; files. Strange behaviour with jka-compr otherwise? -;; * Copy files in dired from SMB to another method doesn't work. ;; * Try to remove the inclusion of dummy "" directory. Seems to be at ;; several places, especially in `tramp-smb-handle-insert-directory'. -;; * Provide variables for debug. ;; * (RMS) Use unwind-protect to clean up the state so as to make the state ;; regular again. +;; * Make it multi-hop capable. ;;; arch-tag: fcc9dbec-7503-4d73-b638-3c8aa59575f5 ;;; tramp-smb.el ends here diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp-util.el --- a/lisp/net/tramp-util.el Tue Jul 10 02:07:45 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -;;; -*- coding: iso-2022-7bit; -*- -;;; tramp-util.el --- Misc utility functions to use with Tramp - -;; Copyright (C) 2001, 2002, 2003, 2004, 2005, -;; 2006, 2007 Free Software Foundation, Inc. - -;; Author: kai.grossjohann@gmx.net -;; Keywords: comm, extensions, processes - -;; This file is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. - -;; This file is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to -;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. - -;;; Commentary: - -;; Some misc. utility functions that might go nicely with Tramp. -;; Mostly, these are kluges awaiting real solutions later on. - -;;; Code: - -(require 'compile) -(require 'tramp) -(add-hook 'tramp-util-unload-hook - '(lambda () - (when (featurep 'tramp) - (unload-feature 'tramp 'force)))) - -;; Define a Tramp minor mode. It's intention is to redefine some keys for Tramp -;; specific functions, like compilation. -;; The key remapping works since Emacs 22 only. Unknown for XEmacs. - -;; Pacify byte-compiler -(eval-when-compile - (unless (fboundp 'define-minor-mode) - (defalias 'define-minor-mode 'identity) - (defvar tramp-minor-mode)) - (unless (featurep 'xemacs) - (defalias 'add-menu-button 'ignore))) - -(defvar tramp-minor-mode-map (make-sparse-keymap) - "Keymap for Tramp minor mode.") - -(define-minor-mode tramp-minor-mode "Tramp minor mode for utility functions." - :group 'tramp - :global nil - :init-value nil - :lighter " Tramp" - :keymap tramp-minor-mode-map - (setq tramp-minor-mode - (and tramp-minor-mode (tramp-tramp-file-p default-directory)))) - -(add-hook 'find-file-hooks 'tramp-minor-mode t) -(add-hook 'tramp-util-unload-hook - '(lambda () - (remove-hook 'find-file-hooks 'tramp-minor-mode))) - -(add-hook 'dired-mode-hook 'tramp-minor-mode t) -(add-hook 'tramp-util-unload-hook - '(lambda () - (remove-hook 'dired-mode-hook 'tramp-minor-mode))) - -(defun tramp-remap-command (old-command new-command) - "Replaces bindings of OLD-COMMAND by NEW-COMMAND. -If remapping functionality for keymaps is defined, this happens for all -bindings. Otherwise, only bindings active during invocation are taken -into account. XEmacs menubar bindings are not changed by this." - (if (functionp 'command-remapping) - ;; Emacs 22 - (eval - `(define-key tramp-minor-mode-map [remap ,old-command] new-command)) - ;; previous Emacs versions. - (mapcar - '(lambda (x) - (define-key tramp-minor-mode-map x new-command)) - (where-is-internal old-command)))) - -(tramp-remap-command 'compile 'tramp-compile) -(tramp-remap-command 'recompile 'tramp-recompile) - -;; XEmacs has an own mimic for menu entries -(when (fboundp 'add-menu-button) - (funcall 'add-menu-button - '("Tools" "Compile") - ["Compile..." - (command-execute (if tramp-minor-mode 'tramp-compile 'compile)) - :active (fboundp 'compile)]) - (funcall 'add-menu-button - '("Tools" "Compile") - ["Repeat Compilation" - (command-execute (if tramp-minor-mode 'tramp-recompile 'recompile)) - :active (fboundp 'compile)])) - -;; Utility functions. - -(defun tramp-compile (command) - "Compile on remote host." - (interactive - (if (or compilation-read-command current-prefix-arg) - (list (read-from-minibuffer "Compile command: " - compile-command nil nil - '(compile-history . 1))) - (list compile-command))) - (setq compile-command command) - (save-some-buffers (not compilation-ask-about-save) nil) - (let ((d default-directory)) - (save-excursion - (pop-to-buffer (get-buffer-create "*Compilation*") t) - (erase-buffer) - (setq default-directory d))) - (tramp-handle-shell-command command (get-buffer "*Compilation*")) - (pop-to-buffer (get-buffer "*Compilation*")) - (tramp-minor-mode 1) - (compilation-minor-mode 1)) - -(defun tramp-recompile () - "Re-compile on remote host." - (interactive) - (save-some-buffers (not compilation-ask-about-save) nil) - (tramp-handle-shell-command compile-command (get-buffer "*Compilation*")) - (pop-to-buffer (get-buffer "*Compilation*")) - (tramp-minor-mode 1) - (compilation-minor-mode 1)) - -(provide 'tramp-util) - -;;; arch-tag: 500f9992-a44e-46d0-83a7-980799251808 -;;; tramp-util.el ends here diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp-uu.el --- a/lisp/net/tramp-uu.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/net/tramp-uu.el Sun Jul 15 04:47:46 2007 +0000 @@ -9,8 +9,8 @@ ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -18,9 +18,8 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to -;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs; see the file COPYING. If not, see +;; . ;;; Commentary: diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp-vc.el --- a/lisp/net/tramp-vc.el Tue Jul 10 02:07:45 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,536 +0,0 @@ -;;; tramp-vc.el --- Version control integration for TRAMP.el - -;; Copyright (C) 2000, 2001, 2002, 2003, 2004, -;; 2005, 2006, 2007 Free Software Foundation, Inc. - -;; Author: Daniel Pittman -;; Keywords: comm, processes - -;; This file is part of GNU Emacs. - -;; GNU Emacs is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. - -;;; Commentary: - -;; See the main module, 'tramp.el' for discussion of the purpose of TRAMP. -;; This module provides integration between remote files accessed by TRAMP and -;; the Emacs version control system. - -;;; Code: - -(require 'vc) -;; Old VC defines vc-rcs-release in vc.el, new VC requires extra module. -(unless (boundp 'vc-rcs-release) - (require 'vc-rcs)) -(require 'tramp) - -;; Avoid byte-compiler warnings if the byte-compiler supports this. -;; Currently, XEmacs supports this. -(eval-when-compile - (when (fboundp 'byte-compiler-options) - (let (unused-vars) ; Pacify Emacs byte-compiler - (defalias 'warnings 'identity) ; Pacify Emacs byte-compiler - (byte-compiler-options (warnings (- unused-vars)))))) - -;; -- vc -- - -;; This used to blow away the file-name-handler-alist and reinstall -;; TRAMP into it. This was intended to let VC work remotely. It didn't, -;; at least not in my XEmacs 21.2 install. -;; -;; In any case, tramp-run-real-handler now deals correctly with disabling -;; the things that should be, making this a no-op. -;; -;; I have removed it from the tramp-file-name-handler-alist because the -;; shortened version does nothing. This is for reference only now. -;; -;; Daniel Pittman -;; -;; (defun tramp-handle-vc-registered (file) -;; "Like `vc-registered' for tramp files." -;; (tramp-run-real-handler 'vc-registered (list file))) - -;; `vc-do-command' -;; This function does not deal well with remote files, so we define -;; our own version and make a backup of the original function and -;; call our version for tramp files and the original version for -;; normal files. - -;; The following function is pretty much copied from vc.el, but -;; the part that actually executes a command is changed. -;; CCC: this probably works for Emacs 21, too. -(defun tramp-vc-do-command (buffer okstatus command file last &rest flags) - "Like `vc-do-command' but invoked for tramp files. -See `vc-do-command' for more information." - (save-match-data - (and file (setq file (expand-file-name file))) - (if (not buffer) (setq buffer "*vc*")) - (if vc-command-messages - (message "Running `%s' on `%s'..." command file)) - (let ((obuf (current-buffer)) (camefrom (current-buffer)) - (squeezed nil) - (olddir default-directory) - vc-file status) - (let* ((v (tramp-dissect-file-name (expand-file-name file))) - (multi-method (tramp-file-name-multi-method v)) - (method (tramp-file-name-method v)) - (user (tramp-file-name-user v)) - (host (tramp-file-name-host v)) - (localname (tramp-file-name-localname v))) - (set-buffer (get-buffer-create buffer)) - (set (make-local-variable 'vc-parent-buffer) camefrom) - (set (make-local-variable 'vc-parent-buffer-name) - (concat " from " (buffer-name camefrom))) - (setq default-directory olddir) - - (erase-buffer) - - (mapcar - (function - (lambda (s) (and s (setq squeezed (append squeezed (list s)))))) - flags) - (if (and (eq last 'MASTER) file - (setq vc-file (vc-name file))) - (setq squeezed - (append squeezed - (list (tramp-file-name-localname - (tramp-dissect-file-name vc-file)))))) - (if (and file (eq last 'WORKFILE)) - (progn - (let* ((pwd (expand-file-name default-directory)) - (preflen (length pwd))) - (if (string= (substring file 0 preflen) pwd) - (setq file (substring file preflen)))) - (setq squeezed (append squeezed (list file))))) - ;; Unless we (save-window-excursion) the layout of windows in - ;; the current frame changes. This is painful, at best. - ;; - ;; As a point of note, (save-excursion) is still here only because - ;; it preserves (point) in the current buffer. (save-window-excursion) - ;; does not, at least under XEmacs 21.2. - ;; - ;; I trust that the FSF support this as well. I can't find useful - ;; documentation to check :( - ;; - ;; Daniel Pittman - (save-excursion - (save-window-excursion - ;; Actually execute remote command - ;; `shell-command' cannot be used; it isn't magic in XEmacs. - (tramp-handle-shell-command - (mapconcat 'tramp-shell-quote-argument - (cons command squeezed) " ") t) - ;;(tramp-wait-for-output) - ;; Get status from command - (tramp-send-command multi-method method user host "echo $?") - (tramp-wait-for-output) - ;; Make sure to get status from last line of output. - (goto-char (point-max)) (forward-line -1) - (setq status (read (current-buffer))) - (message "Command %s returned status %d." command status))) - (goto-char (point-max)) - (set-buffer-modified-p nil) - (forward-line -1) - (if (or (not (integerp status)) - (and (integerp okstatus) (< okstatus status))) - (progn - (pop-to-buffer buffer) - (goto-char (point-min)) - (shrink-window-if-larger-than-buffer) - (error "Running `%s'...FAILED (%s)" command - (if (integerp status) - (format "status %d" status) - status)) - ) - (if vc-command-messages - (message "Running %s...OK" command)) - ) - (set-buffer obuf) - status)) - )) - -;; Following code snarfed from Emacs 21 vc.el and slightly tweaked. -(defun tramp-vc-do-command-new (buffer okstatus command file &rest flags) - "Like `vc-do-command' but for TRAMP files. -This function is for the new VC which comes with Emacs 21. -Since TRAMP doesn't do async commands yet, this function doesn't, either." - (and file (setq file (expand-file-name file))) - (if vc-command-messages - (message "Running %s on %s..." command file)) - (save-current-buffer - (unless (eq buffer t) - ; Pacify byte-compiler - (funcall (symbol-function 'vc-setup-buffer) buffer)) - (let ((squeezed nil) - (inhibit-read-only t) - (status 0)) - (let* ((v (when file (tramp-dissect-file-name file))) - (multi-method (when file (tramp-file-name-multi-method v))) - (method (when file (tramp-file-name-method v))) - (user (when file (tramp-file-name-user v))) - (host (when file (tramp-file-name-host v))) - (localname (when file (tramp-file-name-localname v)))) - (setq squeezed (delq nil (copy-sequence flags))) - (when file - (setq squeezed (append squeezed (list (file-relative-name - file default-directory))))) - (let ((w32-quote-process-args t)) - (when (eq okstatus 'async) - (message "Tramp doesn't do async commands, running synchronously.")) - ;; `shell-command' cannot be used; it isn't magic in XEmacs. - (setq status (tramp-handle-shell-command - (mapconcat 'tramp-shell-quote-argument - (cons command squeezed) " ") t)) - (when (or (not (integerp status)) - (and (integerp okstatus) (< okstatus status))) - (pop-to-buffer (current-buffer)) - (goto-char (point-min)) - (shrink-window-if-larger-than-buffer) - (error "Running %s...FAILED (%s)" command - (if (integerp status) (format "status %d" status) status)))) - (if vc-command-messages - (message "Running %s...OK" command)) - ; Pacify byte-compiler - (funcall (symbol-function 'vc-exec-after) - `(run-hook-with-args - 'vc-post-command-functions ',command ',localname ',flags)) - status)))) - - -;; The context for a VC command is the current buffer. -;; That makes a test on the buffers file more reliable than a test on the -;; arguments. -;; This is needed to handle remote VC correctly - else we test against the -;; local VC system and get things wrong... -;; Daniel Pittman -;;-(if (fboundp 'vc-call-backend) -;;- () ;; This is the new VC for which we don't have an appropriate advice yet -;;-) -(unless (fboundp 'process-file) - (if (fboundp 'vc-call-backend) - (defadvice vc-do-command - (around tramp-advice-vc-do-command - (buffer okstatus command file &rest flags) - activate) - "Invoke tramp-vc-do-command for tramp files." - (let ((file (symbol-value 'file))) ;pacify byte-compiler - (if (or (and (stringp file) (tramp-tramp-file-p file)) - (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) - (setq ad-return-value - (apply 'tramp-vc-do-command-new buffer okstatus command - file ;(or file (buffer-file-name)) - flags)) - ad-do-it))) - (defadvice vc-do-command - (around tramp-advice-vc-do-command - (buffer okstatus command file last &rest flags) - activate) - "Invoke tramp-vc-do-command for tramp files." - (let ((file (symbol-value 'file))) ;pacify byte-compiler - (if (or (and (stringp file) (tramp-tramp-file-p file)) - (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) - (setq ad-return-value - (apply 'tramp-vc-do-command buffer okstatus command - (or file (buffer-file-name)) last flags)) - ad-do-it)))) - - (add-hook 'tramp-unload-hook - '(lambda () (ad-unadvise 'vc-do-command)))) - - -;; XEmacs uses this to do some of its work. Like vc-do-command, we -;; need to enhance it to make VC work via TRAMP-mode. -;; -;; Like the previous function, this is a cut-and-paste job from the VC -;; file. It's based on the vc-do-command code. -;; CCC: this isn't used in Emacs 21, so do as before. -(defun tramp-vc-simple-command (okstatus command file &rest args) - ;; Simple version of vc-do-command, for use in vc-hooks only. - ;; Don't switch to the *vc-info* buffer before running the - ;; command, because that would change its default directory - (save-match-data - (let* ((v (tramp-dissect-file-name (expand-file-name file))) - (multi-method (tramp-file-name-multi-method v)) - (method (tramp-file-name-method v)) - (user (tramp-file-name-user v)) - (host (tramp-file-name-host v)) - (localname (tramp-file-name-localname v))) - (save-excursion (set-buffer (get-buffer-create "*vc-info*")) - (erase-buffer)) - (let ((exec-path (append vc-path exec-path)) exec-status - ;; Add vc-path to PATH for the execution of this command. - (process-environment - (cons (concat "PATH=" (getenv "PATH") - path-separator - (mapconcat 'identity vc-path path-separator)) - process-environment))) - ;; Call the actual process. See tramp-vc-do-command for discussion of - ;; why this does both (save-window-excursion) and (save-excursion). - ;; - ;; As a note, I don't think that the process-environment stuff above - ;; has any effect on the remote system. This is a hard one though as - ;; there is no real reason to expect local and remote paths to be - ;; identical... - ;; - ;; Daniel Pittman - (save-excursion - (save-window-excursion - ;; Actually execute remote command - ;; `shell-command' cannot be used; it isn't magic in XEmacs. - (tramp-handle-shell-command - (mapconcat 'tramp-shell-quote-argument - (append (list command) args (list localname)) " ") - (get-buffer-create"*vc-info*")) - ;(tramp-wait-for-output) - ;; Get status from command - (tramp-send-command multi-method method user host "echo $?") - (tramp-wait-for-output) - (setq exec-status (read (current-buffer))) - (message "Command %s returned status %d." command exec-status))) - - ;; Maybe okstatus can be `async' here. But then, maybe the - ;; async thing is new in Emacs 21, but this function is only - ;; used in Emacs 20. - (cond ((> exec-status okstatus) - (switch-to-buffer (get-file-buffer file)) - (shrink-window-if-larger-than-buffer - (display-buffer "*vc-info*")) - (error "Couldn't find version control information"))) - exec-status)))) - -;; This function does not exist any more in Emacs-21's VC -(defadvice vc-simple-command - (around tramp-advice-vc-simple-command - (okstatus command file &rest args) - activate) - "Invoke tramp-vc-simple-command for tramp files." - (let ((file (symbol-value 'file))) ;pacify byte-compiler - (if (or (and (stringp file) (tramp-tramp-file-p file)) - (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) - (setq ad-return-value - (apply 'tramp-vc-simple-command okstatus command - (or file (buffer-file-name)) args)) - ad-do-it))) - -(add-hook 'tramp-unload-hook - '(lambda () (ad-unadvise 'vc-simple-command))) - - -;; `vc-workfile-unchanged-p' -;; This function does not deal well with remote files, so we do the -;; same as for `vc-do-command'. - -;; `vc-workfile-unchanged-p' checks the modification time, we cannot -;; do that for remote files, so here's a version which relies on diff. -;; CCC: this one probably works for Emacs 21, too. -(defun tramp-vc-workfile-unchanged-p - (filename &optional want-differences-if-changed) - (if (fboundp 'vc-backend-diff) - ;; Old VC. Call `vc-backend-diff'. - (let ((status (funcall (symbol-function 'vc-backend-diff) - filename nil nil - (not want-differences-if-changed)))) - (zerop status)) - ;; New VC. Call `vc-default-workfile-unchanged-p'. - (funcall (symbol-function 'vc-default-workfile-unchanged-p) - (vc-backend filename) filename))) - -(defadvice vc-workfile-unchanged-p - (around tramp-advice-vc-workfile-unchanged-p - (filename &optional want-differences-if-changed) - activate) - "Invoke tramp-vc-workfile-unchanged-p for tramp files." - (if (and (stringp filename) - (tramp-tramp-file-p filename) - (not - (let ((v (tramp-dissect-file-name filename))) - ;; The following check is probably to test whether - ;; file-attributes returns correct last modification - ;; times. This check needs to be changed. - (tramp-get-remote-perl (tramp-file-name-multi-method v) - (tramp-file-name-method v) - (tramp-file-name-user v) - (tramp-file-name-host v))))) - (setq ad-return-value - (tramp-vc-workfile-unchanged-p filename want-differences-if-changed)) - ad-do-it)) - -(add-hook 'tramp-unload-hook - '(lambda () (ad-unadvise 'vc-workfile-unchanged-p))) - - -;; Redefine a function from vc.el -- allow tramp files. -;; `save-match-data' seems not to be required -- it isn't in -;; the original version, either. -;; CCC: this might need some work -- how does the Emacs 21 version -;; work, anyway? Does it work over ange-ftp? Hm. -(if (not (fboundp 'vc-backend-checkout)) - () ;; our replacement won't work and is unnecessary anyway -(defun vc-checkout (filename &optional writable rev) - "Retrieve a copy of the latest version of the given file." - ;; If ftp is on this system and the name matches the ange-ftp format - ;; for a remote file, the user is trying something that won't work. - (funcall (symbol-function 'vc-backend-checkout) filename writable rev) - (vc-resynch-buffer filename t t)) -) - - -;; Do we need to advise the vc-user-login-name function anyway? -;; This will return the correct login name for the owner of a -;; file. It does not deal with the default remote user name... -;; -;; That is, when vc calls (vc-user-login-name), we return the -;; local login name, something that may be different to the remote -;; default. -;; -;; The remote VC operations will occur as the user that we logged -;; in with however - not always the same as the local user. -;; -;; In the end, I did advise the function. This is because, well, -;; the thing didn't work right otherwise ;) -;; -;; Daniel Pittman - -(defun tramp-handle-vc-user-login-name (&optional uid) - "Return the default user name on the remote machine. -Whenever VC calls this function, `file' is bound to the file name -in question. If no uid is provided or the uid is equal to the uid -owning the file, then we return the user name given in the file name. - -This should only be called when `file' is bound to the -filename we are thinking about..." - ;; Pacify byte-compiler; this symbol is bound in the calling - ;; function. CCC: Maybe it would be better to move the - ;; boundness-checking into this function? - (let* ((file (symbol-value 'file)) - (remote-uid - ;; With Emacs 22, `file-attributes' has got an optional parameter - ;; ID-FORMAT. Handle this case backwards compatible. - (if (and (functionp 'subr-arity) - (= 2 (cdr (funcall (symbol-function 'subr-arity) - (symbol-function 'file-attributes))))) - (nth 2 (file-attributes file 'integer)) - (nth 2 (file-attributes file))))) - (if (and uid (/= uid remote-uid)) - (error "tramp-handle-vc-user-login-name cannot map a uid to a name") - (let* ((v (tramp-dissect-file-name (expand-file-name file))) - (u (tramp-file-name-user v))) - (cond ((stringp u) u) - ((vectorp u) (elt u (1- (length u)))) - ((null u) (user-login-name)) - (t (error "tramp-handle-vc-user-login-name cannot cope!"))))))) - - -;; The following defadvice is no longer necessary after changes in VC -;; on 2006-01-25, Andre. - -(unless (fboundp 'process-file) - (defadvice vc-user-login-name - (around tramp-vc-user-login-name activate) - "Support for files on remote machines accessed by TRAMP." - ;; We rely on the fact that `file' is bound when this is called. - ;; This appears to be the case everywhere in vc.el and vc-hooks.el - ;; as of Emacs 20.5. - ;; - ;; With Emacs 22, the definition of `vc-user-login-name' has been - ;; changed. It doesn't need to be adviced any longer. - (let ((file (when (boundp 'file) - (symbol-value 'file)))) ;pacify byte-compiler - (or (and (stringp file) - (tramp-tramp-file-p file) ; tramp file - (setq ad-return-value - (save-match-data - (tramp-handle-vc-user-login-name uid)))) ; get the owner name - ad-do-it))) ; else call the original - - (add-hook 'tramp-unload-hook - '(lambda () (ad-unadvise 'vc-user-login-name)))) - - -;; Determine the name of the user owning a file. -(defun tramp-file-owner (filename) - "Return who owns FILE (user name, as a string)." - (let ((v (tramp-dissect-file-name - (expand-file-name filename)))) - (if (not (file-exists-p filename)) - nil ; file cannot be opened - ;; file exists, find out stuff - (save-excursion - (tramp-send-command - (tramp-file-name-multi-method v) (tramp-file-name-method v) - (tramp-file-name-user v) (tramp-file-name-host v) - (format "%s -Lld %s" - (tramp-get-ls-command (tramp-file-name-multi-method v) - (tramp-file-name-method v) - (tramp-file-name-user v) - (tramp-file-name-host v)) - (tramp-shell-quote-argument (tramp-file-name-localname v)))) - (tramp-wait-for-output) - ;; parse `ls -l' output ... - ;; ... file mode flags - (read (current-buffer)) - ;; ... number links - (read (current-buffer)) - ;; ... uid (as a string) - (symbol-name (read (current-buffer))))))) - -;; Wire ourselves into the VC infrastructure... -;; This function does not exist any more in Emacs-21's VC -;; CCC: it appears that no substitute is needed for Emacs 21. -(defadvice vc-file-owner - (around tramp-vc-file-owner activate) - "Support for files on remote machines accessed by TRAMP." - (let ((filename (ad-get-arg 0))) - (or (and (tramp-file-name-p filename) ; tramp file - (setq ad-return-value - (save-match-data - (tramp-file-owner filename)))) ; get the owner name - ad-do-it))) ; else call the original - -(add-hook 'tramp-unload-hook - '(lambda () (ad-unadvise 'vc-file-owner))) - - -;; We need to make the version control software backend version -;; information local to the current buffer. This is because each TRAMP -;; buffer can (theoretically) have a different VC version and I am -;; *way* too lazy to try and push the correct value into each new -;; buffer. -;; -;; Remote VC costs will just have to be paid, at least for the moment. -;; Well, at least, they will right until I feel guilty about doing a -;; botch job here and fix it. :/ -;; -;; Daniel Pittman -;; CCC: this is probably still needed for Emacs 21. -(defun tramp-vc-setup-for-remote () - "Make the backend release variables buffer local. -This makes remote VC work correctly at the cost of some processing time." - (when (and (buffer-file-name) - (tramp-tramp-file-p (buffer-file-name))) - (make-local-variable 'vc-rcs-release) - (setq vc-rcs-release nil))) - -(add-hook 'find-file-hooks 'tramp-vc-setup-for-remote t) -(add-hook 'tramp-unload-hook - '(lambda () - (remove-hook 'find-file-hooks 'tramp-vc-setup-for-remote))) - -;; No need to load this again if anyone asks. -(provide 'tramp-vc) - -;;; arch-tag: 27cc42ce-da19-468d-ad5c-a2690558db60 -;;; tramp-vc.el ends here diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/tramp.el --- a/lisp/net/tramp.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/net/tramp.el Sun Jul 15 04:47:46 2007 +0000 @@ -14,8 +14,8 @@ ;; GNU Emacs is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -23,9 +23,8 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs; see the file COPYING. If not, see +;; . ;;; Commentary: @@ -39,10 +38,9 @@ ;; Notes: ;; ----- ;; -;; This package only works for Emacs 20 and higher, and for XEmacs 21 -;; and higher. (XEmacs 20 is missing the `with-timeout' macro. Emacs -;; 19 is reported to have other problems. For XEmacs 21, you need the -;; package `fsf-compat' for the `with-timeout' macro.) +;; This package only works for Emacs 21.1 and higher, and for XEmacs 21.4 +;; and higher. For XEmacs 21, you need the package `fsf-compat' for +;; the `with-timeout' macro.) ;; ;; This version might not work with pre-Emacs 21 VC unless VC is ;; loaded before tramp.el. Could you please test this and tell me about @@ -74,6 +72,8 @@ (when (featurep 'trampver) (unload-feature 'trampver 'force)))) +(require 'custom) + (if (featurep 'xemacs) (require 'timer-funcs) (require 'timer)) @@ -85,15 +85,24 @@ (load "password" 'noerror) (require 'password nil 'noerror)) ;from No Gnus, also in tar ball -;; The explicit check is not necessary in Emacs, which provides the -;; feature even if implemented in C, but it appears to be necessary -;; in XEmacs. -(unless (and (fboundp 'base64-encode-region) - (fboundp 'base64-decode-region)) - (require 'base64)) ;for the mimencode methods (require 'shell) (require 'advice) +;; Requiring 'tramp-cache results in an endless loop. +(autoload 'tramp-get-file-property "tramp-cache") +(autoload 'tramp-set-file-property "tramp-cache") +(autoload 'tramp-flush-file-property "tramp-cache") +(autoload 'tramp-flush-directory-property "tramp-cache") +(autoload 'tramp-cache-print "tramp-cache") +(autoload 'tramp-get-connection-property "tramp-cache") +(autoload 'tramp-set-connection-property "tramp-cache") +(autoload 'tramp-flush-connection-property "tramp-cache") +(autoload 'tramp-parse-connection-properties "tramp-cache") +(add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-cache) + (unload-feature 'tramp-cache 'force)))) + (autoload 'tramp-uuencode-region "tramp-uu" "Implementation of `uuencode' in Lisp.") (add-hook 'tramp-unload-hook @@ -101,75 +110,85 @@ (when (featurep 'tramp-uu) (unload-feature 'tramp-uu 'force)))) -(unless (fboundp 'uudecode-decode-region) - (autoload 'uudecode-decode-region "uudecode")) - -;; XEmacs is distributed with few Lisp packages. Further packages are -;; installed using EFS. If we use a unified filename format, then -;; Tramp is required in addition to EFS. (But why can't Tramp just -;; disable EFS when Tramp is loaded? Then XEmacs can ship with EFS -;; just like before.) Another reason for using a separate filename -;; syntax on XEmacs is that EFS hooks into XEmacs in many places, but -;; Tramp only knows how to deal with `file-name-handler-alist', not -;; the other places. -;;;###autoload -(defvar tramp-unified-filenames (not (featurep 'xemacs)) - "Non-nil means to use unified Ange-FTP/Tramp filename syntax. -Otherwise, use a separate filename syntax for Tramp.") - -;; Load foreign methods. Because they do require Tramp internally, this -;; must be done with the `eval-after-load' trick. - -;; tramp-ftp supports Ange-FTP only. Not suited for XEmacs therefore. -(unless (featurep 'xemacs) - (eval-after-load "tramp" - '(progn - (require 'tramp-ftp) +(autoload 'uudecode-decode-region "uudecode") + +;; The following Tramp packages must be loaded after Tramp, because +;; they require Tramp as well. +(eval-after-load "tramp" + '(progn + + ;; Load foreign FTP method. + (let ((feature (if (featurep 'xemacs) 'tramp-efs 'tramp-ftp))) + (require feature) (add-hook 'tramp-unload-hook - '(lambda () - (when (featurep 'tramp-ftp) - (unload-feature 'tramp-ftp 'force))))))) -(when (and tramp-unified-filenames (featurep 'xemacs)) - (eval-after-load "tramp" - '(progn - (require 'tramp-efs) - (add-hook 'tramp-unload-hook - '(lambda () - (when (featurep 'tramp-efs) - (unload-feature 'tramp-efs 'force))))))) - -;; tramp-smb uses "smbclient" from Samba. -;; Not available under Cygwin and Windows, because they don't offer -;; "smbclient". And even not necessary there, because Emacs supports -;; UNC file names like "//host/share/localname". -(unless (memq system-type '(cygwin windows-nt)) - (eval-after-load "tramp" - '(progn + `(lambda () + (when (featurep ,feature) + (unload-feature ,feature 'force))))) + + ;; tramp-smb uses "smbclient" from Samba. Not available under + ;; Cygwin and Windows, because they don't offer "smbclient". And + ;; even not necessary there, because Emacs supports UNC file names + ;; like "//host/share/localname". + (unless (memq system-type '(cygwin windows-nt)) (require 'tramp-smb) (add-hook 'tramp-unload-hook '(lambda () (when (featurep 'tramp-smb) - (unload-feature 'tramp-smb 'force))))))) - -(require 'custom) - -(unless (boundp 'custom-print-functions) - (defvar custom-print-functions nil)) ; not autoloaded before Emacs 20.4 + (unload-feature 'tramp-smb 'force))))) + + ;; Load foreign FISH method. + (require 'tramp-fish) + (add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-fish) + (unload-feature 'tramp-fish 'force)))) + + ;; Load gateways. It needs `make-network-process' from Emacs 22. + (if (functionp 'make-network-process) + (progn + (require 'tramp-gw) + (add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-gw) + (unload-feature 'tramp-gw 'force))))) + ;; We need to declare used tramp-gw-* symbols at least. + (setq tramp-gw-tunnel-method "" + tramp-gw-socks-method "") + (defalias 'tramp-gw-open-connection 'ignore)) + + ;; tramp-util offers integration into other (X)Emacs packages like + ;; compile.el, gud.el etc. Not necessary in Emacs 23. + (unless (functionp 'start-file-process) + (require 'tramp-util) + (add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-util) + (unload-feature 'tramp-util 'force))))))) ;; Avoid byte-compiler warnings if the byte-compiler supports this. ;; Currently, XEmacs supports this. (eval-when-compile (when (featurep 'xemacs) - (let (unused-vars) ; Pacify Emacs byte-compiler - (defalias 'warnings 'identity) ; Pacify Emacs byte-compiler - (byte-compiler-options (warnings (- unused-vars)))))) + (byte-compiler-options (warnings (- unused-vars))))) + +;; `last-coding-system-used' is unknown in XEmacs. +(eval-when-compile + (unless (boundp 'last-coding-system-used) + (defvar last-coding-system-used nil))) ;; `directory-sep-char' is an obsolete variable in Emacs. But it is ;; used in XEmacs, so we set it here and there. The following is needed ;; to pacify Emacs byte-compiler. (eval-when-compile - (when (boundp 'byte-compile-not-obsolete-var) - (setq byte-compile-not-obsolete-var 'directory-sep-char))) + (unless (boundp 'byte-compile-not-obsolete-var) + (defvar byte-compile-not-obsolete-var nil)) + (setq byte-compile-not-obsolete-var 'directory-sep-char)) + +;; `with-temp-message' does not exists in XEmacs. +(eval-and-compile + (condition-case nil + (with-temp-message (current-message) nil) + (error (defmacro with-temp-message (message &rest body) `(progn ,@body))))) ;; `set-buffer-multibyte' comes from Emacs Leim. (eval-and-compile @@ -183,16 +202,23 @@ :group 'files :version "22.1") -(defcustom tramp-verbose 9 - "*Verbosity level for tramp.el. 0 means be silent, 10 is most verbose." +(defcustom tramp-verbose 3 + "*Verbosity level for tramp. +Any level x includes messages for all levels 1 .. x-1. The levels are + + 0 silent (no tramp messages at all) + 1 errors + 2 warnings + 3 connection to remote hosts (default level) + 4 activities + 5 internal + 6 sent and received strings + 7 file caching + 8 connection properties +10 traces (huge)." :group 'tramp :type 'integer) -(defcustom tramp-debug-buffer nil - "*Whether to send all commands and responses to a debug buffer." - :group 'tramp - :type 'boolean) - ;; Emacs case (eval-and-compile (when (boundp 'backup-directory-alist) @@ -201,7 +227,7 @@ Each element looks like (REGEXP . DIRECTORY), with the same meaning like in `backup-directory-alist'. If a Tramp file is backed up, and DIRECTORY is a local file name, the backup directory is prepended with Tramp file -name prefix \(multi-method, method, user, host\) of file. +name prefix \(method, user, host\) of file. \(setq tramp-backup-directory-alist backup-directory-alist\) @@ -220,7 +246,7 @@ It has the same meaning like `bkup-backup-directory-info' from package `backup-dir'. If a Tramp file is backed up, and BACKUP-DIR is a local file name, the backup directory is prepended with Tramp file name prefix -\(multi-method, method, user, host\) of file. +\(method, user, host\) of file. \(setq tramp-bkup-backup-directory-info bkup-backup-directory-info\) @@ -240,8 +266,7 @@ "*Put auto-save files in this directory, if set. The idea is to use a local directory so that auto-saving is faster." :group 'tramp - :type '(choice (const nil) - string)) + :type '(choice (const nil) string)) (defcustom tramp-encoding-shell (if (memq system-type '(windows-nt)) @@ -258,9 +283,7 @@ /bin/sh -c COMMAND < INPUT > OUTPUT This variable can be used to change the \"/bin/sh\" part. See the -variable `tramp-encoding-command-switch' for the \"-c\" part. Also, see the -variable `tramp-encoding-reads-stdin' to specify whether the commands read -standard input or a file. +variable `tramp-encoding-command-switch' for the \"-c\" part. Note that this variable is not used for remote commands. There are mechanisms in tramp.el which automatically determine the right shell to @@ -277,286 +300,313 @@ :group 'tramp :type 'string) -(defcustom tramp-encoding-reads-stdin t - "*If non-nil, encoding commands read from standard input. -If nil, the filename is the last argument. - -Note that the commands always must write to standard output." +(defcustom tramp-copy-size-limit 10240 + "*The maximum file size where inline copying is preferred over an out-of-the-band copy." :group 'tramp - :type 'boolean) - -(defcustom tramp-multi-sh-program - tramp-encoding-shell - "*Use this program for bootstrapping multi-hop connections. -This variable is similar to `tramp-encoding-shell', but it is only used -when initializing a multi-hop connection. Therefore, the set of -commands sent to this shell is quite restricted, and if you are -careful it works to use CMD.EXE under Windows (instead of a Bourne-ish -shell which does not normally exist on Windows anyway). - -To use multi-hop methods from Windows, you also need suitable entries -in `tramp-multi-connection-function-alist' for the first hop. - -This variable defaults to the value of `tramp-encoding-shell'." + :type 'integer) + +(defcustom tramp-terminal-type "dumb" + "*Value of TERM environment variable for logging in to remote host. +Because Tramp wants to parse the output of the remote shell, it is easily +confused by ANSI color escape sequences and suchlike. Often, shell init +files conditionalize this setup based on the TERM environment variable." :group 'tramp - :type '(file :must-match t)) - -;; CCC I have changed all occurrences of comint-quote-filename with -;; tramp-shell-quote-argument, except in tramp-handle-expand-many-files. -;; There, comint-quote-filename was removed altogether. If it turns -;; out to be necessary there, something will need to be done. -;;-(defcustom tramp-file-name-quote-list -;;- '(?] ?[ ?\| ?& ?< ?> ?\( ?\) ?\; ?\ ?\* ?\? ?\! ?\" ?\' ?\` ?# ?\@ ?\+ ) -;;- "*Protect these characters from the remote shell. -;;-Any character in this list is quoted (preceded with a backslash) -;;-because it means something special to the shell. This takes effect -;;-when sending file and directory names to the remote shell. -;;- -;;-See `comint-file-name-quote-list' for details." -;;- :group 'tramp -;;- :type '(repeat character)) - -(defcustom tramp-methods - '( ("rcp" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "rsh") - (tramp-copy-program "rcp") - (tramp-remote-sh "/bin/sh") - (tramp-login-args nil) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("scp" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program "scp") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("scp1" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program "scp") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-1" "-e" "none")) - (tramp-copy-args ("-1")) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("scp2" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program "scp") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-2" "-e" "none")) - (tramp-copy-args ("-2")) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("scp1_old" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh1") - (tramp-copy-program "scp1") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("scp2_old" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh2") - (tramp-copy-program "scp2") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("rsync" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program "rsync") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none")) - (tramp-copy-args ("-e" "ssh")) - (tramp-copy-keep-date-arg "-t") - (tramp-password-end-of-line nil)) - ("remcp" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "remsh") - (tramp-copy-program "rcp") - (tramp-remote-sh "/bin/sh") - (tramp-login-args nil) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("rsh" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "rsh") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args nil) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("ssh" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("ssh1" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-1" "-e" "none")) - (tramp-copy-args ("-1")) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("ssh2" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-2" "-e" "none")) - (tramp-copy-args ("-2")) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("ssh1_old" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh1") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("ssh2_old" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh2") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("remsh" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "remsh") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args nil) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("telnet" - (tramp-connection-function tramp-open-connection-telnet) - (tramp-login-program "telnet") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args nil) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("su" (tramp-connection-function tramp-open-connection-su) - (tramp-login-program "su") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-" "%u")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("sudo" (tramp-connection-function tramp-open-connection-su) - (tramp-login-program "sudo") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-u" "%u" "-s" - "-p" "Password:")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("multi" (tramp-connection-function tramp-open-connection-multi) - (tramp-login-program nil) - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args nil) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("scpc" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program "scp") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-o" "ControlPath=%t.%%r@%%h:%%p" - "-o" "ControlMaster=yes" - "-e" "none")) - (tramp-copy-args ("-o" "ControlPath=%t.%%r@%%h:%%p" - "-o" "ControlMaster=auto")) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("scpx" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program "scp") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none" "-t" "-t" "/bin/sh")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ("sshx" (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "ssh") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-e" "none" "-t" "-t" "/bin/sh")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("krlogin" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "krlogin") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-x")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line nil)) - ("plink" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "plink") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-ssh")) ;optionally add "-v" - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line "xy")) ;see docstring for "xy" - ("plink1" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "plink") - (tramp-copy-program nil) - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-1" "-ssh")) ;optionally add "-v" - (tramp-copy-args nil) - (tramp-copy-keep-date-arg nil) - (tramp-password-end-of-line "xy")) ;see docstring for "xy" - ("pscp" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "plink") - (tramp-copy-program "pscp") - (tramp-remote-sh "/bin/sh") - (tramp-login-args ("-ssh")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line "xy")) ;see docstring for "xy" - ("fcp" - (tramp-connection-function tramp-open-connection-rsh) - (tramp-login-program "fsh") - (tramp-copy-program "fcp") - (tramp-remote-sh "/bin/sh -i") - (tramp-login-args ("sh" "-i")) - (tramp-copy-args nil) - (tramp-copy-keep-date-arg "-p") - (tramp-password-end-of-line nil)) - ) + :type 'string) + +(defvar tramp-methods + `(("rcp" (tramp-login-program "rsh") + (tramp-login-args (("%h") ("-l" "%u"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "rcp") + (tramp-copy-args (("-p" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil)) + ("scp" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "scp") + (tramp-copy-args (("-P" "%p") ("-p" "%k") ("-q"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("scp1" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-1" "-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "scp") + (tramp-copy-args (("-1") ("-P" "%p") ("-p" "%k") + ("-q"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("scp2" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-2" "-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "scp") + (tramp-copy-args (("-2") ("-P" "%p") ("-p" "%k") + ("-q"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("scp1_old" + (tramp-login-program "ssh1") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "scp1") + (tramp-copy-args (("-p" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil)) + ("scp2_old" + (tramp-login-program "ssh2") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "scp2") + (tramp-copy-args (("-p" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil)) + ("sftp" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "sftp") + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("rsync" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "rsync") + (tramp-copy-args (("-e" "ssh") ("-t" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil)) + ("remcp" (tramp-login-program "remsh") + (tramp-login-args (("%h") ("-l" "%u"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "rcp") + (tramp-copy-args (("-p" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil)) + ("rsh" (tramp-login-program "rsh") + (tramp-login-args (("%h") ("-l" "%u"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("ssh" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("ssh1" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-1" "-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("ssh2" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-2" "-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("ssh1_old" + (tramp-login-program "ssh1") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("ssh2_old" + (tramp-login-program "ssh2") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("remsh" (tramp-login-program "remsh") + (tramp-login-args (("%h") ("-l" "%u"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("telnet" + (tramp-login-program "telnet") + (tramp-login-args (("%h") ("%p"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil) + (tramp-default-port 23)) + ("su" (tramp-login-program "su") + (tramp-login-args (("-") ("%u"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("sudo" (tramp-login-program "sudo") + (tramp-login-args (("-u" "%u") + ("-s" "-p" "Password:"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("scpc" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-o" "ControlPath=%t.%%r@%%h:%%p") + ("-o" "ControlMaster=yes") + ("-e" "none"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "scp") + (tramp-copy-args (("-P" "%p") ("-p" "%k") ("-q") + ("-o" "ControlPath=%t.%%r@%%h:%%p") + ("-o" "ControlMaster=auto"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("scpx" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none" "-t" "-t" "/bin/sh"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "scp") + (tramp-copy-args (("-p" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("sshx" (tramp-login-program "ssh") + (tramp-login-args (("%h") ("-l" "%u") ("-p" "%p") + ("-e" "none" "-t" "-t" "/bin/sh"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil) + (tramp-gw-args (("-o" + "GlobalKnownHostsFile=/dev/null") + ("-o" "UserKnownHostsFile=/dev/null") + ("-o" "StrictHostKeyChecking=no"))) + (tramp-default-port 22)) + ("krlogin" + (tramp-login-program "krlogin") + (tramp-login-args (("%h") ("-l" "%u") ("-x"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("plink" (tramp-login-program "plink") + (tramp-login-args (("%h") ("-l" "%u") ("-P" "%p") + ("-ssh"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line "xy") ;see docstring for "xy" + (tramp-default-port 22)) + ("plink1" + (tramp-login-program "plink") + (tramp-login-args (("%h") ("-l" "%u") ("-P" "%p") + ("-1" "-ssh"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line "xy") ;see docstring for "xy" + (tramp-default-port 22)) + ("plinkx" + (tramp-login-program "plink") + (tramp-login-args (("-load" "%h") ("-t") + (,(format "env 'TERM=%s' 'PS1=$ '" + tramp-terminal-type)) + ("/bin/sh"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program nil) + (tramp-copy-args nil) + (tramp-copy-keep-date nil) + (tramp-password-end-of-line nil)) + ("pscp" (tramp-login-program "plink") + (tramp-login-args (("%h") ("-l" "%u") ("-P" "%p") + ("-ssh"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "pscp") + (tramp-copy-args (("-scp") ("-p" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line "xy") ;see docstring for "xy" + (tramp-default-port 22)) + ("psftp" (tramp-login-program "plink") + (tramp-login-args (("%h") ("-l" "%u") ("-P" "%p") + ("-ssh"))) + (tramp-remote-sh "/bin/sh") + (tramp-copy-program "pscp") + (tramp-copy-args (("-psftp") ("-p" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line "xy")) ;see docstring for "xy" + ("fcp" (tramp-login-program "fsh") + (tramp-login-args (("%h") ("-l" "%u") ("sh" "-i"))) + (tramp-remote-sh "/bin/sh -i") + (tramp-copy-program "fcp") + (tramp-copy-args (("-p" "%k"))) + (tramp-copy-keep-date t) + (tramp-password-end-of-line nil))) "*Alist of methods for remote files. This is a list of entries of the form (NAME PARAM1 PARAM2 ...). Each NAME stands for a remote access method. Each PARAM is a pair of the form (KEY VALUE). The following KEYs are defined: - * `tramp-connection-function' - This specifies the function to use to connect to the remote host. - Currently, `tramp-open-connection-rsh', `tramp-open-connection-telnet' - and `tramp-open-connection-su' are defined. See the documentation - of these functions for more details. * `tramp-remote-sh' This specifies the Bourne shell to use on the remote host. This MUST be a Bourne-like shell. It is normally not necessary to set @@ -566,21 +616,22 @@ the value that you decide to use. You Have Been Warned. * `tramp-login-program' This specifies the name of the program to use for logging in to the - remote host. Depending on `tramp-connection-function', this may be - the name of rsh or a workalike program (when - `tramp-connection-function' is `tramp-open-connection-rsh'), or the - name of telnet or a workalike (for `tramp-open-connection-telnet'), - or the name of su or a workalike (for `tramp-open-connection-su'). + remote host. This may be the name of rsh or a workalike program, + or the name of telnet or a workalike, or the name of su or a workalike. * `tramp-login-args' This specifies the list of arguments to pass to the above - mentioned program. Please note that this is a list of arguments, + mentioned program. Please note that this is a list of list of arguments, that is, normally you don't want to put \"-a -b\" or \"-f foo\" - here. Instead, you want two list elements, one for \"-a\" and one - for \"-b\", or one for \"-f\" and one for \"foo\". - If `tramp-connection-function' is `tramp-open-connection-su', then - \"%u\" in this list is replaced by the user name, and \"%%\" can - be used to obtain a literal percent character. - \"%t\" is replaced by the temporary file name for `scp'-like methods. + here. Instead, you want a list (\"-a\" \"-b\"), or (\"-f\" \"foo\"). + There are some patterns: \"%h\" in this list is replaced by the host + name, \"%u\" is replaced by the user name, \"%p\" is replaced by the + port number, and \"%%\" can be used to obtain a literal percent character. + If a list containing \"%h\", \"%u\" or \"%p\" is unchanged during + expansion (i.e. no host or no user specified), this list is not used as + argument. By this, arguments like (\"-l\" \"%u\") are optional. + \"%t\" is replaced by the temporary file name produced with + `tramp-make-tramp-temp-file'. \"%k\" indicates the keep-date + parameter of a program, if exists. * `tramp-copy-program' This specifies the name of the program to use for remotely copying the file; this might be the absolute filename of rcp or the name of @@ -588,10 +639,16 @@ * `tramp-copy-args' This specifies the list of parameters to pass to the above mentioned program, the hints for `tramp-login-args' also apply here. - * `tramp-copy-keep-date-arg' - This specifies the parameter to use for the copying program when the - timestamp of the original file should be kept. For `rcp', use `-p', for - `rsync', use `-t'. + * `tramp-copy-keep-date' + This specifies whether the copying program when the preserves the + timestamp of the original file. + * `tramp-default-port' + The default port of a method is needed in case of gateway connections. + Additionally, it is used as indication which method is prepared for + passing gateways. + * `tramp-gw-args' + As the attribute name says, additional arguments are specified here + when a method is applied via a gateway. * `tramp-password-end-of-line' This specifies the string to use for terminating the line after submitting the password. If this method parameter is nil, then the @@ -613,78 +670,22 @@ this case, the file contents need to be protected since the `tramp-login-program' might use escape codes or the connection might not be eight-bit clean. Therefore, file contents are encoded for transit. -See the variable `tramp-coding-commands' for details. +See the variables `tramp-local-coding-commands' and +`tramp-remote-coding-commands' for details. So, to summarize: if the method is an out-of-band method, then you must specify `tramp-copy-program' and `tramp-copy-args'. If it is an -inline method, then these two parameters should be nil. Every method, -inline or out of band, must specify `tramp-connection-function' plus -the associated arguments (for example, the login program if you chose -`tramp-open-connection-telnet'). +inline method, then these two parameters should be nil. Methods which +are fit for gateways must have `tramp-default-port' at least. Notes: -When using `tramp-open-connection-su' the phrase `open connection to a -remote host' sounds strange, but it is used nevertheless, for -consistency. No connection is opened to a remote host, but `su' is -started on the local host. You are not allowed to specify a remote -host other than `localhost' or the name of the local host." - :group 'tramp - :type '(repeat - (cons string - (set (list (const tramp-connection-function) function) - (list (const tramp-login-program) - (choice (const nil) string)) - (list (const tramp-copy-program) - (choice (const nil) string)) - (list (const tramp-remote-sh) - (choice (const nil) string)) - (list (const tramp-login-args) (repeat string)) - (list (const tramp-copy-args) (repeat string)) - (list (const tramp-copy-keep-date-arg) - (choice (const nil) string)) - (list (const tramp-encoding-command) - (choice (const nil) string)) - (list (const tramp-decoding-command) - (choice (const nil) string)) - (list (const tramp-encoding-function) - (choice (const nil) function)) - (list (const tramp-decoding-function) - (choice (const nil) function)) - (list (const tramp-password-end-of-line) - (choice (const nil) string)))))) - -(defcustom tramp-multi-methods '("multi" "multiu") - "*List of multi-hop methods. -Each entry in this list should be a method name as mentioned in the -variable `tramp-methods'." - :group 'tramp - :type '(repeat string)) - -(defcustom tramp-multi-connection-function-alist - '(("telnet" tramp-multi-connect-telnet "telnet %h%n") - ("rsh" tramp-multi-connect-rlogin "rsh %h -l %u%n") - ("remsh" tramp-multi-connect-rlogin "remsh %h -l %u%n") - ("ssh" tramp-multi-connect-rlogin "ssh %h -l %u%n") - ("ssht" tramp-multi-connect-rlogin "ssh %h -e none -t -t -l %u%n") - ("su" tramp-multi-connect-su "su - %u%n") - ("sudo" tramp-multi-connect-su "sudo -u %u -s -p Password:%n")) - "*List of connection functions for multi-hop methods. -Each list item is a list of three items (METHOD FUNCTION COMMAND), -where METHOD is the name as used in the file name, FUNCTION is the -function to be executed, and COMMAND is the shell command used for -connecting. - -COMMAND may contain percent escapes. `%u' will be replaced with the -user name, `%h' will be replaced with the host name, and `%n' will be -replaced with an end-of-line character, as specified in the variable -`tramp-rsh-end-of-line'. Use `%%' for a literal percent character. -Note that the interpretation of the percent escapes also depends on -the FUNCTION. For example, the `%u' escape is forbidden with the -function `tramp-multi-connect-telnet'. See the documentation of the -various functions for details." - :group 'tramp - :type '(repeat (list string function string))) +When using `su' or `sudo' the phrase `open connection to a remote +host' sounds strange, but it is used nevertheless, for consistency. +No connection is opened to a remote host, but `su' or `sudo' is +started on the local host. You should specify a remote host +`localhost' or the name of the local host. Another host name is +useful only in combination with `tramp-default-proxies-alist'.") (defcustom tramp-default-method ;; An external copy method seems to be preferred, because it is much @@ -696,30 +697,26 @@ ;; another good choice because of the "ControlMaster" option, but ;; this is a more modern alternative in OpenSSH 4, which cannot be ;; taken as default. - (let ((e-f (fboundp 'executable-find))) - (cond - ;; PuTTY is installed. - ((and e-f (funcall 'executable-find "pscp")) - (if (or (fboundp 'password-read) - ;; Pageant is running. - (and (fboundp 'w32-window-exists-p) - (funcall 'w32-window-exists-p "Pageant" "Pageant"))) - "pscp" - "plink")) - ;; There is an ssh installation. - ((and e-f (funcall 'executable-find "scp")) - (if (or (fboundp 'password-read) - ;; ssh-agent is running. - (getenv "SSH_AUTH_SOCK") - (getenv "SSH_AGENT_PID")) - "scp" - "ssh")) - ;; Under Emacs 20, `executable-find' does not exists. So we - ;; couldn't check whether there is an ssh implementation. Let's - ;; hope the best. - ((not e-f) "ssh") - ;; Fallback. - (t "ftp"))) + (cond + ;; PuTTY is installed. + ((executable-find "pscp") + (if (or (fboundp 'password-read) + ;; Pageant is running. + (and (fboundp 'w32-window-exists-p) + (funcall (symbol-function 'w32-window-exists-p) + "Pageant" "Pageant"))) + "pscp" + "plink")) + ;; There is an ssh installation. + ((executable-find "scp") + (if (or (fboundp 'password-read) + ;; ssh-agent is running. + (getenv "SSH_AUTH_SOCK") + (getenv "SSH_AGENT_PID")) + "scp" + "ssh")) + ;; Fallback. + (t "ftp")) "*Default method to use for transferring files. See `tramp-methods' for possibilities. Also see `tramp-default-method-alist'." @@ -728,7 +725,7 @@ (defcustom tramp-default-method-alist '(("\\`localhost\\'" "\\`root\\'" "su")) - "*Default method to use for specific user/host pairs. + "*Default method to use for specific host/user pairs. This is an alist of items (HOST USER METHOD). The first matching item specifies the method to use for a file name which does not specify a method. HOST and USER are regular expressions or nil, which is @@ -744,42 +741,90 @@ (regexp :tag "User regexp") (string :tag "Method")))) -;; Default values for non-Unices seeked +(defcustom tramp-default-user + nil + "*Default user to use for transferring files. +It is nil by default; otherwise settings in configuration files like +\"~/.ssh/config\" would be overwritten. Also see `tramp-default-user-alist'. + +This variable is regarded as obsolete, and will be removed soon." + :group 'tramp + :type '(choice (const nil) string)) + +(defcustom tramp-default-user-alist + `(("\\`su\\(do\\)?\\'" nil "root") + ("\\`r\\(em\\)?\\(cp\\|sh\\)\\|telnet\\|plink1?\\'" + nil ,(user-login-name))) + "*Default user to use for specific method/host pairs. +This is an alist of items (METHOD HOST USER). The first matching item +specifies the user to use for a file name which does not specify a +user. METHOD and USER are regular expressions or nil, which is +interpreted as a regular expression which always matches. If no entry +matches, the variable `tramp-default-user' takes effect. + +If the file name does not specify the method, lookup is done using the +empty string for the method name." + :group 'tramp + :type '(repeat (list (regexp :tag "Method regexp") + (regexp :tag "Host regexp") + (string :tag "User")))) + +(defcustom tramp-default-host + (system-name) + "*Default host to use for transferring files. +Useful for su and sudo methods mostly." + :group 'tramp + :type 'string) + +(defcustom tramp-default-proxies-alist nil + "*Route to be followed for specific host/user pairs. +This is an alist of items (HOST USER PROXY). The first matching +item specifies the proxy to be passed for a file name located on +a remote target matching USER@HOST. HOST and USER are regular +expressions or nil, which is interpreted as a regular expression +which always matches. PROXY must be a Tramp filename without a +localname part. Method and user name on PROXY are optional, +which is interpreted with the default values. PROXY can contain +the patterns %h and %u, which are replaced by the strings +matching HOST or USER, respectively." + :group 'tramp + :type '(repeat (list (regexp :tag "Host regexp") + (regexp :tag "User regexp") + (string :tag "Proxy remote name")))) + (defconst tramp-completion-function-alist-rsh - (unless (memq system-type '(windows-nt)) - '((tramp-parse-rhosts "/etc/hosts.equiv") - (tramp-parse-rhosts "~/.rhosts"))) + '((tramp-parse-rhosts "/etc/hosts.equiv") + (tramp-parse-rhosts "~/.rhosts")) "Default list of (FUNCTION FILE) pairs to be examined for rsh methods.") -;; Default values for non-Unices seeked (defconst tramp-completion-function-alist-ssh - (unless (memq system-type '(windows-nt)) - '((tramp-parse-rhosts "/etc/hosts.equiv") - (tramp-parse-rhosts "/etc/shosts.equiv") - (tramp-parse-shosts "/etc/ssh_known_hosts") - (tramp-parse-sconfig "/etc/ssh_config") - (tramp-parse-shostkeys "/etc/ssh2/hostkeys") - (tramp-parse-sknownhosts "/etc/ssh2/knownhosts") - (tramp-parse-rhosts "~/.rhosts") - (tramp-parse-rhosts "~/.shosts") - (tramp-parse-shosts "~/.ssh/known_hosts") - (tramp-parse-sconfig "~/.ssh/config") - (tramp-parse-shostkeys "~/.ssh2/hostkeys") - (tramp-parse-sknownhosts "~/.ssh2/knownhosts"))) + '((tramp-parse-rhosts "/etc/hosts.equiv") + (tramp-parse-rhosts "/etc/shosts.equiv") + (tramp-parse-shosts "/etc/ssh_known_hosts") + (tramp-parse-sconfig "/etc/ssh_config") + (tramp-parse-shostkeys "/etc/ssh2/hostkeys") + (tramp-parse-sknownhosts "/etc/ssh2/knownhosts") + (tramp-parse-rhosts "~/.rhosts") + (tramp-parse-rhosts "~/.shosts") + (tramp-parse-shosts "~/.ssh/known_hosts") + (tramp-parse-sconfig "~/.ssh/config") + (tramp-parse-shostkeys "~/.ssh2/hostkeys") + (tramp-parse-sknownhosts "~/.ssh2/knownhosts")) "Default list of (FUNCTION FILE) pairs to be examined for ssh methods.") -;; Default values for non-Unices seeked (defconst tramp-completion-function-alist-telnet - (unless (memq system-type '(windows-nt)) - '((tramp-parse-hosts "/etc/hosts"))) + '((tramp-parse-hosts "/etc/hosts")) "Default list of (FUNCTION FILE) pairs to be examined for telnet methods.") -;; Default values for non-Unices seeked (defconst tramp-completion-function-alist-su - (unless (memq system-type '(windows-nt)) - '((tramp-parse-passwd "/etc/passwd"))) + '((tramp-parse-passwd "/etc/passwd")) "Default list of (FUNCTION FILE) pairs to be examined for su methods.") +(defconst tramp-completion-function-alist-putty + '((tramp-parse-putty + "HKEY_CURRENT_USER\\Software\\SimonTatham\\PuTTY\\Sessions")) + "Default list of (FUNCTION REGISTRY) pairs to be examined for putty methods.") + (defvar tramp-completion-function-alist nil "*Alist of methods for remote files. This is a list of entries of the form (NAME PAIR1 PAIR2 ...). @@ -795,6 +840,7 @@ * `tramp-parse-hosts' for \"/etc/hosts\" like files, * `tramp-parse-passwd' for \"/etc/passwd\" like files. * `tramp-parse-netrc' for \"~/.netrc\" like files. + * `tramp-parse-putty' for PuTTY registry keys. FUNCTION can also be a customer defined function. For more details see the info pages.") @@ -838,8 +884,6 @@ (tramp-set-completion-function "sudo" tramp-completion-function-alist-su) (tramp-set-completion-function - "multi" nil) - (tramp-set-completion-function "scpx" tramp-completion-function-alist-ssh) (tramp-set-completion-function "sshx" tramp-completion-function-alist-ssh) @@ -850,10 +894,26 @@ (tramp-set-completion-function "plink1" tramp-completion-function-alist-ssh) (tramp-set-completion-function + "plinkx" tramp-completion-function-alist-putty) + (tramp-set-completion-function "pscp" tramp-completion-function-alist-ssh) (tramp-set-completion-function "fcp" tramp-completion-function-alist-ssh))) +(defconst tramp-echo-mark "_echo\b\b\b\b\b" + "String mark to be transmitted around shell commands. +Used to separate their echo from the output they produce. This +will only be used if we cannot disable remote echo via stty. +This string must have no effect on the remote shell except for +producing some echo which can later be detected by +`tramp-echoed-echo-mark-regexp'. Using some characters followed +by an equal number of backspaces to erase them will usually +suffice.") + +(defconst tramp-echoed-echo-mark-regexp "_echo\\(\b\\( \b\\)?\\)\\{5\\}" + "Regexp which matches `tramp-echo-mark' as it gets echoed by +the remote shell.") + (defcustom tramp-rsh-end-of-line "\n" "*String used for end of line in rsh connections. I don't think this ever needs to be changed, so please tell me about it @@ -878,17 +938,53 @@ :group 'tramp :type 'string) +;; "getconf PATH" yields: +;; HP-UX: /usr/bin:/usr/ccs/bin:/opt/ansic/bin:/opt/langtools/bin:/opt/fortran/bin +;; Solaris: /usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin +;; Linux (Debian, Suse): /bin:/usr/bin +;; FreeBSD: /usr/bin:/bin:/usr/sbin:/sbin: - beware trailing ":"! (defcustom tramp-remote-path - ;; "/usr/xpg4/bin" has been placed first, because on Solaris a POSIX - ;; compatible "id" is needed. - '("/usr/xpg4/bin" "/bin" "/usr/bin" "/usr/sbin" "/usr/local/bin" - "/usr/ccs/bin" "/local/bin" "/local/freeware/bin" "/local/gnu/bin" + '(tramp-default-remote-path "/usr/sbin" "/usr/local/bin" + "/local/bin" "/local/freeware/bin" "/local/gnu/bin" "/usr/freeware/bin" "/usr/pkg/bin" "/usr/contrib/bin") "*List of directories to search for executables on remote host. -Please notify me about other semi-standard directories to include here. +For every remote host, this variable will be set buffer local, +keeping the list of existing directories on that host. You can use `~' in this list, but when searching for a shell which groks -tilde expansion, all directory names starting with `~' will be ignored." +tilde expansion, all directory names starting with `~' will be ignored. + +`Default Directories' represent the list of directories given by +the command \"getconf PATH\". It is recommended to use this +entry on top of this list, because these are the default +directories for POSIX compatible commands." + :group 'tramp + :type '(repeat (choice + (const :tag "Default Directories" tramp-default-remote-path) + (string :tag "Directory")))) + +(defcustom tramp-terminal-type "dumb" + "*Value of TERM environment variable for logging in to remote host. +Because Tramp wants to parse the output of the remote shell, it is easily +confused by ANSI color escape sequences and suchlike. Often, shell init +files conditionalize this setup based on the TERM environment variable." + :group 'tramp + :type 'string) + +(defcustom tramp-remote-process-environment + `("HISTFILE=$HOME/.tramp_history" "HISTSIZE=1" "LC_TIME=C" + ,(concat "TERM=" tramp-terminal-type) + "CDPATH=" "HISTORY=" "MAIL=" "MAILCHECK=" "MAILPATH=" + "autocorrect=" "correct=") + + "*List of environment variables to be set on the remote host. + +Each element should be a string of the form ENVVARNAME=VALUE. An +entry ENVVARNAME= diables the corresponding environment variable, +which might have been set in the init files like ~/.profile. + +Special handling is applied to the PATH environment, which should +not be set here. Instead of, it should be set via `tramp-remote-path'." :group 'tramp :type '(repeat string)) @@ -915,7 +1011,7 @@ :type 'regexp) (defcustom tramp-password-prompt-regexp - "^.*\\([pP]assword\\|passphrase\\).*:\^@? *" + "^.*\\([pP]assword\\|[pP]assphrase\\).*:\^@? *" "*Regexp matching password-like prompts. The regexp should match at end of buffer. @@ -930,10 +1026,12 @@ "Login incorrect" "Login Incorrect" "Connection refused" - "Connection closed" + "Connection closed by foreign host." "Sorry, try again." "Name or service not known" - "Host key verification failed.") t) + "Host key verification failed." + "No supported authentication methods left to try!" + "Tramp connection closed") t) ".*" "\\|" "^.*\\(" @@ -1006,7 +1104,7 @@ In fact this expression is empty by intention, it will be used only to check regularly the status of the associated process. The answer will be provided by `tramp-action-process-alive', -`tramp-multi-action-process-alive' and`tramp-action-out-of-band', which see." +`tramp-action-out-of-band', which see." :group 'tramp :type 'regexp) @@ -1020,12 +1118,6 @@ :group 'tramp :type 'string) -(defcustom tramp-discard-garbage nil - "*If non-nil, try to discard garbage sent by remote shell. -Some shells send such garbage upon connection setup." - :group 'tramp - :type 'boolean) - (defcustom tramp-sh-extra-args '(("/bash\\'" . "-norc -noprofile")) "*Alist specifying extra arguments to pass to the remote shell. Entries are (REGEXP . ARGS) where REGEXP is a regular expression @@ -1042,139 +1134,134 @@ '(alist :key-type string :value-type string) '(repeat (cons string string)))) -(defcustom tramp-prefix-format - (if tramp-unified-filenames "/" "/[") +;; XEmacs is distributed with few Lisp packages. Further packages are +;; installed using EFS. If we use a unified filename format, then +;; Tramp is required in addition to EFS. (But why can't Tramp just +;; disable EFS when Tramp is loaded? Then XEmacs can ship with EFS +;; just like before.) Another reason for using a separate filename +;; syntax on XEmacs is that EFS hooks into XEmacs in many places, but +;; Tramp only knows how to deal with `file-name-handler-alist', not +;; the other places. + +;; Currently, we have the choice between 'ftp, 'sep, and 'url. +;;;###autoload +(defcustom tramp-syntax + (if (featurep 'xemacs) 'sep 'ftp) + "Tramp filename syntax to be used. + +It can have the following values: + + 'ftp -- Ange-FTP respective EFS like syntax (GNU Emacs default) + 'sep -- Syntax as defined for XEmacs (not available yet for GNU Emacs) + 'url -- URL-like syntax." + :group 'tramp + :type (if (featurep 'xemacs) + '(choice (const :tag "EFS" ftp) + (const :tag "XEmacs" sep) + (const :tag "URL" url)) + '(choice (const :tag "Ange-FTP" ftp) + (const :tag "URL" url)))) + +(defconst tramp-prefix-format + (cond ((equal tramp-syntax 'ftp) "/") + ((equal tramp-syntax 'sep) "/[") + ((equal tramp-syntax 'url) "/") + (t (error "Wrong `tramp-syntax' defined"))) "*String matching the very beginning of tramp file names. -Used in `tramp-make-tramp-file-name' and `tramp-make-tramp-multi-file-name'." - :group 'tramp - :type 'string) - -(defcustom tramp-prefix-regexp +Used in `tramp-make-tramp-file-name'.") + +(defconst tramp-prefix-regexp (concat "^" (regexp-quote tramp-prefix-format)) "*Regexp matching the very beginning of tramp file names. -Should always start with \"^\". Derived from `tramp-prefix-format'." - :group 'tramp - :type 'regexp) - -(defcustom tramp-method-regexp +Should always start with \"^\". Derived from `tramp-prefix-format'.") + +(defconst tramp-method-regexp "[a-zA-Z_0-9-]+" - "*Regexp matching methods identifiers." - :group 'tramp - :type 'regexp) - -;; It is a little bit annoying that in XEmacs case this delimeter is different -;; for single-hop and multi-hop cases. -(defcustom tramp-postfix-single-method-format - (if tramp-unified-filenames ":" "/") - "*String matching delimeter between method and user or host names. -Applicable for single-hop methods. -Used in `tramp-make-tramp-file-name'." - :group 'tramp - :type 'string) - -(defcustom tramp-postfix-single-method-regexp - (regexp-quote tramp-postfix-single-method-format) - "*Regexp matching delimeter between method and user or host names. -Applicable for single-hop methods. -Derived from `tramp-postfix-single-method-format'." - :group 'tramp - :type 'regexp) - -(defcustom tramp-postfix-multi-method-format - ":" + "*Regexp matching methods identifiers.") + +(defconst tramp-postfix-method-format + (cond ((equal tramp-syntax 'ftp) ":") + ((equal tramp-syntax 'sep) "/") + ((equal tramp-syntax 'url) "://") + (t (error "Wrong `tramp-syntax' defined"))) "*String matching delimeter between method and user or host names. -Applicable for multi-hop methods. -Used in `tramp-make-tramp-multi-file-name'." - :group 'tramp - :type 'string) - -(defcustom tramp-postfix-multi-method-regexp - (regexp-quote tramp-postfix-multi-method-format) +Used in `tramp-make-tramp-file-name'.") + +(defconst tramp-postfix-method-regexp + (regexp-quote tramp-postfix-method-format) "*Regexp matching delimeter between method and user or host names. -Applicable for multi-hop methods. -Derived from `tramp-postfix-multi-method-format'." - :group 'tramp - :type 'regexp) - -(defcustom tramp-postfix-multi-hop-format - (if tramp-unified-filenames ":" "/") - "*String matching delimeter between host and next method. -Applicable for multi-hop methods. -Used in `tramp-make-tramp-multi-file-name'." - :group 'tramp - :type 'string) - -(defcustom tramp-postfix-multi-hop-regexp - (regexp-quote tramp-postfix-multi-hop-format) - "*Regexp matching delimeter between host and next method. -Applicable for multi-hop methods. -Derived from `tramp-postfix-multi-hop-format'." - :group 'tramp - :type 'regexp) - -(defcustom tramp-user-regexp - "[^:/ \t]*" - "*Regexp matching user names." - :group 'tramp - :type 'regexp) - -(defcustom tramp-postfix-user-format +Derived from `tramp-postfix-method-format'.") + +(defconst tramp-user-regexp + "[^:/ \t]+" + "*Regexp matching user names.") + +(defconst tramp-postfix-user-format "@" "*String matching delimeter between user and host names. -Used in `tramp-make-tramp-file-name' and `tramp-make-tramp-multi-file-name'." - :group 'tramp - :type 'string) - -(defcustom tramp-postfix-user-regexp +Used in `tramp-make-tramp-file-name'.") + +(defconst tramp-postfix-user-regexp (regexp-quote tramp-postfix-user-format) "*Regexp matching delimeter between user and host names. -Derived from `tramp-postfix-user-format'." - :group 'tramp - :type 'regexp) - -(defcustom tramp-host-regexp - "[a-zA-Z0-9_.-]*" - "*Regexp matching host names." - :group 'tramp - :type 'regexp) - -(defcustom tramp-host-with-port-regexp - "[a-zA-Z0-9_.#-]*" - "*Regexp matching host names." - :group 'tramp - :type 'regexp) - -(defcustom tramp-postfix-host-format - (if tramp-unified-filenames ":" "]") +Derived from `tramp-postfix-user-format'.") + +(defconst tramp-host-regexp + "[a-zA-Z0-9_.-]+" + "*Regexp matching host names.") + +(defconst tramp-prefix-port-format + (cond ((equal tramp-syntax 'ftp) "#") + ((equal tramp-syntax 'sep) "#") + ((equal tramp-syntax 'url) ":") + (t (error "Wrong `tramp-syntax' defined"))) + "*String matching delimeter between host names and port numbers.") + +(defconst tramp-prefix-port-regexp + (regexp-quote tramp-prefix-port-format) + "*Regexp matching delimeter between host names and port numbers. +Derived from `tramp-prefix-port-format'.") + +(defconst tramp-port-regexp + "[0-9]+" + "*Regexp matching port numbers.") + +(defconst tramp-host-with-port-regexp + (concat "\\(" tramp-host-regexp "\\)" + tramp-prefix-port-regexp + "\\(" tramp-port-regexp "\\)") + "*Regexp matching host names with port numbers.") + +(defconst tramp-postfix-host-format + (cond ((equal tramp-syntax 'ftp) ":") + ((equal tramp-syntax 'sep) "]") + ((equal tramp-syntax 'url) "") + (t (error "Wrong `tramp-syntax' defined"))) "*String matching delimeter between host names and localnames. -Used in `tramp-make-tramp-file-name' and `tramp-make-tramp-multi-file-name'." - :group 'tramp - :type 'string) - -(defcustom tramp-postfix-host-regexp +Used in `tramp-make-tramp-file-name'.") + +(defconst tramp-postfix-host-regexp (regexp-quote tramp-postfix-host-format) "*Regexp matching delimeter between host names and localnames. -Derived from `tramp-postfix-host-format'." - :group 'tramp - :type 'regexp) - -(defcustom tramp-localname-regexp +Derived from `tramp-postfix-host-format'.") + +(defconst tramp-localname-regexp ".*$" - "*Regexp matching localnames." - :group 'tramp - :type 'regexp) + "*Regexp matching localnames.") ;; File name format. -(defcustom tramp-file-name-structure +(defconst tramp-file-name-structure (list (concat tramp-prefix-regexp - "\\(" "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp "\\)?" - "\\(" "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp "\\)?" - "\\(" tramp-host-with-port-regexp "\\)" tramp-postfix-host-regexp - "\\(" tramp-localname-regexp "\\)") - 2 4 5 6) + "\\(" "\\(" tramp-method-regexp "\\)" tramp-postfix-method-regexp "\\)?" + "\\(" "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp "\\)?" + "\\(" tramp-host-regexp + "\\(" tramp-prefix-port-regexp tramp-port-regexp "\\)?" "\\)?" + tramp-postfix-host-regexp + "\\(" tramp-localname-regexp "\\)") + 2 4 5 7) "*List of five elements (REGEXP METHOD USER HOST FILE), detailing \ the tramp file name structure. @@ -1190,69 +1277,81 @@ These numbers are passed directly to `match-string', which see. That means the opening parentheses are counted to identify the pair. -See also `tramp-file-name-regexp'." - :group 'tramp - :type '(list (regexp :tag "File name regexp") - (integer :tag "Paren pair for method name") - (integer :tag "Paren pair for user name ") - (integer :tag "Paren pair for host name ") - (integer :tag "Paren pair for file name "))) +See also `tramp-file-name-regexp'.") ;;;###autoload (defconst tramp-file-name-regexp-unified "\\`/[^/:]+:" "Value for `tramp-file-name-regexp' for unified remoting. Emacs (not XEmacs) uses a unified filename syntax for Ange-FTP and -Tramp. See `tramp-file-name-structure-unified' for more explanations.") +Tramp. See `tramp-file-name-structure' for more explanations.") ;;;###autoload (defconst tramp-file-name-regexp-separate "\\`/\\[.*\\]" "Value for `tramp-file-name-regexp' for separate remoting. XEmacs uses a separate filename syntax for Tramp and EFS. -See `tramp-file-name-structure-separate' for more explanations.") +See `tramp-file-name-structure' for more explanations.") ;;;###autoload -(defcustom tramp-file-name-regexp - (if tramp-unified-filenames - tramp-file-name-regexp-unified - tramp-file-name-regexp-separate) +(defconst tramp-file-name-regexp-url + "\\`/[^/:]+://" + "Value for `tramp-file-name-regexp' for URL-like remoting. +See `tramp-file-name-structure' for more explanations.") + +;;;###autoload +(defconst tramp-file-name-regexp + (cond ((equal tramp-syntax 'ftp) tramp-file-name-regexp-unified) + ((equal tramp-syntax 'sep) tramp-file-name-regexp-separate) + ((equal tramp-syntax 'url) tramp-file-name-regexp-url) + (t (error "Wrong `tramp-syntax' defined"))) "*Regular expression matching file names handled by tramp. This regexp should match tramp file names but no other file names. \(When tramp.el is loaded, this regular expression is prepended to `file-name-handler-alist', and that is searched sequentially. Thus, if the tramp entry appears rather early in the `file-name-handler-alist' and is a bit too general, then some files might be considered tramp -files which are not really tramp files. +files which are not really Tramp files. Please note that the entry in `file-name-handler-alist' is made when this file (tramp.el) is loaded. This means that this variable must be set before loading tramp.el. Alternatively, `file-name-handler-alist' can be updated after changing this variable. -Also see `tramp-file-name-structure'." - :group 'tramp - :type 'regexp) +Also see `tramp-file-name-structure'.") ;;;###autoload (defconst tramp-completion-file-name-regexp-unified - "^/$\\|^/[^/:][^/]*$" + (if (memq system-type '(cygwin windows-nt)) + "^\\([a-zA-Z]:\\)?/$\\|^\\([a-zA-Z]:\\)?/[^/:][^/]*$" + "^/$\\|^/[^/:][^/]*$") "Value for `tramp-completion-file-name-regexp' for unified remoting. Emacs (not XEmacs) uses a unified filename syntax for Ange-FTP and -Tramp. See `tramp-file-name-structure-unified' for more explanations.") +Tramp. See `tramp-file-name-structure' for more explanations.") ;;;###autoload (defconst tramp-completion-file-name-regexp-separate - "^/\\([[][^]]*\\)?$" + (if (memq system-type '(cygwin windows-nt)) + "^\\([a-zA-Z]:\\)?/\\([[][^]]*\\)?$" + "^/\\([[][^]]*\\)?$") "Value for `tramp-completion-file-name-regexp' for separate remoting. XEmacs uses a separate filename syntax for Tramp and EFS. -See `tramp-file-name-structure-separate' for more explanations.") +See `tramp-file-name-structure' for more explanations.") ;;;###autoload -(defcustom tramp-completion-file-name-regexp - (if tramp-unified-filenames - tramp-completion-file-name-regexp-unified - tramp-completion-file-name-regexp-separate) +(defconst tramp-completion-file-name-regexp-url + (if (memq system-type '(cygwin windows-nt)) + "^\\([a-zA-Z]:\\)?/$\\|^\\([a-zA-Z]:\\)?/[^/:]+\\(:\\(/\\(/[^/]*\\)?\\)?\\)?$" + "^/$\\|^/[^/:]+\\(:\\(/\\(/[^/]*\\)?\\)?\\)?$") + "Value for `tramp-completion-file-name-regexp' for URL-like remoting. +See `tramp-file-name-structure' for more explanations.") + +;;;###autoload +(defconst tramp-completion-file-name-regexp + (cond ((equal tramp-syntax 'ftp) tramp-completion-file-name-regexp-unified) + ((equal tramp-syntax 'sep) tramp-completion-file-name-regexp-separate) + ((equal tramp-syntax 'url) tramp-completion-file-name-regexp-url) + (t (error "Wrong `tramp-syntax' defined"))) "*Regular expression matching file names handled by tramp completion. This regexp should match partial tramp file names only. @@ -1261,121 +1360,14 @@ before loading tramp.el. Alternatively, `file-name-handler-alist' can be updated after changing this variable. -Also see `tramp-file-name-structure'." - :group 'tramp - :type 'regexp) - -(defcustom tramp-multi-file-name-structure - (list - (concat - tramp-prefix-regexp - "\\(" "\\(" tramp-method-regexp "\\)" "\\)?" - "\\(" "\\(" tramp-postfix-multi-hop-regexp "%s" "\\)+" "\\)?" - tramp-postfix-host-regexp "\\(" tramp-localname-regexp "\\)") - 2 3 -1) - "*Describes the file name structure of `multi' files. -Multi files allow you to contact a remote host in several hops. -This is a list of four elements (REGEXP METHOD HOP LOCALNAME). - -The first element, REGEXP, gives a regular expression to match against -the file name. In this regular expression, `%s' is replaced with the -value of `tramp-multi-file-name-hop-structure'. (Note: in order to -allow multiple hops, you normally want to use something like -\"\\\\(\\\\(%s\\\\)+\\\\)\" in the regular expression. The outer pair -of parentheses is used for the HOP element, see below.) - -All remaining elements are numbers. METHOD gives the number of the -paren pair which matches the method name. HOP gives the number of the -paren pair which matches the hop sequence. LOCALNAME gives the number of -the paren pair which matches the localname (pathname) on the remote host. - -LOCALNAME can also be negative, which means to count from the end. Ie, a -value of -1 means the last paren pair. - -I think it would be good if the regexp matches the whole of the -string, but I haven't actually tried what happens if it doesn't..." - :group 'tramp - :type '(list (regexp :tag "File name regexp") - (integer :tag "Paren pair for method name") - (integer :tag "Paren pair for hops") - (integer :tag "Paren pair to match localname"))) - -(defcustom tramp-multi-file-name-hop-structure - (list - (concat - "\\(" tramp-method-regexp "\\)" tramp-postfix-multi-method-regexp - "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp - "\\(" tramp-host-with-port-regexp "\\)") - 1 2 3) - "*Describes the structure of a hop in multi files. -This is a list of four elements (REGEXP METHOD USER HOST). First -element REGEXP is used to match against the hop. Pair number METHOD -matches the method of one hop, pair number USER matches the user of -one hop, pair number HOST matches the host of one hop. - -This regular expression should match exactly all of one hop." - :group 'tramp - :type '(list (regexp :tag "Hop regexp") - (integer :tag "Paren pair for method name") - (integer :tag "Paren pair for user name") - (integer :tag "Paren pair for host name"))) - -(defcustom tramp-make-multi-tramp-file-format - (list - (concat tramp-prefix-format "%m") - (concat tramp-postfix-multi-hop-format - "%m" tramp-postfix-multi-method-format - "%u" tramp-postfix-user-format - "%h") - (concat tramp-postfix-host-format "%p")) - "*Describes how to construct a `multi' file name. -This is a list of three elements PREFIX, HOP and LOCALNAME. - -The first element PREFIX says how to construct the prefix, the second -element HOP specifies what each hop looks like, and the final element -LOCALNAME says how to construct the localname (pathname). - -In PREFIX, `%%' means `%' and `%m' means the method name. - -In HOP, `%%' means `%' and `%m', `%u', `%h' mean the hop method, hop -user and hop host, respectively. - -In LOCALNAME, `%%' means `%' and `%p' means the localname. - -The resulting file name always contains one copy of PREFIX and one -copy of LOCALNAME, but there is one copy of HOP for each hop in the file -name. - -Note: the current implementation requires the prefix to contain the -method name, followed by all the hops, and the localname must come -last." - :group 'tramp - :type '(list string string string)) - -(defcustom tramp-terminal-type "dumb" - "*Value of TERM environment variable for logging in to remote host. -Because Tramp wants to parse the output of the remote shell, it is easily -confused by ANSI color escape sequences and suchlike. Often, shell init -files conditionalize this setup based on the TERM environment variable." - :group 'tramp - :type 'string) - -(defcustom tramp-completion-without-shell-p nil - "*If nil, use shell wildcards for completion, else rely on Lisp only. -Using shell wildcards for completions has the advantage that it can be -fast even in large directories, but completion is always -case-sensitive. Relying on Lisp only means that case-insensitive -completion is possible (subject to the variable `completion-ignore-case'), -but it might be slow on large directories." - :group 'tramp - :type 'boolean) - -(defcustom tramp-actions-before-shell - '((tramp-password-prompt-regexp tramp-action-password) - (tramp-login-prompt-regexp tramp-action-login) +Also see `tramp-file-name-structure'.") + +(defconst tramp-actions-before-shell + '((tramp-login-prompt-regexp tramp-action-login) + (tramp-password-prompt-regexp tramp-action-password) + (tramp-wrong-passwd-regexp tramp-action-permission-denied) (shell-prompt-pattern tramp-action-succeed) (tramp-shell-prompt-pattern tramp-action-succeed) - (tramp-wrong-passwd-regexp tramp-action-permission-denied) (tramp-yesno-prompt-regexp tramp-action-yesno) (tramp-yn-prompt-regexp tramp-action-yn) (tramp-terminal-prompt-regexp tramp-action-terminal) @@ -1390,51 +1382,19 @@ appended to it. The ACTION should also be a symbol, but a function. When the -corresponding PATTERN matches, the ACTION function is called." - :group 'tramp - :type '(repeat (list variable function))) - -(defcustom tramp-actions-copy-out-of-band +corresponding PATTERN matches, the ACTION function is called.") + +(defconst tramp-actions-copy-out-of-band '((tramp-password-prompt-regexp tramp-action-password) (tramp-wrong-passwd-regexp tramp-action-permission-denied) - (tramp-copy-failed-regexp tramp-action-copy-failed) + (tramp-copy-failed-regexp tramp-action-permission-denied) (tramp-process-alive-regexp tramp-action-out-of-band)) "List of pattern/action pairs. This list is used for copying/renaming with out-of-band methods. -See `tramp-actions-before-shell' for more info." - :group 'tramp - :type '(repeat (list variable function))) - -(defcustom tramp-multi-actions - '((tramp-password-prompt-regexp tramp-multi-action-password) - (tramp-login-prompt-regexp tramp-multi-action-login) - (shell-prompt-pattern tramp-multi-action-succeed) - (tramp-shell-prompt-pattern tramp-multi-action-succeed) - (tramp-wrong-passwd-regexp tramp-multi-action-permission-denied) - (tramp-process-alive-regexp tramp-multi-action-process-alive)) - "List of pattern/action pairs. -This list is used for each hop in multi-hop connections. -See `tramp-actions-before-shell' for more info." - :group 'tramp - :type '(repeat (list variable function))) - -(defcustom tramp-initial-commands - '("unset HISTORY" - "unset correct" - "unset autocorrect") - "List of commands to send to the first remote shell that we see. -These commands will be sent to any shell, and thus they should be -designed to work in such circumstances. Also, restrict the commands -to the bare necessity for getting the remote shell into a state -where it is possible to execute the Bourne-ish shell. - -At the moment, the command to execute the Bourne-ish shell uses strange -quoting which `tcsh' tries to correct, so we send the command \"unset -autocorrect\" to the remote host." - :group 'tramp - :type '(repeat string)) - -;; Chunked sending kluge. We set this to 500 for black-listed constellations + +See `tramp-actions-before-shell' for more info.") + +;; Chunked sending kludge. We set this to 500 for black-listed constellations ;; known to have a bug in `process-send-string'; some ssh connections appear ;; to drop bytes when data is sent too quickly. There is also a connection ;; buffer local variable, which is computed depending on remote host properties @@ -1490,16 +1450,16 @@ If that number exceeds 1000, you can stop the execution by hitting C-g, because your Emacs is likely clean. +When it is necessary to set `tramp-chunksize', you might consider to +use an out-of-the-band method (like \"scp\") instead of an internal one +\(like \"ssh\"), because setting `tramp-chunksize' to non-nil decreases +performance. + If your Emacs is buggy, the code stops and gives you an indication about the value `tramp-chunksize' should be set. Maybe you could just experiment a bit, e.g. changing the values of `init' and `step' in the third line of the code. -When it is necessary to set `tramp-chunksize', you might consider to -use an out-of-the-band method (like \"scp\") instead of an internal one -\(like \"ssh\"), because setting `tramp-chunksize' to non-nil decreases -performance. - Please raise a bug report via \"M-x tramp-bug\" if your system needs this variable to be set as well." :group 'tramp @@ -1518,144 +1478,25 @@ ;;; Internal Variables: -(defvar tramp-buffer-file-attributes nil - "Holds the `ls -ild' output for the current buffer. -This variable is local to each buffer. It is not used if the remote -machine groks Perl. If it is used, it's used as an emulation for -the visited file modtime.") -(make-variable-buffer-local 'tramp-buffer-file-attributes) - -(defvar tramp-md5-function - (cond ((and (require 'md5) (fboundp 'md5)) 'md5) - ((fboundp 'md5-encode) - (lambda (x) (base64-encode-string - (funcall (symbol-function 'md5-encode) x)))) - (t (error "Couldn't find an `md5' function"))) - "Function to call for running the MD5 algorithm.") - (defvar tramp-end-of-output - (concat "///" - (funcall tramp-md5-function - (concat - (prin1-to-string process-environment) - (current-time-string) -;; (prin1-to-string -;; (if (fboundp 'directory-files-and-attributes) -;; (funcall 'directory-files-and-attributes -;; (or (getenv "HOME") -;; (tramp-temporary-file-directory))) -;; (mapcar -;; (lambda (x) -;; (cons x (file-attributes x))) -;; (directory-files (or (getenv "HOME") -;; (tramp-temporary-file-directory)) -;; t)))) - ))) + (concat + "///" (md5 (concat + (prin1-to-string process-environment) (current-time-string)))) "String used to recognize end of output.") -(defvar tramp-connection-function nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-remote-sh nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-login-program nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-login-args nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-copy-program nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-copy-args nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-copy-keep-date-arg nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-encoding-command nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-decoding-command nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-encoding-function nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-decoding-function nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -(defvar tramp-password-end-of-line nil - "This internal variable holds a parameter for `tramp-methods'. -In the connection buffer, this variable has the value of the like-named -method parameter, as specified in `tramp-methods' (which see).") - -;; CCC `local in each buffer'? -(defvar tramp-ls-command nil - "This command is used to get a long listing with numeric user and group ids. -This variable is automatically made buffer-local to each rsh process buffer -upon opening the connection.") - -(defvar tramp-current-multi-method nil - "Name of `multi' connection method for this *tramp* buffer, or nil if not multi. -This variable is automatically made buffer-local to each rsh process buffer -upon opening the connection.") - (defvar tramp-current-method nil - "Connection method for this *tramp* buffer. -This variable is automatically made buffer-local to each rsh process buffer -upon opening the connection.") + "Connection method for this *tramp* buffer.") (defvar tramp-current-user nil - "Remote login name for this *tramp* buffer. -This variable is automatically made buffer-local to each rsh process buffer -upon opening the connection.") + "Remote login name for this *tramp* buffer.") (defvar tramp-current-host nil - "Remote host for this *tramp* buffer. -This variable is automatically made buffer-local to each rsh process buffer -upon opening the connection.") - -(defvar tramp-test-groks-nt nil - "Whether the `test' command groks the `-nt' switch. -\(`test A -nt B' tests if file A is newer than file B.) -This variable is automatically made buffer-local to each rsh process buffer -upon opening the connection.") - -(defvar tramp-file-exists-command nil - "Command to use for checking if a file exists. -This variable is automatically made buffer-local to each rsh process buffer -upon opening the connection.") - -(defconst tramp-uudecode "\ -tramp_uudecode () { -\(echo begin 600 /tmp/tramp.$$; tail +2) | uudecode + "Remote host for this *tramp* buffer.") + +(defconst tramp-uudecode + "(echo begin 600 /tmp/tramp.$$; tail +2) | uudecode cat /tmp/tramp.$$ -rm -f /tmp/tramp.$$ -}" +rm -f /tmp/tramp.$$" "Shell function to implement `uudecode' to standard output. Many systems support `uudecode -o /dev/stdout' or `uudecode -o -' for this or `uudecode -p', but some systems don't, and for them @@ -1667,7 +1508,8 @@ ;; end. ;; The device number is returned as "-1", because there will be a virtual ;; device number set in `tramp-handle-file-attributes' -(defconst tramp-perl-file-attributes "\ +(defconst tramp-perl-file-attributes + "%s -e ' @stat = lstat($ARGV[0]); if (($stat[2] & 0170000) == 0120000) { @@ -1685,7 +1527,7 @@ $uid = ($ARGV[1] eq \"integer\") ? $stat[4] : \"\\\"\" . getpwuid($stat[4]) . \"\\\"\"; $gid = ($ARGV[1] eq \"integer\") ? $stat[5] : \"\\\"\" . getgrgid($stat[5]) . \"\\\"\"; printf( - \"(%s %u %s %s (%u %u) (%u %u) (%u %u) %u %u t (%u . %u) -1)\\n\", + \"(%%s %%u %%s %%s (%%u %%u) (%%u %%u) (%%u %%u) %%u %%u t (%%u . %%u) -1)\\n\", $type, $stat[3], $uid, @@ -1700,11 +1542,14 @@ $stat[2], $stat[1] >> 16 & 0xffff, $stat[1] & 0xffff -);" +);' \"$1\" \"$2\" \"$3\" 2>/dev/null" "Perl script to produce output suitable for use with `file-attributes' -on the remote file system.") - -(defconst tramp-perl-directory-files-and-attributes "\ +on the remote file system. +Escape sequence %s is replaced with name of Perl binary. +This string is passed to `format', so percent characters need to be doubled.") + +(defconst tramp-perl-directory-files-and-attributes + "%s -e ' chdir($ARGV[0]) or printf(\"\\\"Cannot change to $ARGV[0]: $''!''\\\"\\n\"), exit(); opendir(DIR,\".\") or printf(\"\\\"Cannot open directory $ARGV[0]: $''!''\\\"\\n\"), exit(); @list = readdir(DIR); @@ -1731,7 +1576,7 @@ $uid = ($ARGV[1] eq \"integer\") ? $stat[4] : \"\\\"\" . getpwuid($stat[4]) . \"\\\"\"; $gid = ($ARGV[1] eq \"integer\") ? $stat[5] : \"\\\"\" . getgrgid($stat[5]) . \"\\\"\"; printf( - \"(\\\"%s\\\" %s %u %s %s (%u %u) (%u %u) (%u %u) %u %u t (%u . %u) (%u %u))\\n\", + \"(\\\"%%s\\\" %%s %%u %%s %%s (%%u %%u) (%%u %%u) (%%u %%u) %%u %%u t (%%u . %%u) (%%u %%u))\\n\", $filename, $type, $stat[3], @@ -1750,9 +1595,11 @@ $stat[0] >> 16 & 0xffff, $stat[0] & 0xffff); } -printf(\")\\n\");" +printf(\")\\n\");' \"$1\" \"$2\" \"$3\" 2>/dev/null" "Perl script implementing `directory-files-attributes' as Lisp `read'able -output.") +output. +Escape sequence %s is replaced with name of Perl binary. +This string is passed to `format', so percent characters need to be doubled.") ;; ;; These two use uu encoding. ;; (defvar tramp-perl-encode "%s -e'\ @@ -1775,25 +1622,25 @@ ;; Escape sequence %s is replaced with name of Perl binary.") ;; These two use base64 encoding. -(defvar tramp-perl-encode-with-module - "perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)'" +(defconst tramp-perl-encode-with-module + "%s -MMIME::Base64 -0777 -ne 'print encode_base64($_)' 2>/dev/null" "Perl program to use for encoding a file. Escape sequence %s is replaced with name of Perl binary. This string is passed to `format', so percent characters need to be doubled. This implementation requires the MIME::Base64 Perl module to be installed on the remote host.") -(defvar tramp-perl-decode-with-module - "perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)'" +(defconst tramp-perl-decode-with-module + "%s -MMIME::Base64 -0777 -ne 'print decode_base64($_)' 2>/dev/null" "Perl program to use for decoding a file. Escape sequence %s is replaced with name of Perl binary. This string is passed to `format', so percent characters need to be doubled. This implementation requires the MIME::Base64 Perl module to be installed on the remote host.") -(defvar tramp-perl-encode +(defconst tramp-perl-encode "%s -e ' -# This script contributed by Juanma Barranquero . +# This script is contributed by Juanma Barranquero . # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 # Free Software Foundation, Inc. use strict; @@ -1828,15 +1675,14 @@ (substr(unpack(q(B*), $data) . q(00000), 0, 432) =~ /....../g)), $pad, qq(\\n); -} -'" +}' 2>/dev/null" "Perl program to use for encoding a file. Escape sequence %s is replaced with name of Perl binary. This string is passed to `format', so percent characters need to be doubled.") -(defvar tramp-perl-decode +(defconst tramp-perl-decode "%s -e ' -# This script contributed by Juanma Barranquero . +# This script is contributed by Juanma Barranquero . # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 # Free Software Foundation, Inc. use strict; @@ -1874,8 +1720,7 @@ ((join q(), map {$trans{$_} || q()} split //, $chunk) =~ /......../g); last if $finished; -} -'" +}' 2>/dev/null" "Perl program to use for decoding a file. Escape sequence %s is replaced with name of Perl binary. This string is passed to `format', so percent characters need to be doubled.") @@ -1898,44 +1743,12 @@ "A list of file types returned from the `stat' system call. This is used to map a mode number to a permission string.") -(defvar tramp-dos-coding-system - (if (and (fboundp 'coding-system-p) - (funcall 'coding-system-p '(dos))) - 'dos - 'undecided-dos) - "Some Emacsen know the `dos' coding system, others need `undecided-dos'.") - -(defvar tramp-last-cmd nil - "Internal Tramp variable recording the last command sent. -This variable is buffer-local in every buffer.") -(make-variable-buffer-local 'tramp-last-cmd) - -(defvar tramp-process-echoes nil - "Whether to process echoes from the remote shell.") - -(defvar tramp-last-cmd-time nil - "Internal Tramp variable recording the time when the last cmd was sent. -This variable is buffer-local in every buffer.") -(make-variable-buffer-local 'tramp-last-cmd-time) - -;; This variable does not have the right value in XEmacs. What should -;; I use instead of find-operation-coding-system in XEmacs? -(defvar tramp-feature-write-region-fix - (when (fboundp 'find-operation-coding-system) - (let ((file-coding-system-alist '(("test" emacs-mule)))) - (funcall (symbol-function 'find-operation-coding-system) - 'write-region 0 0 "" nil "test"))) - "Internal variable to say if `write-region' chooses the right coding. -Older versions of Emacs chose the coding system for `write-region' based -on the FILENAME argument, even if VISIT was a string.") - ;; New handlers should be added here. The following operations can be ;; handled using the normal primitives: file-name-as-directory, ;; file-name-directory, file-name-nondirectory, ;; file-name-sans-versions, get-file-buffer. (defconst tramp-file-name-handler-alist - '( - (load . tramp-handle-load) + '((load . tramp-handle-load) (make-symbolic-link . tramp-handle-make-symbolic-link) (file-name-directory . tramp-handle-file-name-directory) (file-name-nondirectory . tramp-handle-file-name-nondirectory) @@ -1943,7 +1756,6 @@ (file-exists-p . tramp-handle-file-exists-p) (file-directory-p . tramp-handle-file-directory-p) (file-executable-p . tramp-handle-file-executable-p) - (file-accessible-directory-p . tramp-handle-file-accessible-directory-p) (file-readable-p . tramp-handle-file-readable-p) (file-regular-p . tramp-handle-file-regular-p) (file-symlink-p . tramp-handle-file-symlink-p) @@ -1964,10 +1776,14 @@ (delete-directory . tramp-handle-delete-directory) (delete-file . tramp-handle-delete-file) (directory-file-name . tramp-handle-directory-file-name) + ;; `executable-find' is not official yet. + (executable-find . tramp-handle-executable-find) + (start-file-process . tramp-handle-start-file-process) + (process-file . tramp-handle-process-file) (shell-command . tramp-handle-shell-command) - (process-file . tramp-handle-process-file) (insert-directory . tramp-handle-insert-directory) (expand-file-name . tramp-handle-expand-file-name) + (substitute-in-file-name . tramp-handle-substitute-in-file-name) (file-local-copy . tramp-handle-file-local-copy) (file-remote-p . tramp-handle-file-remote-p) (insert-file-contents . tramp-handle-insert-file-contents) @@ -1976,7 +1792,6 @@ (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) (dired-compress-file . tramp-handle-dired-compress-file) - (dired-call-process . tramp-handle-dired-call-process) (dired-recursive-delete-directory . tramp-handle-dired-recursive-delete-directory) (set-visited-file-modtime . tramp-handle-set-visited-file-modtime) @@ -2006,37 +1821,115 @@ ;;; Internal functions which must come first. -(defsubst tramp-message (level fmt-string &rest args) +(defsubst tramp-debug-message (vec fmt-string &rest args) + "Append message to debug buffer. +Message is formatted with FMT-STRING as control string and the remaining +ARGS to actually emit the message (if applicable)." + (when (get-buffer (tramp-buffer-name vec)) + (with-current-buffer (tramp-get-debug-buffer vec) + (goto-char (point-max)) + (unless (bolp) + (insert "\n")) + ;; Timestamp + (insert (format-time-string "%T ")) + ;; Calling function + (let ((btn 1) btf fn) + (while (not fn) + (setq btf (nth 1 (backtrace-frame btn))) + (if (not btf) + (setq fn "") + (when (symbolp btf) + (setq fn (symbol-name btf)) + (unless (and (string-match "^tramp" fn) + (not (string-match + "^tramp\\(-debug\\)?\\(-message\\|-error\\)$" + fn))) + (setq fn nil))) + (setq btn (1+ btn)))) + ;; The following code inserts filename and line number. + ;; Should be deactivated by default, because it is time + ;; consuming. +; (let ((ffn (find-function-noselect (intern fn)))) +; (insert +; (format +; "%s:%d: " +; (file-name-nondirectory (buffer-file-name (car ffn))) +; (with-current-buffer (car ffn) +; (1+ (count-lines (point-min) (cdr ffn))))))) + (insert (format "%s " fn))) + ;; The message + (insert (apply 'format fmt-string args))))) + +(defsubst tramp-message (vec-or-proc level fmt-string &rest args) "Emit a message depending on verbosity level. -First arg LEVEL says to be quiet if `tramp-verbose' is less than LEVEL. The -message is emitted only if `tramp-verbose' is greater than or equal to LEVEL. -Calls function `message' with FMT-STRING as control string and the remaining -ARGS to actually emit the message (if applicable). - -This function expects to be called from the tramp buffer only!" - (when (<= level tramp-verbose) - (apply #'message (concat "tramp: " fmt-string) args) - (when tramp-debug-buffer - (save-excursion - (set-buffer - (tramp-get-debug-buffer - tramp-current-multi-method tramp-current-method - tramp-current-user tramp-current-host)) - (goto-char (point-max)) - (unless (bolp) - (insert "\n")) - (tramp-insert-with-face - 'italic - (concat "# " (apply #'format fmt-string args) "\n")))))) - -(defun tramp-message-for-buffer - (multi-method method user host level fmt-string &rest args) - "Like `tramp-message' but temporarily switches to the tramp buffer. -First three args METHOD, USER, and HOST identify the tramp buffer to use, -remaining args passed to `tramp-message'." - (save-excursion - (set-buffer (tramp-get-buffer multi-method method user host)) - (apply 'tramp-message level fmt-string args))) +VEC-OR-PROC identifies the tramp buffer to use. It can be either a +vector or a process. LEVEL says to be quiet if `tramp-verbose' is +less than LEVEL. The message is emitted only if `tramp-verbose' is +greater than or equal to LEVEL. + +The message is also logged into the debug buffer when `tramp-verbose' +is greater than or equal 4. + +Calls functions `message' and `tramp-debug-message' with FMT-STRING as +control string and the remaining ARGS to actually emit the message (if +applicable)." + (condition-case nil + (when (<= level tramp-verbose) + ;; Match data must be preserved! + (save-match-data + ;; Display only when there is a minimum level. + (when (<= level 3) + (apply 'message + (concat + (cond + ((= level 0) "") + ((= level 1) "") + ((= level 2) "Warning: ") + (t "Tramp: ")) + fmt-string) + args)) + ;; Log only when there is a minimum level. + (when (>= tramp-verbose 4) + (when (and vec-or-proc + (processp vec-or-proc) + (buffer-name (process-buffer vec-or-proc))) + (with-current-buffer (process-buffer vec-or-proc) + ;; Translate proc to vec. + (setq vec-or-proc (tramp-dissect-file-name default-directory)))) + (when (and vec-or-proc (vectorp vec-or-proc)) + (apply 'tramp-debug-message + vec-or-proc + (concat (format "(%d) # " level) fmt-string) + args))))) + ;; Suppress all errors. + (error nil))) + +(defsubst tramp-error (vec-or-proc signal fmt-string &rest args) + "Emit an error. +VEC-OR-PROC identifies the connection to use, SIGNAL is the +signal identifier to be raised, remaining args passed to +`tramp-message'. Finally, signal SIGNAL is raised." + (tramp-message + vec-or-proc 1 "%s" + (error-message-string + (list signal (get signal 'error-message) (apply 'format fmt-string args)))) + (signal signal (list (apply 'format fmt-string args)))) + +(defsubst tramp-error-with-buffer + (buffer vec-or-proc signal fmt-string &rest args) + "Emit an error, and show BUFFER. +If BUFFER is nil, show the connection buffer. Wait for 30\", or until +an input event arrives. The other arguments are passed to `tramp-error'." + (save-window-excursion + (unwind-protect + (apply 'tramp-error vec-or-proc signal fmt-string args) + (when (and vec-or-proc (not (zerop tramp-verbose))) + (let ((enable-recursive-minibuffers t)) + (pop-to-buffer + (or (and (bufferp buffer) buffer) + (and (processp vec-or-proc) (process-buffer vec-or-proc)) + (tramp-get-buffer vec-or-proc))) + (sit-for 30)))))) (defsubst tramp-line-end-position nil "Return point at end of line. @@ -2054,18 +1947,15 @@ Second arg VAR is a symbol. It is used as a variable name to hold the filename structure. It is also used as a prefix for the variables holding the components. For example, if VAR is the symbol `foo', then -`foo' will be bound to the whole structure, `foo-multi-method' will -be bound to the multi-method component, and so on for `foo-method', -`foo-user', `foo-host', `foo-localname'. +`foo' will be bound to the whole structure, `foo-method' will be bound to +the method component, and so on for `foo-user', `foo-host', `foo-localname'. Remaining args are Lisp expressions to be evaluated (inside an implicit `progn'). -If VAR is nil, then we bind `v' to the structure and `multi-method', -`method', `user', `host', `localname' to the components." +If VAR is nil, then we bind `v' to the structure and `method', `user', +`host', `localname' to the components." `(let* ((,(or var 'v) (tramp-dissect-file-name ,filename)) - (,(if var (intern (concat (symbol-name var) "-multi-method")) 'multi-method) - (tramp-file-name-multi-method ,(or var 'v))) (,(if var (intern (concat (symbol-name var) "-method")) 'method) (tramp-file-name-method ,(or var 'v))) (,(if var (intern (concat (symbol-name var) "-user")) 'user) @@ -2077,15 +1967,45 @@ ,@body)) (put 'with-parsed-tramp-file-name 'lisp-indent-function 2) +(put 'with-parsed-tramp-file-name 'edebug-form-spec '(form symbolp body)) ;; Enable debugging. -(eval-and-compile - (when (featurep 'edebug) - (def-edebug-spec with-parsed-tramp-file-name (form symbolp body)))) +;(eval-and-compile +; (when (featurep 'edebug) +; (def-edebug-spec with-parsed-tramp-file-name (form symbolp body)))) ;; Highlight as keyword. (when (functionp 'font-lock-add-keywords) (funcall 'font-lock-add-keywords 'emacs-lisp-mode '("\\"))) +(defmacro with-file-property (vec file property &rest body) + "Check in Tramp cache for PROPERTY, otherwise execute BODY and set cache. +FILE must be a local file name on a connection identified via VEC." + `(if (file-name-absolute-p ,file) + (let ((value (tramp-get-file-property ,vec ,file ,property 'undef))) + (when (eq value 'undef) + ;; We cannot pass @body as parameter to + ;; `tramp-set-file-property' because it mangles our + ;; debug messages. + (setq value (progn ,@body)) + (tramp-set-file-property ,vec ,file ,property value)) + value) + ,@body)) +(put 'with-file-property 'lisp-indent-function 3) +(put 'with-file-property 'edebug-form-spec t) + +(defmacro with-connection-property (key property &rest body) + "Checks in Tramp for property PROPERTY, otherwise executes BODY and set." + `(let ((value (tramp-get-connection-property ,key ,property 'undef))) + (when (eq value 'undef) + ;; We cannot pass ,@body as parameter to + ;; `tramp-set-connection-property' because it mangles our debug + ;; messages. + (setq value (progn ,@body)) + (tramp-set-connection-property ,key ,property value)) + value)) +(put 'with-connection-property 'lisp-indent-function 2) +(put 'with-connection-property 'edebug-form-spec t) + (defmacro tramp-let-maybe (variable value &rest body) "Let-bind VARIABLE to VALUE in BODY, but only if VARIABLE is not obsolete. BODY is executed whether or not the variable is obsolete. @@ -2122,12 +2042,17 @@ tramp-completion-function-alist)) (while v - ;; Remove double entries + ;; Remove double entries. (when (member (car v) (cdr v)) (setcdr v (delete (car v) (cdr v)))) - ;; Check for function and file + ;; Check for function and file or registry key. (unless (and (functionp (nth 0 (car v))) - (file-exists-p (nth 1 (car v)))) + (if (string-match "^HKEY_CURRENT_USER" (nth 1 (car v))) + ;; Windows registry. + (and (memq system-type '(cygwin windows-nt)) + (zerop (call-process "reg" nil nil nil "query" (nth 1 (car v))))) + ;; Configuration file. + (file-exists-p (nth 1 (car v))))) (setq r (delete (car v) r))) (setq v (cdr v))) @@ -2136,15 +2061,19 @@ (cons method r))))) (defun tramp-get-completion-function (method) - "Returns list of completion functions for METHOD. + "Returns a list of completion functions for METHOD. For definition of that list see `tramp-set-completion-function'." - (cdr (assoc method tramp-completion-function-alist))) + (cons + ;; Hosts visited once shall be remembered. + `(tramp-parse-connection-properties ,method) + ;; The method related defaults. + (cdr (assoc method tramp-completion-function-alist)))) ;;; File Name Handler Functions: (defun tramp-handle-make-symbolic-link (filename linkname &optional ok-if-already-exists) - "Like `make-symbolic-link' for tramp files. + "Like `make-symbolic-link' for Tramp files. If LINKNAME is a non-Tramp file, it is used verbatim as the target of the symlink. If LINKNAME is a Tramp file, only the localname component is used as the target of the symlink. @@ -2154,12 +2083,12 @@ this can give surprising results if the user/host for the source and target of the symlink differ." (with-parsed-tramp-file-name linkname l - (let ((ln (tramp-get-remote-ln l-multi-method l-method l-user l-host)) + (let ((ln (tramp-get-remote-ln l)) (cwd (file-name-directory l-localname))) (unless ln - (signal 'file-error - (list "Making a symbolic link." - "ln(1) does not exist on the remote host."))) + (tramp-error + l 'file-error + "Making a symbolic link. ln(1) does not exist on the remote host.")) ;; Do the 'confirm if exists' thing. (when (file-exists-p linkname) @@ -2170,7 +2099,8 @@ (format "File %s already exists; make it a link anyway? " l-localname))))) - (signal 'file-already-exists (list "File already exists" l-localname)) + (tramp-error + l 'file-already-exists "File %s already exists" l-localname) (delete-file linkname))) ;; If FILENAME is a Tramp name, use just the localname component. @@ -2184,19 +2114,12 @@ ;; that FILENAME belongs to. (zerop (tramp-send-command-and-check - l-multi-method l-method l-user l-host - (format "cd %s && %s -sf %s %s" - cwd ln - filename - l-localname) - t))))) + l (format "cd %s && %s -sf %s %s" cwd ln filename l-localname) t))))) (defun tramp-handle-load (file &optional noerror nomessage nosuffix must-suffix) - "Like `load' for tramp files. Not implemented!" - (unless (file-name-absolute-p file) - (error "Tramp cannot `load' files without absolute file name")) - (with-parsed-tramp-file-name file nil + "Like `load' for Tramp files." + (with-parsed-tramp-file-name (expand-file-name file) nil (unless nosuffix (cond ((file-exists-p (concat file ".elc")) (setq file (concat file ".elc"))) @@ -2207,138 +2130,138 @@ ;; Included for safety's sake. (unless (or (file-name-directory file) (string-match "\\.elc?\\'" file)) - (error "File `%s' does not include a `.el' or `.elc' suffix" - file))) + (tramp-error + v 'file-error + "File `%s' does not include a `.el' or `.elc' suffix" file))) (unless noerror (when (not (file-exists-p file)) - (error "Cannot load nonexistent file `%s'" file))) + (tramp-error v 'file-error "Cannot load nonexistent file `%s'" file))) (if (not (file-exists-p file)) nil - (unless nomessage - (message "Loading %s..." file)) + (unless nomessage (tramp-message v 0 "Loading %s..." file)) (let ((local-copy (file-local-copy file))) ;; MUST-SUFFIX doesn't exist on XEmacs, so let it default to nil. (load local-copy noerror t t) (delete-file local-copy)) - (unless nomessage - (message "Loading %s...done" file)) + (unless nomessage (tramp-message v 0 "Loading %s...done" file)) t))) ;; Localname manipulation functions that grok TRAMP localnames... (defun tramp-handle-file-name-directory (file) - "Like `file-name-directory' but aware of TRAMP files." + "Like `file-name-directory' but aware of Tramp files." ;; Everything except the last filename thing is the directory. (with-parsed-tramp-file-name file nil ;; Run the command on the localname portion only. (tramp-make-tramp-file-name - multi-method method user host (file-name-directory (or localname ""))))) + method user host (file-name-directory (or localname ""))))) (defun tramp-handle-file-name-nondirectory (file) - "Like `file-name-nondirectory' but aware of TRAMP files." + "Like `file-name-nondirectory' but aware of Tramp files." (with-parsed-tramp-file-name file nil (file-name-nondirectory localname))) (defun tramp-handle-file-truename (filename &optional counter prev-dirs) - "Like `file-truename' for tramp files." + "Like `file-truename' for Tramp files." (with-parsed-tramp-file-name (expand-file-name filename) nil - (let* ((steps (tramp-split-string localname "/")) - (localnamedir (tramp-let-maybe directory-sep-char ?/ ;for XEmacs - (file-name-as-directory localname))) - (is-dir (string= localname localnamedir)) - (thisstep nil) - (numchase 0) - ;; Don't make the following value larger than necessary. - ;; People expect an error message in a timely fashion when - ;; something is wrong; otherwise they might think that Emacs - ;; is hung. Of course, correctness has to come first. - (numchase-limit 20) - (result nil) ;result steps in reverse order - symlink-target) - (tramp-message-for-buffer - multi-method method user host - 10 "Finding true name for `%s'" filename) - (while (and steps (< numchase numchase-limit)) - (setq thisstep (pop steps)) - (tramp-message-for-buffer - multi-method method user host - 10 "Check %s" - (mapconcat 'identity - (append '("") (reverse result) (list thisstep)) - "/")) - (setq symlink-target - (nth 0 (file-attributes - (tramp-make-tramp-file-name - multi-method method user host - (mapconcat 'identity - (append '("") - (reverse result) - (list thisstep)) - "/"))))) - (cond ((string= "." thisstep) - (tramp-message-for-buffer multi-method method user host - 10 "Ignoring step `.'")) - ((string= ".." thisstep) - (tramp-message-for-buffer multi-method method user host - 10 "Processing step `..'") - (pop result)) - ((stringp symlink-target) - ;; It's a symlink, follow it. - (tramp-message-for-buffer - multi-method method user host - 10 "Follow symlink to %s" symlink-target) - (setq numchase (1+ numchase)) - (when (file-name-absolute-p symlink-target) - (setq result nil)) - ;; If the symlink was absolute, we'll get a string like - ;; "/user@host:/some/target"; extract the - ;; "/some/target" part from it. - (when (tramp-tramp-file-p symlink-target) - (with-parsed-tramp-file-name symlink-target sym - (unless (equal (list multi-method method user host) - (list sym-multi-method sym-method - sym-user sym-host)) - (error "Symlink target `%s' on wrong host" - symlink-target)) - (setq symlink-target localname))) - (setq steps - (append (tramp-split-string symlink-target "/") steps))) - (t - ;; It's a file. - (setq result (cons thisstep result))))) - (when (>= numchase numchase-limit) - (error "Maximum number (%d) of symlinks exceeded" numchase-limit)) - (setq result (reverse result)) - ;; Combine list to form string. - (setq result - (if result - (mapconcat 'identity (cons "" result) "/") - "/")) - (when (and is-dir (or (string= "" result) - (not (string= (substring result -1) "/")))) - (setq result (concat result "/"))) - (tramp-message-for-buffer - multi-method method user host - 10 "True name of `%s' is `%s'" filename result) - (tramp-make-tramp-file-name - multi-method method user host result)))) + (with-file-property v localname "file-truename" + (let* ((steps (tramp-split-string localname "/")) + (localnamedir (tramp-let-maybe directory-sep-char ?/ ;for XEmacs + (file-name-as-directory localname))) + (is-dir (string= localname localnamedir)) + (thisstep nil) + (numchase 0) + ;; Don't make the following value larger than necessary. + ;; People expect an error message in a timely fashion when + ;; something is wrong; otherwise they might think that Emacs + ;; is hung. Of course, correctness has to come first. + (numchase-limit 20) + (result nil) ;result steps in reverse order + symlink-target) + (tramp-message v 4 "Finding true name for `%s'" filename) + (while (and steps (< numchase numchase-limit)) + (setq thisstep (pop steps)) + (tramp-message + v 5 "Check %s" + (mapconcat 'identity + (append '("") (reverse result) (list thisstep)) + "/")) + (setq symlink-target + (nth 0 (file-attributes + (tramp-make-tramp-file-name + method user host + (mapconcat 'identity + (append '("") + (reverse result) + (list thisstep)) + "/"))))) + (cond ((string= "." thisstep) + (tramp-message v 5 "Ignoring step `.'")) + ((string= ".." thisstep) + (tramp-message v 5 "Processing step `..'") + (pop result)) + ((stringp symlink-target) + ;; It's a symlink, follow it. + (tramp-message v 5 "Follow symlink to %s" symlink-target) + (setq numchase (1+ numchase)) + (when (file-name-absolute-p symlink-target) + (setq result nil)) + ;; If the symlink was absolute, we'll get a string like + ;; "/user@host:/some/target"; extract the + ;; "/some/target" part from it. + (when (tramp-tramp-file-p symlink-target) + (unless (tramp-equal-remote filename symlink-target) + (tramp-error + v 'file-error + "Symlink target `%s' on wrong host" symlink-target)) + (setq symlink-target localname)) + (setq steps + (append (tramp-split-string symlink-target "/") + steps))) + (t + ;; It's a file. + (setq result (cons thisstep result))))) + (when (>= numchase numchase-limit) + (tramp-error + v 'file-error + "Maximum number (%d) of symlinks exceeded" numchase-limit)) + (setq result (reverse result)) + ;; Combine list to form string. + (setq result + (if result + (mapconcat 'identity (cons "" result) "/") + "/")) + (when (and is-dir (or (string= "" result) + (not (string= (substring result -1) "/")))) + (setq result (concat result "/"))) + (tramp-message v 4 "True name of `%s' is `%s'" filename result) + (tramp-make-tramp-file-name method user host result))))) ;; Basic functions. (defun tramp-handle-file-exists-p (filename) - "Like `file-exists-p' for tramp files." + "Like `file-exists-p' for Tramp files." (with-parsed-tramp-file-name filename nil - (save-excursion + (with-file-property v localname "file-exists-p" (zerop (tramp-send-command-and-check - multi-method method user host + v (format - (tramp-get-file-exists-command multi-method method user host) + "%s %s" + (tramp-get-file-exists-command v) (tramp-shell-quote-argument localname))))))) +;; Inodes don't exist for some file systems. Therefore we must +;; generate virtual ones. Used in `find-buffer-visiting'. The method +;; applied might be not so efficient (Ange-FTP uses hashes). But +;; performance isn't the major issue given that file transfer will +;; take time. +(defvar tramp-inodes nil + "Keeps virtual inodes numbers.") + ;; Devices must distinguish physical file systems. The device numbers ;; provided by "lstat" aren't unique, because we operate on different hosts. ;; So we use virtual device numbers, generated by Tramp. Both Ange-FTP and ;; EFS use device number "-1". In order to be different, we use device number -;; (-1 x), whereby "x" is unique for a given (multi-method method user host). +;; (-1 x), whereby "x" is unique for a given (method user host). (defvar tramp-devices nil "Keeps virtual device numbers.") @@ -2346,123 +2269,133 @@ ;; when something goes wrong. ;; Daniel Pittman (defun tramp-handle-file-attributes (filename &optional id-format) - "Like `file-attributes' for tramp files." - (when (file-exists-p filename) - ;; file exists, find out stuff - (unless id-format (setq id-format 'integer)) - (with-parsed-tramp-file-name filename nil - (save-excursion - (tramp-convert-file-attributes - multi-method method user host - (if (tramp-get-remote-perl multi-method method user host) - (tramp-handle-file-attributes-with-perl multi-method method user host - localname id-format) - (tramp-handle-file-attributes-with-ls multi-method method user host - localname id-format))))))) - -(defun tramp-handle-file-attributes-with-ls - (multi-method method user host localname &optional id-format) - "Implement `file-attributes' for tramp files using the ls(1) command." + "Like `file-attributes' for Tramp files." + (unless id-format (setq id-format 'integer)) + (with-parsed-tramp-file-name (expand-file-name filename) nil + (with-file-property v localname (format "file-attributes-%s" id-format) + (when (file-exists-p filename) + ;; file exists, find out stuff + (save-excursion + (tramp-convert-file-attributes + v + (if (tramp-get-remote-stat v) + (tramp-handle-file-attributes-with-stat v localname id-format) + (if (tramp-get-remote-perl v) + (tramp-handle-file-attributes-with-perl v localname id-format) + (tramp-handle-file-attributes-with-ls + v localname id-format))))))))) + +(defun tramp-handle-file-attributes-with-ls (vec localname &optional id-format) + "Implement `file-attributes' for Tramp files using the ls(1) command." (let (symlinkp dirp res-inode res-filemodes res-numlinks res-uid res-gid res-size res-symlink-target) - (tramp-message-for-buffer multi-method method user host 10 - "file attributes with ls: %s" - (tramp-make-tramp-file-name - multi-method method user host localname)) + (tramp-message vec 5 "file attributes with ls: %s" localname) (tramp-send-command - multi-method method user host + vec (format "%s %s %s" - (tramp-get-ls-command multi-method method user host) + (tramp-get-ls-command vec) (if (eq id-format 'integer) "-ildn" "-ild") (tramp-shell-quote-argument localname))) - (tramp-wait-for-output) ;; parse `ls -l' output ... - ;; ... inode - (setq res-inode - (condition-case err - (read (current-buffer)) - (invalid-read-syntax - (when (and (equal (cadr err) - "Integer constant overflow in reader") - (string-match - "^[0-9]+\\([0-9][0-9][0-9][0-9][0-9]\\)\\'" - (car (cddr err)))) - (let* ((big (read (substring (car (cddr err)) 0 - (match-beginning 1)))) - (small (read (match-string 1 (car (cddr err))))) - (twiddle (/ small 65536))) - (cons (+ big twiddle) - (- small (* twiddle 65536)))))))) - ;; ... file mode flags - (setq res-filemodes (symbol-name (read (current-buffer)))) - ;; ... number links - (setq res-numlinks (read (current-buffer))) - ;; ... uid and gid - (setq res-uid (read (current-buffer))) - (setq res-gid (read (current-buffer))) - (when (eq id-format 'integer) - (unless (numberp res-uid) (setq res-uid -1)) - (unless (numberp res-gid) (setq res-gid -1))) - ;; ... size - (setq res-size (read (current-buffer))) - ;; From the file modes, figure out other stuff. - (setq symlinkp (eq ?l (aref res-filemodes 0))) - (setq dirp (eq ?d (aref res-filemodes 0))) - ;; if symlink, find out file name pointed to - (when symlinkp - (search-forward "-> ") - (setq res-symlink-target - (buffer-substring (point) - (tramp-line-end-position)))) - ;; return data gathered - (list - ;; 0. t for directory, string (name linked to) for symbolic - ;; link, or nil. - (or dirp res-symlink-target nil) - ;; 1. Number of links to file. - res-numlinks - ;; 2. File uid. - res-uid - ;; 3. File gid. - res-gid - ;; 4. Last access time, as a list of two integers. First - ;; integer has high-order 16 bits of time, second has low 16 - ;; bits. - ;; 5. Last modification time, likewise. - ;; 6. Last status change time, likewise. - '(0 0) '(0 0) '(0 0) ;CCC how to find out? - ;; 7. Size in bytes (-1, if number is out of range). - res-size - ;; 8. File modes, as a string of ten letters or dashes as in ls -l. - res-filemodes - ;; 9. t iff file's gid would change if file were deleted and - ;; recreated. Will be set in `tramp-convert-file-attributes' - t - ;; 10. inode number. - res-inode - ;; 11. Device number. Will be replaced by a virtual device number. - -1 - ))) + (with-current-buffer (tramp-get-buffer vec) + (goto-char (point-min)) + ;; ... inode + (setq res-inode + (condition-case err + (read (current-buffer)) + (invalid-read-syntax + (when (and (equal (cadr err) + "Integer constant overflow in reader") + (string-match + "^[0-9]+\\([0-9][0-9][0-9][0-9][0-9]\\)\\'" + (car (cddr err)))) + (let* ((big (read (substring (car (cddr err)) 0 + (match-beginning 1)))) + (small (read (match-string 1 (car (cddr err))))) + (twiddle (/ small 65536))) + (cons (+ big twiddle) + (- small (* twiddle 65536)))))))) + ;; ... file mode flags + (setq res-filemodes (symbol-name (read (current-buffer)))) + ;; ... number links + (setq res-numlinks (read (current-buffer))) + ;; ... uid and gid + (setq res-uid (read (current-buffer))) + (setq res-gid (read (current-buffer))) + (if (eq id-format 'integer) + (progn + (unless (numberp res-uid) (setq res-uid -1)) + (unless (numberp res-gid) (setq res-gid -1))) + (progn + (unless (stringp res-uid) (setq res-uid (symbol-name res-uid))) + (unless (stringp res-gid) (setq res-gid (symbol-name res-gid))))) + ;; ... size + (setq res-size (read (current-buffer))) + ;; From the file modes, figure out other stuff. + (setq symlinkp (eq ?l (aref res-filemodes 0))) + (setq dirp (eq ?d (aref res-filemodes 0))) + ;; if symlink, find out file name pointed to + (when symlinkp + (search-forward "-> ") + (setq res-symlink-target + (buffer-substring (point) (tramp-line-end-position)))) + ;; return data gathered + (list + ;; 0. t for directory, string (name linked to) for symbolic + ;; link, or nil. + (or dirp res-symlink-target) + ;; 1. Number of links to file. + res-numlinks + ;; 2. File uid. + res-uid + ;; 3. File gid. + res-gid + ;; 4. Last access time, as a list of two integers. First + ;; integer has high-order 16 bits of time, second has low 16 + ;; bits. + ;; 5. Last modification time, likewise. + ;; 6. Last status change time, likewise. + '(0 0) '(0 0) '(0 0) ;CCC how to find out? + ;; 7. Size in bytes (-1, if number is out of range). + res-size + ;; 8. File modes, as a string of ten letters or dashes as in ls -l. + res-filemodes + ;; 9. t iff file's gid would change if file were deleted and + ;; recreated. Will be set in `tramp-convert-file-attributes' + t + ;; 10. inode number. + res-inode + ;; 11. Device number. Will be replaced by a virtual device number. + -1 + )))) (defun tramp-handle-file-attributes-with-perl - (multi-method method user host localname &optional id-format) - "Implement `file-attributes' for tramp files using a Perl script." - (tramp-message-for-buffer multi-method method user host 10 - "file attributes with perl: %s" - (tramp-make-tramp-file-name - multi-method method user host localname)) - (tramp-maybe-send-perl-script multi-method method user host - tramp-perl-file-attributes - "tramp_file_attributes") - (tramp-send-command multi-method method user host - (format "tramp_file_attributes %s %s" - (tramp-shell-quote-argument localname) id-format)) - (tramp-wait-for-output) - (read (current-buffer))) + (vec localname &optional id-format) + "Implement `file-attributes' for Tramp files using a Perl script." + (tramp-message vec 5 "file attributes with perl: %s" localname) + (tramp-maybe-send-script + vec tramp-perl-file-attributes "tramp_perl_file_attributes") + (tramp-send-command-and-read + vec + (format "tramp_perl_file_attributes %s %s" + (tramp-shell-quote-argument localname) id-format))) + +(defun tramp-handle-file-attributes-with-stat + (vec localname &optional id-format) + "Implement `file-attributes' for Tramp files using stat(1) command." + (tramp-message vec 5 "file attributes with stat: %s" localname) + (tramp-send-command-and-read + vec + (format + "%s -c '((\"%%N\") %%h %s %s %%X.0 %%Y.0 %%Z.0 %%s \"%%A\" t %%i.0 -1)' %s" + (tramp-get-remote-stat vec) + (if (eq id-format 'integer) "%u" "\"%U\"") + (if (eq id-format 'integer) "%g" "\"%G\"") + (tramp-shell-quote-argument localname)))) (defun tramp-handle-set-visited-file-modtime (&optional time-list) - "Like `set-visited-file-modtime' for tramp files." + "Like `set-visited-file-modtime' for Tramp files." (unless (buffer-file-name) (error "Can't set-visited-file-modtime: buffer `%s' not visiting a file" (buffer-name))) @@ -2480,16 +2413,16 @@ ;; `tramp-handle-file-attributes-with-ls'. (if (not (equal modtime '(0 0))) (tramp-run-real-handler 'set-visited-file-modtime (list modtime)) - (save-excursion + (progn (tramp-send-command - multi-method method user host + v (format "%s -ild %s" - (tramp-get-ls-command multi-method method user host) + (tramp-get-ls-command v) (tramp-shell-quote-argument localname))) - (tramp-wait-for-output) (setq attr (buffer-substring (point) (progn (end-of-line) (point))))) - (setq tramp-buffer-file-attributes attr)) + (tramp-set-file-property + v localname "visited-file-modtime-ild" attr)) (when (boundp 'last-coding-system-used) (set 'last-coding-system-used coding-system-used)) nil))))) @@ -2499,7 +2432,7 @@ ;; This function makes the same assumption as ;; `tramp-handle-set-visited-file-modtime'. (defun tramp-handle-verify-visited-file-modtime (buf) - "Like `verify-visited-file-modtime' for tramp files. + "Like `verify-visited-file-modtime' for Tramp files. At the time `verify-visited-file-modtime' calls this function, we already know that the buffer is visiting a file and that `visited-file-modtime' does not return 0. Do not call this @@ -2531,53 +2464,48 @@ 2)) ;; modtime has the don't know value. (attr - (save-excursion - (tramp-send-command - multi-method method user host - (format "%s -ild %s" - (tramp-get-ls-command multi-method method user host) - (tramp-shell-quote-argument localname))) - (tramp-wait-for-output) + (tramp-send-command + v + (format "%s -ild %s" + (tramp-get-ls-command v) + (tramp-shell-quote-argument localname))) + (with-current-buffer (tramp-get-buffer v) (setq attr (buffer-substring (point) (progn (end-of-line) (point))))) - (equal tramp-buffer-file-attributes attr)) + (equal + attr + (tramp-get-file-property + v localname "visited-file-modtime-ild" ""))) ;; If file does not exist, say it is not modified ;; if and only if that agrees with the buffer's record. (t (equal mt '(-1 65535)))))))))) (defun tramp-handle-set-file-modes (filename mode) - "Like `set-file-modes' for tramp files." + "Like `set-file-modes' for Tramp files." (with-parsed-tramp-file-name filename nil - (save-excursion - (unless (zerop (tramp-send-command-and-check - multi-method method user host - (format "chmod %s %s" - (tramp-decimal-to-octal mode) - (tramp-shell-quote-argument localname)))) - (signal 'file-error - (list "Doing chmod" - ;; FIXME: extract the proper text from chmod's stderr. - "error while changing file's mode" - filename)))))) + (tramp-flush-file-property v localname) + (unless (zerop (tramp-send-command-and-check + v + (format "chmod %s %s" + (tramp-decimal-to-octal mode) + (tramp-shell-quote-argument localname)))) + ;; FIXME: extract the proper text from chmod's stderr. + (tramp-error + v 'file-error "Error while changing file's mode %s" filename)))) ;; Simple functions using the `test' command. (defun tramp-handle-file-executable-p (filename) - "Like `file-executable-p' for tramp files." + "Like `file-executable-p' for Tramp files." (with-parsed-tramp-file-name filename nil - (zerop (tramp-run-test "-x" filename)))) + (with-file-property v localname "file-executable-p" + (zerop (tramp-run-test "-x" filename))))) (defun tramp-handle-file-readable-p (filename) - "Like `file-readable-p' for tramp files." + "Like `file-readable-p' for Tramp files." (with-parsed-tramp-file-name filename nil - (zerop (tramp-run-test "-r" filename)))) - -(defun tramp-handle-file-accessible-directory-p (filename) - "Like `file-accessible-directory-p' for tramp files." - (with-parsed-tramp-file-name filename nil - (and (zerop (tramp-run-test "-d" filename)) - (zerop (tramp-run-test "-r" filename)) - (zerop (tramp-run-test "-x" filename))))) + (with-file-property v localname "file-readable-p" + (zerop (tramp-run-test "-r" filename))))) ;; When the remote shell is started, it looks for a shell which groks ;; tilde expansion. Here, we assume that all shells which grok tilde @@ -2585,7 +2513,7 @@ ;; newer than). If this breaks, tell me about it and I'll try to do ;; something smarter about it. (defun tramp-handle-file-newer-than-file-p (file1 file2) - "Like `file-newer-than-file-p' for tramp files." + "Like `file-newer-than-file-p' for Tramp files." (cond ((not (file-exists-p file1)) nil) ((not (file-exists-p file2)) @@ -2606,44 +2534,27 @@ ;; However, this only works if both files are Tramp ;; files and both have the same method, same user, same ;; host. - (unless (and (tramp-tramp-file-p file1) - (tramp-tramp-file-p file2)) - (signal - 'file-error - (list - "Cannot check if Tramp file is newer than non-Tramp file" - file1 file2))) - (with-parsed-tramp-file-name file1 v1 - (with-parsed-tramp-file-name file2 v2 - (unless (and (equal v1-multi-method v2-multi-method) - (equal v1-method v2-method) - (equal v1-user v2-user) - (equal v1-host v2-host)) - (signal 'file-error - (list "Files must have same method, user, host" - file1 file2))) - (unless (and (tramp-tramp-file-p file1) - (tramp-tramp-file-p file2)) - (signal 'file-error - (list "Files must be tramp files on same host" - file1 file2))) - (if (tramp-get-test-groks-nt - v1-multi-method v1-method v1-user v1-host) - (zerop (tramp-run-test2 "test" file1 file2 "-nt")) - (zerop (tramp-run-test2 - "tramp_test_nt" file1 file2))))))))))) + (unless (tramp-equal-remote file1 file2) + (with-parsed-tramp-file-name + (if (tramp-tramp-file-p file1) file1 file2) nil + (tramp-error + v 'file-error + "Files %s and %s must have same method, user, host" + file1 file2))) + (with-parsed-tramp-file-name file1 nil + (zerop (tramp-run-test2 + (tramp-get-test-nt-command v) file1 file2))))))))) ;; Functions implemented using the basic functions above. (defun tramp-handle-file-modes (filename) - "Like `file-modes' for tramp files." - (with-parsed-tramp-file-name filename nil - (when (file-exists-p filename) - (tramp-mode-string-to-int - (nth 8 (file-attributes filename)))))) + "Like `file-modes' for Tramp files." + (when (file-exists-p filename) + (tramp-mode-string-to-int + (nth 8 (file-attributes filename))))) (defun tramp-handle-file-directory-p (filename) - "Like `file-directory-p' for tramp files." + "Like `file-directory-p' for Tramp files." ;; Care must be taken that this function returns `t' for symlinks ;; pointing to directories. Surely the most obvious implementation ;; would be `test -d', but that returns false for such symlinks. @@ -2653,78 +2564,52 @@ ;; ;; Alternatives: `cd %s', `test -d %s' (with-parsed-tramp-file-name filename nil - (save-excursion - (zerop - (tramp-send-command-and-check - multi-method method user host - (format "test -d %s" - (tramp-shell-quote-argument localname)) - t))))) ;run command in subshell + (with-file-property v localname "file-directory-p" + (zerop (tramp-run-test "-d" filename))))) (defun tramp-handle-file-regular-p (filename) - "Like `file-regular-p' for tramp files." - (with-parsed-tramp-file-name filename nil - (and (file-exists-p filename) - (eq ?- (aref (nth 8 (file-attributes filename)) 0))))) + "Like `file-regular-p' for Tramp files." + (and (file-exists-p filename) + (eq ?- (aref (nth 8 (file-attributes filename)) 0)))) (defun tramp-handle-file-symlink-p (filename) - "Like `file-symlink-p' for tramp files." + "Like `file-symlink-p' for Tramp files." (with-parsed-tramp-file-name filename nil (let ((x (car (file-attributes filename)))) (when (stringp x) ;; When Tramp is running on VMS, then `file-name-absolute-p' ;; might do weird things. (if (file-name-absolute-p x) - (tramp-make-tramp-file-name - multi-method method user host x) + (tramp-make-tramp-file-name method user host x) x))))) (defun tramp-handle-file-writable-p (filename) - "Like `file-writable-p' for tramp files." + "Like `file-writable-p' for Tramp files." (with-parsed-tramp-file-name filename nil - (if (file-exists-p filename) - ;; Existing files must be writable. - (zerop (tramp-run-test "-w" filename)) - ;; If file doesn't exist, check if directory is writable. - (and (zerop (tramp-run-test - "-d" (file-name-directory filename))) - (zerop (tramp-run-test - "-w" (file-name-directory filename))))))) + (with-file-property v localname "file-writable-p" + (if (file-exists-p filename) + ;; Existing files must be writable. + (zerop (tramp-run-test "-w" filename)) + ;; If file doesn't exist, check if directory is writable. + (and (zerop (tramp-run-test + "-d" (file-name-directory filename))) + (zerop (tramp-run-test + "-w" (file-name-directory filename)))))))) (defun tramp-handle-file-ownership-preserved-p (filename) - "Like `file-ownership-preserved-p' for tramp files." + "Like `file-ownership-preserved-p' for Tramp files." (with-parsed-tramp-file-name filename nil - (let ((attributes (file-attributes filename))) - ;; Return t if the file doesn't exist, since it's true that no - ;; information would be lost by an (attempted) delete and create. - (or (null attributes) - (= (nth 2 attributes) - (tramp-get-remote-uid multi-method method user host)))))) + (with-file-property v localname "file-ownership-preserved-p" + (let ((attributes (file-attributes filename))) + ;; Return t if the file doesn't exist, since it's true that no + ;; information would be lost by an (attempted) delete and create. + (or (null attributes) + (= (nth 2 attributes) (tramp-get-remote-uid v 'integer))))))) ;; Other file name ops. -;; ;; Matthias K,Av(Bppe -;; (defun tramp-handle-directory-file-name (directory) -;; "Like `directory-file-name' for tramp files." -;; (if (and (eq (aref directory (- (length directory) 1)) ?/) -;; (not (eq (aref directory (- (length directory) 2)) ?:))) -;; (substring directory 0 (- (length directory) 1)) -;; directory)) - -;; ;; Philippe Troin -;; (defun tramp-handle-directory-file-name (directory) -;; "Like `directory-file-name' for tramp files." -;; (with-parsed-tramp-file-name directory nil -;; (let ((directory-length-1 (1- (length directory)))) -;; (save-match-data -;; (if (and (eq (aref directory directory-length-1) ?/) -;; (eq (string-match tramp-file-name-regexp directory) 0) -;; (/= (match-end 0) directory-length-1)) -;; (substring directory 0 directory-length-1) -;; directory))))) - (defun tramp-handle-directory-file-name (directory) - "Like `directory-file-name' for tramp files." + "Like `directory-file-name' for Tramp files." ;; If localname component of filename is "/", leave it unchanged. ;; Otherwise, remove any trailing slash from localname component. ;; Method, host, etc, are unchanged. Does it make sense to try @@ -2738,145 +2623,137 @@ ;; Directory listings. -(defun tramp-handle-directory-files (directory - &optional full match nosort files-only) - "Like `directory-files' for tramp files." - (with-parsed-tramp-file-name directory nil - (let (result x) - (save-excursion - (tramp-barf-unless-okay - multi-method method user host - (concat "cd " (tramp-shell-quote-argument localname)) - nil - 'file-error - "tramp-handle-directory-files: couldn't `cd %s'" - (tramp-shell-quote-argument localname)) - (tramp-send-command - multi-method method user host - (concat (tramp-get-ls-command multi-method method user host) - " -a | cat")) - (tramp-wait-for-output) - (goto-char (point-max)) - (while (zerop (forward-line -1)) - (setq x (buffer-substring (point) - (tramp-line-end-position))) - (when (or (not match) (string-match match x)) - (if full - (push (concat (file-name-as-directory directory) - x) - result) - (push x result)))) - (tramp-send-command multi-method method user host "cd") - (tramp-wait-for-output) - ;; Remove non-files or non-directories if necessary. Using - ;; the remote shell for this would probably be way faster. - ;; Maybe something could be adapted from - ;; tramp-handle-file-name-all-completions. - (when files-only - (let ((temp (nreverse result)) - item) - (setq result nil) - (if (equal files-only t) - ;; files only - (while temp - (setq item (pop temp)) - (when (file-regular-p item) - (push item result))) - ;; directories only - (while temp - (setq item (pop temp)) - (when (file-directory-p item) - (push item result))))))) +(defun tramp-handle-directory-files + (directory &optional full match nosort files-only) + "Like `directory-files' for Tramp files." + ;; FILES-ONLY is valid for XEmacs only. + (when (file-directory-p directory) + (setq directory (expand-file-name directory)) + (let ((temp (nreverse (file-name-all-completions "" directory))) + result item) + + (while temp + (setq item (directory-file-name (pop temp))) + (when (and (or (null match) (string-match match item)) + (or (null files-only) + ;; files only + (and (equal files-only t) (file-regular-p item)) + ;; directories only + (file-directory-p item))) + (push (if full (expand-file-name item directory) item) + result))) result))) (defun tramp-handle-directory-files-and-attributes (directory &optional full match nosort id-format) - "Like `directory-files-and-attributes' for tramp files." - (when (tramp-handle-file-exists-p directory) - (save-excursion - (setq directory (tramp-handle-expand-file-name directory)) - (with-parsed-tramp-file-name directory nil - (tramp-maybe-send-perl-script multi-method method user host - tramp-perl-directory-files-and-attributes - "tramp_directory_files_and_attributes") - (tramp-send-command multi-method method user host - (format "tramp_directory_files_and_attributes %s %s" - (tramp-shell-quote-argument localname) - (or id-format 'integer))) - (tramp-wait-for-output) - (let* ((root (cons nil (let ((object (read (current-buffer)))) - (when (stringp object) - (error object)) - object))) - (cell root)) - (while (cdr cell) - (if (and match (not (string-match match (car (cadr cell))))) - ;; Remove from list - (setcdr cell (cddr cell)) - ;; Include in list - (setq cell (cdr cell)) - (let ((l (car cell))) - (tramp-convert-file-attributes multi-method method user host - (cdr l)) - ;; If FULL, make file name absolute - (when full (setcar l (concat directory "/" (car l))))))) - (if nosort - (cdr root) - (sort (cdr root) (lambda (x y) (string< (car x) (car y)))))))))) + "Like `directory-files-and-attributes' for Tramp files." + (unless id-format (setq id-format 'integer)) + (when (file-directory-p directory) + (setq directory (expand-file-name directory)) + (let* ((temp + (copy-tree + (with-parsed-tramp-file-name directory nil + (with-file-property + v localname + (format "directory-files-and-attributes-%s" id-format) + (save-excursion + (mapcar + '(lambda (x) + (cons (car x) + (tramp-convert-file-attributes v (cdr x)))) + (if (tramp-get-remote-stat v) + (tramp-handle-directory-files-and-attributes-with-stat + v localname id-format) + (if (tramp-get-remote-perl v) + (tramp-handle-directory-files-and-attributes-with-perl + v localname id-format))))))))) + result item) + + (while temp + (setq item (pop temp)) + (when (or (null match) (string-match match (car item))) + (when full + (setcar item (expand-file-name (car item) directory))) + (push item result))) + + (if nosort + result + (sort result (lambda (x y) (string< (car x) (car y)))))))) + +(defun tramp-handle-directory-files-and-attributes-with-perl + (vec localname &optional id-format) + "Implement `directory-files-and-attributes' for Tramp files using a Perl script." + (tramp-message vec 5 "directory-files-and-attributes with perl: %s" localname) + (tramp-maybe-send-script + vec tramp-perl-directory-files-and-attributes + "tramp_perl_directory_files_and_attributes") + (let ((object + (tramp-send-command-and-read + vec + (format "tramp_perl_directory_files_and_attributes %s %s" + (tramp-shell-quote-argument localname) id-format)))) + (when (stringp object) (tramp-error vec 'file-error object)) + object)) + +(defun tramp-handle-directory-files-and-attributes-with-stat + (vec localname &optional id-format) + "Implement `directory-files-and-attributes' for Tramp files using stat(1) command." + (tramp-message vec 5 "directory-files-and-attributes with stat: %s" localname) + (tramp-send-command-and-read + vec + (format + (concat + "cd %s; echo \"(\"; (%s -ab | xargs " + "%s -c '(\"%%n\" (\"%%N\") %%h %s %s %%X.0 %%Y.0 %%Z.0 %%s \"%%A\" t %%i.0 -1)'); " + "echo \")\"") + (tramp-shell-quote-argument localname) + (tramp-get-ls-command vec) + (tramp-get-remote-stat vec) + (if (eq id-format 'integer) "%u" "\"%U\"") + (if (eq id-format 'integer) "%g" "\"%G\"")))) ;; This function should return "foo/" for directories and "bar" for -;; files. We use `ls -ad' to get a list of files (including -;; directories), and `find . -type d \! -name . -prune' to get a list -;; of directories. +;; files. (defun tramp-handle-file-name-all-completions (filename directory) - "Like `file-name-all-completions' for tramp files." - (with-parsed-tramp-file-name directory nil - (unless (save-match-data (string-match "/" filename)) - (let* ((nowild tramp-completion-without-shell-p) - result) - (save-excursion - (tramp-barf-unless-okay - multi-method method user host - (format "cd %s" (tramp-shell-quote-argument localname)) - nil 'file-error - "tramp-handle-file-name-all-completions: Couldn't `cd %s'" - (tramp-shell-quote-argument localname)) - - ;; Get a list of directories and files, including reliably - ;; tagging the directories with a trailing '/'. Because I - ;; rock. --daniel@danann.net - (tramp-send-command - multi-method method user host - (format (concat "%s -a %s 2>/dev/null | while read f; do " - "if test -d \"$f\" 2>/dev/null; " - "then echo \"$f/\"; else echo \"$f\"; fi; done") - (tramp-get-ls-command multi-method method user host) - (if (or nowild (zerop (length filename))) - "" - (format "-d %s*" - (tramp-shell-quote-argument filename))))) - - ;; Now grab the output. - (tramp-wait-for-output) - (goto-char (point-max)) - (while (zerop (forward-line -1)) - (push (buffer-substring (point) - (tramp-line-end-position)) - result)) - - (tramp-send-command multi-method method user host "cd") - (tramp-wait-for-output) - - ;; Return the list. - (if nowild - (all-completions filename (mapcar 'list result)) - result)))))) - + "Like `file-name-all-completions' for Tramp files." + (unless (save-match-data (string-match "/" filename)) + (with-parsed-tramp-file-name directory nil + (all-completions + filename + (mapcar + 'list + (with-file-property v localname "file-name-all-completions" + (let (result) + (tramp-barf-unless-okay + v + (format "cd %s" (tramp-shell-quote-argument localname)) + "tramp-handle-file-name-all-completions: Couldn't `cd %s'" + (tramp-shell-quote-argument localname)) + + ;; Get a list of directories and files, including reliably + ;; tagging the directories with a trailing '/'. Because I + ;; rock. --daniel@danann.net + (tramp-send-command + v + (format (concat "%s -ab 2>/dev/null | while read f; do " + "if %s -d \"$f\" 2>/dev/null; " + "then echo \"$f/\"; else echo \"$f\"; fi; done") + (tramp-get-ls-command v) + (tramp-get-test-command v))) + + ;; Now grab the output. + (with-current-buffer (tramp-get-buffer v) + (goto-char (point-max)) + (while (zerop (forward-line -1)) + (push (buffer-substring (point) (tramp-line-end-position)) + result))) + + result))))))) ;; The following isn't needed for Emacs 20 but for 19.34? (defun tramp-handle-file-name-completion (filename directory &optional predicate) - "Like `file-name-completion' for tramp files." + "Like `file-name-completion' for Tramp files." (unless (tramp-tramp-file-p directory) (error "tramp-handle-file-name-completion invoked on non-tramp directory `%s'" @@ -2891,18 +2768,17 @@ (defun tramp-handle-add-name-to-file (filename newname &optional ok-if-already-exists) - "Like `add-name-to-file' for tramp files." + "Like `add-name-to-file' for Tramp files." + (unless (tramp-equal-remote filename newname) + (with-parsed-tramp-file-name + (if (tramp-tramp-file-p filename) filename newname) nil + (tramp-error + v 'file-error + "add-name-to-file: %s" + "only implemented for same method, same user, same host"))) (with-parsed-tramp-file-name filename v1 (with-parsed-tramp-file-name newname v2 - (let ((ln (when v1 (tramp-get-remote-ln - v1-multi-method v1-method v1-user v1-host)))) - (unless (and v1-method v2-method v1-user v2-user v1-host v2-host - (equal v1-multi-method v2-multi-method) - (equal v1-method v2-method) - (equal v1-user v2-user) - (equal v1-host v2-host)) - (error "add-name-to-file: %s" - "only implemented for same method, same user, same host")) + (let ((ln (when v1 (tramp-get-remote-ln v1)))) (when (and (not ok-if-already-exists) (file-exists-p newname) (not (numberp ok-if-already-exists)) @@ -2910,18 +2786,20 @@ (format "File %s already exists; make it a new name anyway? " newname))) - (error "add-name-to-file: file %s already exists" newname)) + (tramp-error + v2 'file-error + "add-name-to-file: file %s already exists" newname)) + (tramp-flush-file-property v2 v2-localname) (tramp-barf-unless-okay - v1-multi-method v1-method v1-user v1-host + v1 (format "%s %s %s" ln (tramp-shell-quote-argument v1-localname) (tramp-shell-quote-argument v2-localname)) - nil 'file-error "error with add-name-to-file, see buffer `%s' for details" (buffer-name)))))) (defun tramp-handle-copy-file (filename newname &optional ok-if-already-exists keep-date) - "Like `copy-file' for tramp files." + "Like `copy-file' for Tramp files." ;; Check if both files are local -- invoke normal copy-file. ;; Otherwise, use tramp from local system. (setq filename (expand-file-name filename)) @@ -2932,12 +2810,11 @@ (tramp-do-copy-or-rename-file 'copy filename newname ok-if-already-exists keep-date) (tramp-run-real-handler - 'copy-file - (list filename newname ok-if-already-exists keep-date)))) + 'copy-file (list filename newname ok-if-already-exists keep-date)))) (defun tramp-handle-rename-file (filename newname &optional ok-if-already-exists) - "Like `rename-file' for tramp files." + "Like `rename-file' for Tramp files." ;; Check if both files are local -- invoke normal rename-file. ;; Otherwise, use tramp from local system. (setq filename (expand-file-name filename)) @@ -2946,9 +2823,9 @@ (if (or (tramp-tramp-file-p filename) (tramp-tramp-file-p newname)) (tramp-do-copy-or-rename-file - 'rename filename newname ok-if-already-exists) - (tramp-run-real-handler 'rename-file - (list filename newname ok-if-already-exists)))) + 'rename filename newname ok-if-already-exists t) + (tramp-run-real-handler + 'rename-file (list filename newname ok-if-already-exists)))) (defun tramp-do-copy-or-rename-file (op filename newname &optional ok-if-already-exists keep-date) @@ -2965,169 +2842,148 @@ and `rename'. FILENAME and NEWNAME must be absolute file names." (unless (memq op '(copy rename)) (error "Unknown operation `%s', must be `copy' or `rename'" op)) - (unless ok-if-already-exists - (when (file-exists-p newname) - (signal 'file-already-exists - (list "File already exists" newname)))) (let ((t1 (tramp-tramp-file-p filename)) - (t2 (tramp-tramp-file-p newname)) - v1-multi-method v1-method v1-user v1-host v1-localname - v2-multi-method v2-method v2-user v2-host v2-localname) - - ;; Check which ones of source and target are Tramp files. - ;; We cannot invoke `with-parsed-tramp-file-name'; - ;; it fails if the file isn't a Tramp file name. - (if t1 - (with-parsed-tramp-file-name filename l - (setq v1-multi-method l-multi-method - v1-method l-method - v1-user l-user - v1-host l-host - v1-localname l-localname)) - (setq v1-localname filename)) - (if t2 - (with-parsed-tramp-file-name newname l - (setq v2-multi-method l-multi-method - v2-method l-method - v2-user l-user - v2-host l-host - v2-localname l-localname)) - (setq v2-localname newname)) - - (cond - ;; Both are Tramp files. - ((and t1 t2) - (cond - ;; Shortcut: if method, host, user are the same for both - ;; files, we invoke `cp' or `mv' on the remote host - ;; directly. - ((and (equal v1-multi-method v2-multi-method) - (equal v1-method v2-method) - (equal v1-user v2-user) - (equal v1-host v2-host)) - (tramp-do-copy-or-rename-file-directly - op v1-multi-method v1-method v1-user v1-host - v1-localname v2-localname keep-date)) - ;; If both source and target are Tramp files, - ;; both are using the same copy-program, then we - ;; can invoke rcp directly. Note that - ;; default-directory should point to a local - ;; directory if we want to invoke rcp. - ((and (not v1-multi-method) - (not v2-multi-method) - (equal v1-method v2-method) - (tramp-method-out-of-band-p - v1-multi-method v1-method v1-user v1-host) - (not (string-match "\\([^#]*\\)#\\(.*\\)" v1-host)) - (not (string-match "\\([^#]*\\)#\\(.*\\)" v2-host))) - (tramp-do-copy-or-rename-file-out-of-band - op filename newname keep-date)) - ;; No shortcut was possible. So we copy the - ;; file first. If the operation was `rename', we go - ;; back and delete the original file (if the copy was - ;; successful). The approach is simple-minded: we - ;; create a new buffer, insert the contents of the - ;; source file into it, then write out the buffer to - ;; the target file. The advantage is that it doesn't - ;; matter which filename handlers are used for the - ;; source and target file. - (t - (tramp-do-copy-or-rename-file-via-buffer - op filename newname keep-date)))) - - ;; One file is a Tramp file, the other one is local. - ((or t1 t2) - ;; If the Tramp file has an out-of-band method, the corresponding - ;; copy-program can be invoked. - (if (and (not v1-multi-method) - (not v2-multi-method) - (or (and t1 (tramp-method-out-of-band-p - v1-multi-method v1-method v1-user v1-host)) - (and t2 (tramp-method-out-of-band-p - v2-multi-method v2-method v2-user v2-host)))) - (tramp-do-copy-or-rename-file-out-of-band - op filename newname keep-date) - ;; Use the generic method via a Tramp buffer. - (tramp-do-copy-or-rename-file-via-buffer - op filename newname keep-date))) - - (t - ;; One of them must be a Tramp file. - (error "Tramp implementation says this cannot happen"))))) + (t2 (tramp-tramp-file-p newname))) + + (unless ok-if-already-exists + (when (and t2 (file-exists-p newname)) + (with-parsed-tramp-file-name newname nil + (tramp-error + v 'file-already-exists "File %s already exists" newname)))) + + (prog1 + (cond + ;; Both are Tramp files. + ((and t1 t2) + (with-parsed-tramp-file-name filename v1 + (with-parsed-tramp-file-name newname v2 + (cond + ;; Shortcut: if method, host, user are the same for both + ;; files, we invoke `cp' or `mv' on the remote host + ;; directly. + ((tramp-equal-remote filename newname) + (tramp-do-copy-or-rename-file-directly + op v1 v1-localname v2-localname keep-date)) + ;; If both source and target are Tramp files, + ;; both are using the same copy-program, then we + ;; can invoke rcp directly. Note that + ;; default-directory should point to a local + ;; directory if we want to invoke rcp. + ((and (equal v1-method v2-method) + (tramp-method-out-of-band-p v1) + (> (nth 7 (file-attributes filename)) + tramp-copy-size-limit)) + (tramp-do-copy-or-rename-file-out-of-band + op filename newname keep-date)) + ;; No shortcut was possible. So we copy the + ;; file first. If the operation was `rename', we go + ;; back and delete the original file (if the copy was + ;; successful). The approach is simple-minded: we + ;; create a new buffer, insert the contents of the + ;; source file into it, then write out the buffer to + ;; the target file. The advantage is that it doesn't + ;; matter which filename handlers are used for the + ;; source and target file. + (t + (tramp-do-copy-or-rename-file-via-buffer + op filename newname keep-date)))))) + + ;; One file is a Tramp file, the other one is local. + ((or t1 t2) + (with-parsed-tramp-file-name (if t1 filename newname) nil + ;; If the Tramp file has an out-of-band method, the corresponding + ;; copy-program can be invoked. + (if (and (tramp-method-out-of-band-p v) + (> (nth 7 (file-attributes filename)) + tramp-copy-size-limit)) + (tramp-do-copy-or-rename-file-out-of-band + op filename newname keep-date) + ;; Use the generic method via a Tramp buffer. + (tramp-do-copy-or-rename-file-via-buffer + op filename newname keep-date)))) + + (t + ;; One of them must be a Tramp file. + (error "Tramp implementation says this cannot happen"))) + ;; When newname did exist, we have wrong cached values. + (when t2 + (with-parsed-tramp-file-name newname nil + (tramp-flush-file-property v localname)))))) (defun tramp-do-copy-or-rename-file-via-buffer (op filename newname keep-date) "Use an Emacs buffer to copy or rename a file. First arg OP is either `copy' or `rename' and indicates the operation. FILENAME is the source file, NEWNAME the target file. KEEP-DATE is non-nil if NEWNAME should have the same timestamp as FILENAME." - (let ((trampbuf (get-buffer-create "*tramp output*")) - (modtime (nth 5 (file-attributes filename)))) - (when (and keep-date (or (null modtime) (equal modtime '(0 0)))) - (tramp-message - 1 (concat "Warning: cannot preserve file time stamp" - " with inline copying across machines"))) - (save-excursion - (set-buffer trampbuf) (erase-buffer) - (insert-file-contents-literally filename) - ;; We don't want the target file to be compressed, so we let-bind - ;; `jka-compr-inhibit' to t. - (let ((coding-system-for-write 'binary) - (jka-compr-inhibit t)) - (write-region (point-min) (point-max) newname)) - ;; KEEP-DATE handling. - (when keep-date - (when (and (not (null modtime)) - (not (equal modtime '(0 0)))) - (tramp-touch newname modtime))) - ;; Set the mode. - (set-file-modes newname (file-modes filename))) + (let ((modtime (nth 5 (file-attributes filename)))) + (unwind-protect + (with-temp-buffer + (let ((coding-system-for-read 'binary)) + (insert-file-contents-literally filename)) + ;; We don't want the target file to be compressed, so we + ;; let-bind `jka-compr-inhibit' to t. + (let ((coding-system-for-write 'binary) + (jka-compr-inhibit t)) + (write-region (point-min) (point-max) newname)))) + ;; KEEP-DATE handling. + (when keep-date + (when (and (not (null modtime)) + (not (equal modtime '(0 0)))) + (tramp-touch newname modtime))) + ;; Set the mode. + (set-file-modes newname (file-modes filename)) ;; If the operation was `rename', delete the original file. (unless (eq op 'copy) (delete-file filename)))) (defun tramp-do-copy-or-rename-file-directly - (op multi-method method user host localname1 localname2 keep-date) + (op vec localname1 localname2 keep-date) "Invokes `cp' or `mv' on the remote system. OP must be one of `copy' or `rename', indicating `cp' or `mv', -respectively. METHOD, USER, and HOST specify the connection. -LOCALNAME1 and LOCALNAME2 specify the two arguments of `cp' or `mv'. -If KEEP-DATE is non-nil, preserve the time stamp when copying." +respectively. VEC specifies the connection. LOCALNAME1 and +LOCALNAME2 specify the two arguments of `cp' or `mv'. If +KEEP-DATE is non-nil, preserve the time stamp when copying." ;; CCC: What happens to the timestamp when renaming? (let ((cmd (cond ((and (eq op 'copy) keep-date) "cp -f -p") ((eq op 'copy) "cp -f") ((eq op 'rename) "mv -f") - (t (error + (t (tramp-error + vec 'file-error "Unknown operation `%s', must be `copy' or `rename'" op))))) - (save-excursion - (tramp-send-command - multi-method method user host - (format "%s %s %s" - cmd - (tramp-shell-quote-argument localname1) - (tramp-shell-quote-argument localname2))) - (tramp-wait-for-output) + (tramp-send-command + vec + (format "%s %s %s" + cmd + (tramp-shell-quote-argument localname1) + (tramp-shell-quote-argument localname2))) + (with-current-buffer (tramp-get-buffer vec) (goto-char (point-min)) (unless (or (and (eq op 'copy) keep-date ;; Mask cp -f error. (re-search-forward tramp-operation-not-permitted-regexp nil t)) - (zerop (tramp-send-command-and-check - multi-method method user host nil nil))) - (pop-to-buffer (current-buffer)) - (signal 'file-error - (format "Copying directly failed, see buffer `%s' for details." - (buffer-name))))) + (zerop (tramp-send-command-and-check vec nil))) + (tramp-error-with-buffer + nil vec 'file-error + "Copying directly failed, see buffer `%s' for details." + (buffer-name)))) ;; Set the mode. ;; CCC: Maybe `chmod --reference=localname1 localname2' could be used ;; where available? (unless (or (eq op 'rename) keep-date) (set-file-modes - (tramp-make-tramp-file-name multi-method method user host localname2) - (file-modes - (tramp-make-tramp-file-name - multi-method method user host localname1)))))) + (tramp-make-tramp-file-name + (tramp-file-name-method vec) + (tramp-file-name-user vec) + (tramp-file-name-host vec) + localname2) + (file-modes (tramp-make-tramp-file-name + (tramp-file-name-method vec) + (tramp-file-name-user vec) + (tramp-file-name-host vec) + localname1)))))) (defun tramp-do-copy-or-rename-file-out-of-band (op filename newname keep-date) "Invoke rcp program to copy. @@ -3135,176 +2991,137 @@ be a local filename. The method used must be an out-of-band method." (let ((t1 (tramp-tramp-file-p filename)) (t2 (tramp-tramp-file-p newname)) - v1-multi-method v1-method v1-user v1-host v1-localname - v2-multi-method v2-method v2-user v2-host v2-localname - multi-method method user host copy-program copy-args - source target trampbuf) - - ;; Check which ones of source and target are Tramp files. - ;; We cannot invoke `with-parsed-tramp-file-name'; - ;; it fails if the file isn't a Tramp file name. - (if t1 - (with-parsed-tramp-file-name filename l - (setq v1-multi-method l-multi-method - v1-method l-method - v1-user l-user - v1-host l-host - v1-localname l-localname - multi-method l-multi-method - method (tramp-find-method - v1-multi-method v1-method v1-user v1-host) - user l-user - host l-host - copy-program (tramp-get-method-parameter - v1-multi-method method - v1-user v1-host 'tramp-copy-program) - copy-args (tramp-get-method-parameter - v1-multi-method method - v1-user v1-host 'tramp-copy-args))) - (setq v1-localname filename)) - - (if t2 - (with-parsed-tramp-file-name newname l - (setq v2-multi-method l-multi-method - v2-method l-method - v2-user l-user - v2-host l-host - v2-localname l-localname - multi-method l-multi-method - method (tramp-find-method - v2-multi-method v2-method v2-user v2-host) - user l-user - host l-host - copy-program (tramp-get-method-parameter - v2-multi-method method - v2-user v2-host 'tramp-copy-program) - copy-args (tramp-get-method-parameter - v2-multi-method method - v2-user v2-host 'tramp-copy-args))) - (setq v2-localname newname)) - - ;; The following should be changed. We need a more general - ;; mechanism to parse extra host args. - (if (not t1) - (setq source v1-localname) - (when (string-match "\\([^#]*\\)#\\(.*\\)" v1-host) - (setq copy-args (cons "-P" (cons (match-string 2 v1-host) copy-args))) - (setq v1-host (match-string 1 v1-host))) - (setq source - (tramp-make-copy-program-file-name - v1-user v1-host - (tramp-shell-quote-argument v1-localname)))) - - (if (not t2) - (setq target v2-localname) - (when (string-match "\\([^#]*\\)#\\(.*\\)" v2-host) - (setq copy-args (cons "-P" (cons (match-string 2 v2-host) copy-args))) - (setq v2-host (match-string 1 v2-host))) - (setq target - (tramp-make-copy-program-file-name - v2-user v2-host - (tramp-shell-quote-argument v2-localname)))) - - ;; Handle ControlMaster/ControlPath - (setq copy-args - (mapcar - (lambda (x) - (format-spec - x `((?t . ,(format "/tmp/%s" tramp-temp-name-prefix))))) - copy-args)) - - ;; Handle keep-date argument - (when keep-date - (if t1 - (setq copy-args - (cons (tramp-get-method-parameter - v1-multi-method method - v1-user v1-host 'tramp-copy-keep-date-arg) - copy-args)) - (setq copy-args - (cons (tramp-get-method-parameter - v2-multi-method method - v2-user v2-host 'tramp-copy-keep-date-arg) - copy-args)))) - - (setq copy-args (append copy-args (list source target)) - trampbuf (generate-new-buffer - (tramp-buffer-name multi-method method user host))) - - ;; Use an asynchronous process. By this, password can be handled. - (save-excursion + copy-program copy-args copy-keep-date port spec + source target) + + (with-parsed-tramp-file-name (if t1 filename newname) nil + + ;; Expand hops. Might be necessary for gateway methods. + (setq v (car (tramp-compute-multi-hops v))) + (aset v 3 localname) + + ;; Check which ones of source and target are Tramp files. + (setq source (if t1 (tramp-make-copy-program-file-name v) filename) + target (if t2 (tramp-make-copy-program-file-name v) newname)) + + ;; Check for port number. Until now, there's no need for handling + ;; like method, user, host. + (setq host (tramp-file-name-real-host v) + port (tramp-file-name-port v) + port (or (and port (number-to-string port)) "")) + + ;; Compose copy command. + (setq spec `((?h . ,host) (?u . ,user) (?p . ,port) + (?t . ,(tramp-make-tramp-temp-file v)) + (?k . ,(if keep-date " " ""))) + copy-program (tramp-get-method-parameter + method 'tramp-copy-program) + copy-keep-date (tramp-get-method-parameter + method 'tramp-copy-keep-date) + copy-args + (delq + nil + (mapcar + '(lambda (x) + (setq + ;; " " is indication for keep-date argument. + x (delete " " (mapcar '(lambda (y) (format-spec y spec)) x))) + (unless (member "" x) (mapconcat 'identity x " "))) + (tramp-get-method-parameter + method 'tramp-copy-args)))) ;; Check for program. (when (and (fboundp 'executable-find) - (not (executable-find copy-program))) - (error "Cannot find copy program: %s" copy-program)) - - (set-buffer trampbuf) - (setq tramp-current-multi-method multi-method - tramp-current-method method - tramp-current-user user - tramp-current-host host) - (message "Transferring %s to %s..." filename newname) - - ;; Use rcp-like program for file transfer. + (not (let ((default-directory + (tramp-temporary-file-directory))) + (executable-find copy-program)))) + (tramp-error + v 'file-error "Cannot find copy program: %s" copy-program)) + + (tramp-message v 0 "Transferring %s to %s..." filename newname) + (unwind-protect - (let* ((default-directory - (if (and (stringp default-directory) - (file-accessible-directory-p default-directory)) - default-directory - (tramp-temporary-file-directory))) - (p (apply 'start-process (buffer-name trampbuf) trampbuf - copy-program copy-args))) - (tramp-set-process-query-on-exit-flag p nil) - (tramp-process-actions p multi-method method user host - tramp-actions-copy-out-of-band)) - (kill-buffer trampbuf)) - (message "Transferring %s to %s...done" filename newname) + (with-temp-buffer + ;; The default directory must be remote. + (let ((default-directory + (file-name-directory (if t1 filename newname)))) + ;; Set the transfer process properties. + (tramp-set-connection-property + v "process-name" (buffer-name (current-buffer))) + (tramp-set-connection-property + v "process-buffer" (current-buffer)) + + ;; Use an asynchronous process. By this, password can + ;; be handled. The default directory must be local, in + ;; order to apply the correct `copy-program'. We don't + ;; set a timeout, because the copying of large files can + ;; last longer than 60 secs. + (let ((p (let ((default-directory + (tramp-temporary-file-directory))) + (apply 'start-process + (tramp-get-connection-property + v "process-name" nil) + (tramp-get-connection-property + v "process-buffer" nil) + copy-program + (append copy-args (list source target)))))) + (tramp-message + v 6 "%s" (mapconcat 'identity (process-command p) " ")) + (set-process-sentinel p 'tramp-flush-connection-property) + (tramp-set-process-query-on-exit-flag p nil) + (tramp-process-actions p v tramp-actions-copy-out-of-band)))) + + ;; Reset the transfer process properties. + (tramp-set-connection-property v "process-name" nil) + (tramp-set-connection-property v "process-buffer" nil)) + + (tramp-message v 0 "Transferring %s to %s...done" filename newname) + + ;; Handle KEEP-DATE argument. + (when (and keep-date (not copy-keep-date)) + (set-file-times newname (nth 5 (file-attributes filename)))) ;; Set the mode. - (unless keep-date + (unless (and keep-date copy-keep-date) (set-file-modes newname (file-modes filename)))) ;; If the operation was `rename', delete the original file. (unless (eq op 'copy) (delete-file filename)))) -;; mkdir (defun tramp-handle-make-directory (dir &optional parents) - "Like `make-directory' for tramp files." + "Like `make-directory' for Tramp files." (setq dir (expand-file-name dir)) (with-parsed-tramp-file-name dir nil (save-excursion (tramp-barf-unless-okay - multi-method method user host + v (format " %s %s" (if parents "mkdir -p" "mkdir") (tramp-shell-quote-argument localname)) - nil 'file-error "Couldn't make directory %s" dir)))) -;; CCC error checking? (defun tramp-handle-delete-directory (directory) - "Like `delete-directory' for tramp files." + "Like `delete-directory' for Tramp files." (setq directory (expand-file-name directory)) (with-parsed-tramp-file-name directory nil - (save-excursion - (tramp-send-command - multi-method method user host - (format "rmdir %s ; echo ok" - (tramp-shell-quote-argument localname))) - (tramp-wait-for-output)))) + (tramp-flush-directory-property v localname) + (unless (zerop (tramp-send-command-and-check + v + (format "rmdir %s" + (tramp-shell-quote-argument localname)))) + (tramp-error v 'file-error "Couldn't delete %s" directory)))) (defun tramp-handle-delete-file (filename) - "Like `delete-file' for tramp files." + "Like `delete-file' for Tramp files." (setq filename (expand-file-name filename)) (with-parsed-tramp-file-name filename nil - (save-excursion - (unless (zerop (tramp-send-command-and-check - multi-method method user host - (format "rm -f %s" - (tramp-shell-quote-argument localname)))) - (signal 'file-error "Couldn't delete Tramp file"))))) + (tramp-flush-file-property v localname) + (unless (zerop (tramp-send-command-and-check + v + (format "rm -f %s" + (tramp-shell-quote-argument localname)))) + (tramp-error v 'file-error "Couldn't delete %s" filename)))) ;; Dired. @@ -3312,57 +3129,33 @@ ;; we try and delete two directories under TRAMP :/ (defun tramp-handle-dired-recursive-delete-directory (filename) "Recursively delete the directory given. -This is like `dired-recursive-delete-directory' for tramp files." +This is like `dired-recursive-delete-directory' for Tramp files." (with-parsed-tramp-file-name filename nil - ;; run a shell command 'rm -r ' + (tramp-flush-directory-property v filename) + ;; Run a shell command 'rm -r ' ;; Code shamelessly stolen for the dired implementation and, um, hacked :) - (or (file-exists-p filename) - (signal - 'file-error - (list "Removing old file name" "no such directory" filename))) + (unless (file-exists-p filename) + (tramp-error v 'file-error "No such directory: %s" filename)) ;; Which is better, -r or -R? (-r works for me ) - (tramp-send-command multi-method method user host - (format "rm -r %s" (tramp-shell-quote-argument localname))) + (tramp-send-command + v + (format "rm -r %s" (tramp-shell-quote-argument localname)) + ;; Don't read the output, do it explicitely. + nil t) ;; Wait for the remote system to return to us... ;; This might take a while, allow it plenty of time. - (tramp-wait-for-output 120) + (tramp-wait-for-output (tramp-get-connection-process v) 120) ;; Make sure that it worked... (and (file-exists-p filename) - (error "Failed to recursively delete %s" filename)))) - -(defun tramp-handle-dired-call-process (program discard &rest arguments) - "Like `dired-call-process' for tramp files." - (with-parsed-tramp-file-name default-directory nil - (save-excursion - (tramp-barf-unless-okay - multi-method method user host - (format "cd %s" (tramp-shell-quote-argument localname)) - nil 'file-error - "tramp-handle-dired-call-process: Couldn't `cd %s'" - (tramp-shell-quote-argument localname)) - (tramp-send-command - multi-method method user host - (mapconcat #'tramp-shell-quote-argument (cons program arguments) " ")) - (tramp-wait-for-output)) - (unless discard - ;; We cannot use `insert-buffer' because the tramp buffer - ;; changes its contents before insertion due to calling - ;; `expand-file' and alike. - (insert - (with-current-buffer - (tramp-get-buffer multi-method method user host) - (buffer-string)))) - (save-excursion - (prog1 - (tramp-send-command-and-check multi-method method user host nil) - (tramp-send-command multi-method method user host "cd") - (tramp-wait-for-output))))) + (tramp-error + v 'file-error "Failed to recursively delete %s" filename)))) (defun tramp-handle-dired-compress-file (file &rest ok-flag) - "Like `dired-compress-file' for tramp files." + "Like `dired-compress-file' for Tramp files." ;; OK-FLAG is valid for XEmacs only, but not implemented. ;; Code stolen mainly from dired-aux.el. (with-parsed-tramp-file-name file nil + (tramp-flush-file-property v localname) (save-excursion (let ((suffixes (if (not (featurep 'xemacs)) @@ -3388,11 +3181,10 @@ nil) ((and suffix (nth 2 suffix)) ;; We found an uncompression rule. - (message "Uncompressing %s..." file) + (tramp-message v 0 "Uncompressing %s..." file) (when (zerop (tramp-send-command-and-check - multi-method method user host - (concat (nth 2 suffix) " " localname))) - (message "Uncompressing %s...done" file) + v (concat (nth 2 suffix) " " localname))) + (tramp-message v 0 "Uncompressing %s...done" file) ;; `dired-remove-file' is not defined in XEmacs (funcall (symbol-function 'dired-remove-file) file) (string-match (car suffix) file) @@ -3400,11 +3192,10 @@ (t ;; We don't recognize the file as compressed, so compress it. ;; Try gzip. - (message "Compressing %s..." file) + (tramp-message v 0 "Compressing %s..." file) (when (zerop (tramp-send-command-and-check - multi-method method user host - (concat "gzip -f " localname))) - (message "Compressing %s...done" file) + v (concat "gzip -f " localname))) + (tramp-message v 0 "Compressing %s...done" file) ;; `dired-remove-file' is not defined in XEmacs (funcall (symbol-function 'dired-remove-file) file) (cond ((file-exists-p (concat file ".gz")) @@ -3428,21 +3219,21 @@ (defun tramp-handle-insert-directory (filename switches &optional wildcard full-directory-p) - "Like `insert-directory' for tramp files." - (if (and (featurep 'ls-lisp) - (not (symbol-value 'ls-lisp-use-insert-directory-program))) - (tramp-run-real-handler - 'insert-directory (list filename switches wildcard full-directory-p)) - ;; For the moment, we assume that the remote "ls" program does not - ;; grok "--dired". In the future, we should detect this on - ;; connection setup. - (when (string-match "^--dired\\s-+" switches) - (setq switches (replace-match "" nil t switches))) - (setq filename (expand-file-name filename)) - (with-parsed-tramp-file-name filename nil - (tramp-message-for-buffer - multi-method method user host 10 - "Inserting directory `ls %s %s', wildcard %s, fulldir %s" + "Like `insert-directory' for Tramp files." + (setq filename (expand-file-name filename)) + (with-parsed-tramp-file-name filename nil + (tramp-flush-file-property v localname) + (if (and (featurep 'ls-lisp) + (not (symbol-value 'ls-lisp-use-insert-directory-program))) + (tramp-run-real-handler + 'insert-directory (list filename switches wildcard full-directory-p)) + ;; For the moment, we assume that the remote "ls" program does not + ;; grok "--dired". In the future, we should detect this on + ;; connection setup. + (when (string-match "^--dired\\s-+" switches) + (setq switches (replace-match "" nil t switches))) + (tramp-message + v 4 "Inserting directory `ls %s %s', wildcard %s, fulldir %s" switches filename (if wildcard "yes" "no") (if full-directory-p "yes" "no")) (when wildcard @@ -3454,80 +3245,45 @@ (setq switches (concat "-d " switches))) (when wildcard (setq switches (concat switches " " wildcard))) - (save-excursion - ;; If `full-directory-p', we just say `ls -l FILENAME'. - ;; Else we chdir to the parent directory, then say `ls -ld BASENAME'. - (if full-directory-p - (tramp-send-command - multi-method method user host - (format "%s %s %s" - (tramp-get-ls-command multi-method method user host) - switches - (if wildcard - localname - (tramp-shell-quote-argument (concat localname "."))))) - (tramp-barf-unless-okay - multi-method method user host - (format "cd %s" (tramp-shell-quote-argument - (file-name-directory localname))) - nil 'file-error - "Couldn't `cd %s'" - (tramp-shell-quote-argument (file-name-directory localname))) - (tramp-send-command - multi-method method user host - (format "%s %s %s" - (tramp-get-ls-command multi-method method user host) - switches - (if wildcard - localname - (if (zerop (length (file-name-nondirectory localname))) - "" - (tramp-shell-quote-argument - (file-name-nondirectory localname))))))) - (sit-for 1) ;needed for rsh but not ssh? - (tramp-wait-for-output)) - ;; The following let-binding is used by code that's commented - ;; out. Let's leave the let-binding in for a while to see - ;; that the commented-out code is really not needed. Commenting-out - ;; happened on 2003-03-13. - (let ((old-pos (point))) - ;; We cannot use `insert-buffer' because the tramp buffer - ;; changes its contents before insertion due to calling - ;; `expand-file' and alike. - (insert - (with-current-buffer - (tramp-get-buffer multi-method method user host) - (buffer-string))) - ;; On XEmacs, we want to call (exchange-point-and-mark t), but - ;; that doesn't exist on Emacs, so we use this workaround instead. - ;; Since zmacs-region-stays doesn't exist in Emacs, this ought to - ;; be safe. Thanks to Daniel Pittman . - ;; (let ((zmacs-region-stays t)) - ;; (exchange-point-and-mark)) - (save-excursion - (tramp-send-command multi-method method user host "cd") - (tramp-wait-for-output)) - ;; For the time being, the XEmacs kludge is commented out. - ;; Please test it on various XEmacs versions to see if it works. - ;; ;; Another XEmacs specialty follows. What's the right way to do - ;; ;; it? - ;; (when (and (featurep 'xemacs) - ;; (eq major-mode 'dired-mode)) - ;; (save-excursion - ;; (require 'dired) - ;; (dired-insert-set-properties old-pos (point)))) - )))) - -;; Continuation of kluge to pacify byte-compiler. -;;(eval-when-compile -;; (when (eq (symbol-function 'dired-insert-set-properties) 'ignore) -;; (fmakunbound 'dired-insert-set-properties))) + ;; If `full-directory-p', we just say `ls -l FILENAME'. + ;; Else we chdir to the parent directory, then say `ls -ld BASENAME'. + (if full-directory-p + (tramp-send-command + v + (format "%s %s %s" + (tramp-get-ls-command v) + switches + (if wildcard + localname + (tramp-shell-quote-argument (concat localname "."))))) + (tramp-barf-unless-okay + v + (format "cd %s" (tramp-shell-quote-argument + (file-name-directory localname))) + "Couldn't `cd %s'" + (tramp-shell-quote-argument (file-name-directory localname))) + (tramp-send-command + v + (format "%s %s %s" + (tramp-get-ls-command v) + switches + (if (or wildcard + (zerop (length (file-name-nondirectory localname)))) + "" + (tramp-shell-quote-argument + (file-name-nondirectory localname)))))) + ;; We cannot use `insert-buffer-substring' because the tramp buffer + ;; changes its contents before insertion due to calling + ;; `expand-file' and alike. + (insert + (with-current-buffer (tramp-get-buffer v) + (buffer-string)))))) ;; CCC is this the right thing to do? (defun tramp-handle-unhandled-file-name-directory (filename) - "Like `unhandled-file-name-directory' for tramp files." + "Like `unhandled-file-name-directory' for Tramp files." (with-parsed-tramp-file-name filename nil - (expand-file-name "~/"))) + (expand-file-name (tramp-make-tramp-file-name method user host "~/")))) ;; Canonicalization of file names. @@ -3548,7 +3304,7 @@ name)) (defun tramp-handle-expand-file-name (name &optional dir) - "Like `expand-file-name' for tramp files. + "Like `expand-file-name' for Tramp files. If the localname part of the given filename starts with \"/../\" then the result will be a local, non-Tramp, filename." ;; If DIR is not given, use DEFAULT-DIRECTORY or "/". @@ -3556,223 +3312,107 @@ ;; Unless NAME is absolute, concat DIR and NAME. (unless (file-name-absolute-p name) (setq name (concat (file-name-as-directory dir) name))) - ;; If NAME is not a tramp file, run the real handler + ;; If NAME is not a Tramp file, run the real handler. (if (not (tramp-tramp-file-p name)) - (tramp-run-real-handler 'expand-file-name - (list name nil)) + (tramp-run-real-handler 'expand-file-name (list name nil)) ;; Dissect NAME. (with-parsed-tramp-file-name name nil (unless (file-name-absolute-p localname) (setq localname (concat "~/" localname))) - (save-excursion - ;; Tilde expansion if necessary. This needs a shell which - ;; groks tilde expansion! The function `tramp-find-shell' is - ;; supposed to find such a shell on the remote host. Please - ;; tell me about it when this doesn't work on your system. - (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname) - (let ((uname (match-string 1 localname)) - (fname (match-string 2 localname))) - ;; We cannot simply apply "~/", because under sudo "~/" is - ;; expanded to the local user home directory but to the - ;; root home directory. On the other hand, using always - ;; the default user name for tilde expansion is not - ;; appropriate either, because ssh and companions might - ;; use a user name from the config file. - (when (and (string-equal uname "~") - (string-match - "\\`su\\(do\\)?\\'" - (tramp-find-method multi-method method user host))) - (setq uname (concat uname (or user "root")))) - ;; CCC fanatic error checking? - (set-buffer (tramp-get-buffer multi-method method user host)) - (erase-buffer) - (tramp-send-command - multi-method method user host - (format "cd %s; pwd" uname) - t) - (tramp-wait-for-output) - (goto-char (point-min)) - (setq uname (buffer-substring (point) (tramp-line-end-position))) - (setq localname (concat uname fname)) - (erase-buffer))) - ;; There might be a double slash, for example when "~/" - ;; expands to "/". Remove this. - (while (string-match "//" localname) - (setq localname (replace-match "/" t t localname))) - ;; No tilde characters in file name, do normal - ;; expand-file-name (this does "/./" and "/../"). We bind - ;; directory-sep-char here for XEmacs on Windows, which would - ;; otherwise use backslash. `default-directory' is bound to - ;; "/", because on Windows there would be problems with UNC - ;; shares or Cygwin mounts. - (tramp-let-maybe directory-sep-char ?/ - (let ((default-directory "/")) - (tramp-make-tramp-file-name - multi-method (or method (tramp-find-default-method user host)) - user host - (tramp-drop-volume-letter - (tramp-run-real-handler 'expand-file-name - (list localname)))))))))) - -;; old version follows. it uses ".." to cross file handler -;; boundaries. -;; ;; Look if localname starts with "/../" construct. If this is -;; ;; the case, then we return a local name instead of a remote name. -;; (if (string-match "^/\\.\\./" localname) -;; (expand-file-name (substring localname 3)) -;; ;; No tilde characters in file name, do normal -;; ;; expand-file-name (this does "/./" and "/../"). We bind -;; ;; directory-sep-char here for XEmacs on Windows, which -;; ;; would otherwise use backslash. -;; (let ((directory-sep-char ?/)) -;; (tramp-make-tramp-file-name -;; multi-method method user host -;; (tramp-drop-volume-letter -;; (tramp-run-real-handler 'expand-file-name -;; (list localname)))))))))) - -;; Remote commands. - -(defvar tramp-async-proc nil - "Global variable keeping asynchronous process object. -Used in `tramp-handle-shell-command'") - -(defvar tramp-display-shell-command-buffer t - "Whether to display output buffer of `shell-command'. -This is necessary for handling DISPLAY of `process-file'.") - -(defun tramp-handle-shell-command (command &optional output-buffer error-buffer) - "Like `shell-command' for tramp files. -This will break if COMMAND prints a newline, followed by the value of -`tramp-end-of-output', followed by another newline." - ;; Asynchronous processes are far from being perfect. But it works at least - ;; for `find-grep-dired' and `find-name-dired' in Emacs 22. - (if (tramp-tramp-file-p default-directory) - (with-parsed-tramp-file-name default-directory nil - (let ((curbuf (current-buffer)) - (asynchronous (string-match "[ \t]*&[ \t]*\\'" command)) - status) - (unless output-buffer - (setq output-buffer - (get-buffer-create - (if asynchronous - "*Async Shell Command*" - "*Shell Command Output*"))) - (set-buffer output-buffer) - (erase-buffer)) - (unless (bufferp output-buffer) - (setq output-buffer (current-buffer))) - (set-buffer output-buffer) - ;; Tramp doesn't handle the asynchronous case by an asynchronous - ;; process. Instead of, another asynchronous process is opened - ;; which gets the output of the (synchronous) Tramp process - ;; via process-filter. ERROR-BUFFER is disabled. - (when asynchronous - (setq command (substring command 0 (match-beginning 0)) - error-buffer nil - tramp-async-proc (start-process (buffer-name output-buffer) - output-buffer "cat"))) - (save-excursion - (tramp-barf-unless-okay - multi-method method user host - (format "cd %s" (tramp-shell-quote-argument localname)) - nil 'file-error - "tramp-handle-shell-command: Couldn't `cd %s'" - (tramp-shell-quote-argument localname)) - ;; Define the process filter - (when asynchronous - (set-process-filter - (get-buffer-process - (tramp-get-buffer multi-method method user host)) - '(lambda (process string) - ;; Write the output into the Tramp Process - (save-current-buffer - (set-buffer (process-buffer process)) - (goto-char (point-max)) - (insert string)) - ;; Hand-over output to asynchronous process. - (let ((end - (string-match - (regexp-quote tramp-end-of-output) string))) - (when end - (setq string - (substring string 0 (1- (match-beginning 0))))) - (process-send-string tramp-async-proc string) - (when end - (set-process-filter process nil) - (process-send-eof tramp-async-proc)))))) - ;; Send the command - (tramp-send-command - multi-method method user host - (if error-buffer - (format "( %s ) 2>/tmp/tramp.$$.err; tramp_old_status=$?" - command) - (format "%s; tramp_old_status=$?" command))) - (unless asynchronous - (tramp-wait-for-output))) - (unless asynchronous - ;; We cannot use `insert-buffer' because the tramp buffer - ;; changes its contents before insertion due to calling - ;; `expand-file' and alike. - (insert - (with-current-buffer - (tramp-get-buffer multi-method method user host) - (buffer-string)))) - (when error-buffer - (save-excursion - (unless (bufferp error-buffer) - (setq error-buffer (get-buffer-create error-buffer))) - (tramp-send-command - multi-method method user host - "cat /tmp/tramp.$$.err") - (tramp-wait-for-output) - (set-buffer error-buffer) - ;; Same comment as above - (insert - (with-current-buffer - (tramp-get-buffer multi-method method user host) - (buffer-string))) - (tramp-send-command-and-check - multi-method method user host "rm -f /tmp/tramp.$$.err"))) - (save-excursion - (tramp-send-command multi-method method user host "cd") - (unless asynchronous - (tramp-wait-for-output)) - (tramp-send-command - multi-method method user host - (concat "tramp_set_exit_status $tramp_old_status;" - " echo tramp_exit_status $?")) - (unless asynchronous - (tramp-wait-for-output) - (goto-char (point-max)) - (unless (search-backward "tramp_exit_status " nil t) - (error "Couldn't find exit status of `%s'" command)) - (skip-chars-forward "^ ") - (setq status (read (current-buffer))))) - (unless (zerop (buffer-size)) - (when tramp-display-shell-command-buffer - (display-buffer output-buffer))) - (set-buffer curbuf) - status)) - ;; The following is only executed if something strange was - ;; happening. Emit a helpful message and do it anyway. - (message "tramp-handle-shell-command called with non-tramp directory: `%s'" - default-directory) - (tramp-run-real-handler 'shell-command - (list command output-buffer error-buffer)))) - -(defun tramp-handle-process-file (program &optional infile buffer display &rest args) - "Like `process-file' for Tramp files." - (when infile (error "Implementation does not handle input from file")) - (when (and (numberp buffer) (zerop buffer)) - (error "Implementation does not handle immediate return")) - (when (consp buffer) (error "Implementation does not handle error files")) - (let ((tramp-display-shell-command-buffer display)) - (shell-command - (mapconcat 'tramp-shell-quote-argument (cons program args) " ") - buffer))) - -;; File Editing. + ;; Tilde expansion if necessary. This needs a shell which + ;; groks tilde expansion! The function `tramp-find-shell' is + ;; supposed to find such a shell on the remote host. Please + ;; tell me about it when this doesn't work on your system. + (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname) + (let ((uname (match-string 1 localname)) + (fname (match-string 2 localname))) + ;; We cannot simply apply "~/", because under sudo "~/" is + ;; expanded to the local user home directory but to the + ;; root home directory. On the other hand, using always + ;; the default user name for tilde expansion is not + ;; appropriate either, because ssh and companions might + ;; use a user name from the config file. + (when (and (string-equal uname "~") + (string-match "\\`su\\(do\\)?\\'" method)) + (setq uname (concat uname user))) + (setq uname + (with-connection-property v uname + (tramp-send-command v (format "cd %s; pwd" uname)) + (with-current-buffer (tramp-get-buffer v) + (goto-char (point-min)) + (buffer-substring (point) (tramp-line-end-position))))) + (setq localname (concat uname fname)))) + ;; There might be a double slash, for example when "~/" + ;; expands to "/". Remove this. + (while (string-match "//" localname) + (setq localname (replace-match "/" t t localname))) + ;; No tilde characters in file name, do normal + ;; expand-file-name (this does "/./" and "/../"). We bind + ;; `directory-sep-char' here for XEmacs on Windows, which + ;; would otherwise use backslash. `default-directory' is + ;; bound, because on Windows there would be problems with UNC + ;; shares or Cygwin mounts. + (tramp-let-maybe directory-sep-char ?/ + (let ((default-directory (tramp-temporary-file-directory))) + (tramp-make-tramp-file-name + method user host + (tramp-drop-volume-letter + (tramp-run-real-handler 'expand-file-name + (list localname))))))))) + +(defun tramp-handle-substitute-in-file-name (filename) + "Like `substitute-in-file-name' for Tramp files. +\"//\" and \"/~\" substitute only in the local filename part. +If the URL Tramp syntax is chosen, \"//\" as method delimeter and \"/~\" at +beginning of local filename are not substituted." + (with-parsed-tramp-file-name filename nil + (if (equal tramp-syntax 'url) + ;; We need to check localname only. The other parts cannot contain + ;; "//" or "/~". + (if (and (> (length localname) 1) + (or (string-match "//" localname) + (string-match "/~" localname 1))) + (tramp-run-real-handler 'substitute-in-file-name (list filename)) + (tramp-make-tramp-file-name + (when method (substitute-in-file-name method)) + (when user (substitute-in-file-name user)) + (when host (substitute-in-file-name host)) + (when localname (substitute-in-file-name localname)))) + ;; Ignore in LOCALNAME everything before "//" or "/~". + (when (and (stringp localname) (string-match ".+?/\\(/\\|~\\)" localname)) + (setq filename + (tramp-make-tramp-file-name + method user host (replace-match "\\1" nil nil localname))) + ;; "/m:h:~" does not work for completion. We use "/m:h:~/". + (when (string-match "~$" filename) + (setq filename (concat filename "/")))) + (tramp-run-real-handler 'substitute-in-file-name (list filename))))) + +;; In XEmacs, electricity is implemented via a key map for ?/ and ?~, +;; which calls corresponding functions (see minibuf.el). +(when (fboundp 'minibuffer-electric-separator) + (mapcar + '(lambda (x) + (eval + `(defadvice ,x + (around ,(intern (format "tramp-advice-%s" x)) activate) + "Invoke `substitute-in-file-name' for Tramp files." + (if (and (symbol-value 'minibuffer-electric-file-name-behavior) + (tramp-tramp-file-p (buffer-substring))) + ;; We don't need to handle `last-input-event', because + ;; due to the key map we know it must be ?/ or ?~. + (let ((s (concat (buffer-substring (point-min) (point)) + (string last-command-char)))) + (delete-region (point-min) (point)) + (insert (substitute-in-file-name s)) + (setq ad-return-value last-command-char)) + ad-do-it)))) + + '(minibuffer-electric-separator + minibuffer-electric-tilde))) + + +;;; Remote commands. (defsubst tramp-make-temp-file (filename) (concat @@ -3781,102 +3421,254 @@ (tramp-temporary-file-directory))) (file-name-extension filename t))) +(defsubst tramp-make-tramp-temp-file (vec) + (format + "/tmp/%s%s" + tramp-temp-name-prefix + (if (get-buffer-process (tramp-get-connection-buffer vec)) + (process-id (get-buffer-process (tramp-get-connection-buffer vec))) + (emacs-pid)))) + +(defun tramp-handle-executable-find (command) + "Like `executable-find' for Tramp files." + (with-parsed-tramp-file-name default-directory nil + (tramp-find-executable v command tramp-remote-path t))) + +;; We use BUFFER also as connection buffer during setup. Because of +;; this, its original contents must be saved, and restored once +;; connection has been setup. +(defun tramp-handle-start-file-process (name buffer program &rest args) + "Like `start-file-process' for Tramp files." + (with-parsed-tramp-file-name default-directory nil + (unwind-protect + (progn + ;; Set the new process properties. + (tramp-set-connection-property v "process-name" name) + (tramp-set-connection-property + v "process-buffer" + (get-buffer-create + ;; BUFFER can be nil. + (or buffer (generate-new-buffer-name (tramp-buffer-name v))))) + ;; Activate narrowing in order to save BUFFER contents. + (with-current-buffer (tramp-get-connection-buffer v) + (narrow-to-region (point-max) (point-max))) + ;; Goto working directory. `tramp-send-command' opens a new + ;; connection. + (tramp-send-command + v (format "cd %s" (tramp-shell-quote-argument localname))) + ;; Send the command. + (tramp-send-command + v + (format "%s; exit" + (mapconcat 'tramp-shell-quote-argument + (cons program args) " ")) + nil t) ; nooutput + ;; Return process. + (tramp-get-connection-process v)) + ;; Save exit. + (with-current-buffer (tramp-get-connection-buffer v) (widen)) + (tramp-set-connection-property v "process-name" nil) + (tramp-set-connection-property v "process-buffer" nil)))) + +(defun tramp-handle-process-file + (program &optional infile destination display &rest args) + "Like `process-file' for Tramp files." + ;; The implementation is not complete yet. + (when (and (numberp destination) (zerop destination)) + (error "Implementation does not handle immediate return")) + + (with-parsed-tramp-file-name default-directory nil + (let ((temp-name-prefix (tramp-make-tramp-temp-file v)) + command input stderr outbuf ret) + ;; Compute command. + (setq command (mapconcat 'tramp-shell-quote-argument + (cons program args) " ")) + ;; Determine input. + (if (null infile) + (setq input "/dev/null") + (setq infile (expand-file-name infile)) + (if (tramp-equal-remote default-directory infile) + ;; INFILE is on the same remote host. + (setq input (with-parsed-tramp-file-name infile nil localname)) + ;; INFILE must be copied to remote host. + (setq input (concat temp-name-prefix ".in")) + (copy-file + infile + (tramp-make-tramp-file-name method user host input) + t))) + (when input (setq command (format "%s <%s" command input))) + + ;; Determine output. + (cond + ;; Just a buffer + ((bufferp destination) + (setq outbuf destination)) + ;; A buffer name + ((stringp destination) + (setq outbuf (get-buffer-create destination))) + ;; (REAL-DESTINATION ERROR-DESTINATION) + ((consp destination) + ;; output + (cond + ((bufferp (car destination)) + (setq outbuf (car destination))) + ((stringp (car destination)) + (setq outbuf (get-buffer-create (car destination))))) + ;; stderr + (cond + ((stringp (cadr destination)) + (setcar (cdr destination) (expand-file-name (cadr destination))) + (if (tramp-equal-remote default-directory (cadr destination)) + ;; stderr is on the same remote host. + (setq stderr (with-parsed-tramp-file-name + (cadr destination) nil localname)) + ;; stderr must be copied to remote host. The temporary + ;; file must be deleted after execution. + (setq stderr (concat temp-name-prefix ".err")))) + ;; stderr to be discarded + ((null (cadr destination)) + (setq stderr "/dev/null")))) + ;; 't + (destination + (setq outbuf (current-buffer)))) + (when stderr (setq command (format "%s 2>%s" command stderr))) + + ;; If we have a temporary file, it must be removed after operation. + (when (and input (string-match temp-name-prefix input)) + (setq command (format "%s; rm %s" command input))) + ;; Goto working directory. + (tramp-send-command + v (format "cd %s" (tramp-shell-quote-argument localname))) + ;; Send the command. It might not return in time, so we protect it. + (condition-case nil + (unwind-protect + (tramp-send-command v command) + ;; We should show the output anyway. + (when outbuf + (with-current-buffer outbuf + (insert-buffer-substring (tramp-get-connection-buffer v))) + (when display (display-buffer outbuf)))) + ;; When the user did interrupt, we should do it also. + (error + (kill-buffer (tramp-get-connection-buffer v)) + (setq ret 1))) + (unless ret + ;; Check return code. + (setq ret (tramp-send-command-and-check v nil)) + ;; Provide error file. + (when (and stderr (string-match temp-name-prefix stderr)) + (rename-file (tramp-make-tramp-file-name method user host stderr) + (cadr destination) t))) + ;; Return exit status. + ret))) + +(defun tramp-handle-call-process-region + (start end program &optional delete buffer display &rest args) + "Like `call-process-region' for Tramp files." + (let ((tmpfile (tramp-make-temp-file ""))) + (write-region start end tmpfile) + (when delete (delete-region start end)) + (unwind-protect + (apply 'call-process program tmpfile buffer display args) + (delete-file tmpfile)))) + +(defun tramp-handle-shell-command + (command &optional output-buffer error-buffer) + "Like `shell-command' for Tramp files." + (with-parsed-tramp-file-name default-directory nil + (let ((shell-file-name + (tramp-get-connection-property v "remote-shell" "/bin/sh")) + (shell-command-switch "-c")) + (tramp-run-real-handler + 'shell-command (list command output-buffer error-buffer))))) + +;; File Editing. + +(defvar tramp-handle-file-local-copy-hook nil + "Normal hook to be run at the end of `tramp-handle-file-local-copy'.") + (defun tramp-handle-file-local-copy (filename) - "Like `file-local-copy' for tramp files." + "Like `file-local-copy' for Tramp files." (with-parsed-tramp-file-name filename nil - (let ((tramp-buf (tramp-get-buffer multi-method method user host)) - ;; We used to bind the following as late as possible. - ;; loc-enc and loc-dec were bound directly before the if - ;; statement that checks them. But the functions - ;; tramp-get-* might invoke the "are you awake" check in - ;; tramp-maybe-open-connection, which is an unfortunate time - ;; since we rely on the buffer contents at that spot. - (rem-enc (tramp-get-remote-encoding multi-method method user host)) - (rem-dec (tramp-get-remote-decoding multi-method method user host)) - (loc-enc (tramp-get-local-encoding multi-method method user host)) - (loc-dec (tramp-get-local-decoding multi-method method user host)) + (let (;; We used to bind the following as late as possible. + ;; loc-dec was bound directly before the if statement that + ;; checks them. But the functions tramp-get-* might invoke + ;; the "are you awake" check in `tramp-maybe-open-connection', + ;; which is an unfortunate time since we rely on the buffer + ;; contents at that spot. + (rem-enc (tramp-get-remote-coding v "remote-encoding")) + (loc-dec (tramp-get-local-coding v "local-decoding")) tmpfil) (unless (file-exists-p filename) - (error "Cannot make local copy of non-existing file `%s'" - filename)) + (tramp-error + v 'file-error + "Cannot make local copy of non-existing file `%s'" filename)) (setq tmpfil (tramp-make-temp-file filename)) - (cond ((tramp-method-out-of-band-p multi-method method user host) + (cond ((and (tramp-method-out-of-band-p v) + (> (nth 7 (file-attributes filename)) + tramp-copy-size-limit)) ;; `copy-file' handles out-of-band methods (copy-file filename tmpfil t t)) - ((and rem-enc rem-dec) + (rem-enc ;; Use inline encoding for file transfer. (save-excursion - ;; Following line for setting tramp-current-method, - ;; tramp-current-user, tramp-current-host. - (set-buffer tramp-buf) - (tramp-message 5 "Encoding remote file %s..." filename) + (tramp-message v 5 "Encoding remote file %s..." filename) (tramp-barf-unless-okay - multi-method method user host + v (concat rem-enc " < " (tramp-shell-quote-argument localname)) - nil 'file-error - "Encoding remote file failed, see buffer `%s' for details" - tramp-buf) - ;; Remove trailing status code - (goto-char (point-max)) - (delete-region (point) (progn (forward-line -1) (point))) - - (tramp-message 5 "Decoding remote file %s..." filename) - - ;; Here is where loc-enc and loc-dec used to be let-bound. + "Encoding remote file failed") + + (tramp-message v 5 "Decoding remote file %s..." filename) + ;; Here is where loc-dec used to be let-bound. (if (and (symbolp loc-dec) (fboundp loc-dec)) ;; If local decoding is a function, we call it. We ;; must disable multibyte, because ;; `uudecode-decode-region' doesn't handle it ;; correctly. - (let ((tmpbuf (get-buffer-create " *tramp tmp*"))) - (set-buffer tmpbuf) - (erase-buffer) - (set-buffer-multibyte nil) - (insert-buffer-substring tramp-buf) - (tramp-message-for-buffer - multi-method method user host - 6 "Decoding remote file %s with function %s..." - filename loc-dec) - (set-buffer tmpbuf) - ;; Douglas Gray Stephens - ;; says that we need to strip tramp_exit_status - ;; line from the output here. Go to point-max, - ;; search backward for tramp_exit_status, delete - ;; between point and point-max if found. - (let ((coding-system-for-write 'binary)) - (funcall loc-dec (point-min) (point-max)) - (write-region (point-min) (point-max) tmpfil)) - (kill-buffer tmpbuf)) + (unwind-protect + (with-temp-buffer + (set-buffer-multibyte nil) + (insert-buffer-substring (tramp-get-buffer v)) + (tramp-message + v 5 "Decoding remote file %s with function %s..." + filename loc-dec) + (funcall loc-dec (point-min) (point-max)) + (let ((coding-system-for-write 'binary)) + (write-region (point-min) (point-max) tmpfil)))) ;; If tramp-decoding-function is not defined for this ;; method, we invoke tramp-decoding-command instead. (let ((tmpfil2 (tramp-make-temp-file filename))) - (write-region (point-min) (point-max) tmpfil2) + (let ((coding-system-for-write 'binary)) + (write-region (point-min) (point-max) tmpfil2)) (tramp-message - 6 "Decoding remote file %s with command %s..." + v 5 "Decoding remote file %s with command %s..." filename loc-dec) (tramp-call-local-coding-command loc-dec tmpfil2 tmpfil) (delete-file tmpfil2))) - (tramp-message-for-buffer - multi-method method user host - 5 "Decoding remote file %s...done" filename) + (tramp-message v 5 "Decoding remote file %s...done" filename) ;; Set proper permissions. (set-file-modes tmpfil (file-modes filename)))) - (t (error "Wrong method specification for `%s'" method))) + (t (tramp-error + v 'file-error "Wrong method specification for `%s'" method))) + (run-hooks 'tramp-handle-file-local-copy-hook) tmpfil))) -(defun tramp-handle-file-remote-p (filename) - "Like `file-remote-p' for tramp files." +(defun tramp-handle-file-remote-p (filename &optional connected) + "Like `file-remote-p' for Tramp files." (when (tramp-tramp-file-p filename) (with-parsed-tramp-file-name filename nil - (vector multi-method method user host "")))) + (and (or (not connected) + (let ((p (tramp-get-connection-process v))) + (and p (processp p) (memq (process-status p) '(run open))))) + (tramp-make-tramp-file-name method user host ""))))) (defun tramp-handle-insert-file-contents (filename &optional visit beg end replace) - "Like `insert-file-contents' for tramp files." + "Like `insert-file-contents' for Tramp files." (barf-if-buffer-read-only) (setq filename (expand-file-name filename)) (with-parsed-tramp-file-name filename nil @@ -3886,8 +3678,8 @@ (setq buffer-file-name filename) (set-visited-file-modtime) (set-buffer-modified-p nil)) - (signal 'file-error - (format "File `%s' not found on remote host" filename)) + (tramp-error + v 'file-error "File %s not found on remote host" filename) (list (expand-file-name filename) 0)) ;; `insert-file-contents-literally' takes care to avoid calling ;; jka-compr. By let-binding inhibit-file-name-operation, we @@ -3899,20 +3691,16 @@ 'file-local-copy))) (file-local-copy filename))) coding-system-used result) + (tramp-message v 4 "Inserting local temp file `%s'..." local-copy) + (setq result (insert-file-contents local-copy nil beg end replace)) (when visit (setq buffer-file-name filename) (set-visited-file-modtime) (set-buffer-modified-p nil)) - (tramp-message-for-buffer - multi-method method user host - 9 "Inserting local temp file `%s'..." local-copy) - (setq result (insert-file-contents local-copy nil beg end replace)) ;; Now `last-coding-system-used' has right value. Remember it. (when (boundp 'last-coding-system-used) (setq coding-system-used (symbol-value 'last-coding-system-used))) - (tramp-message-for-buffer - multi-method method user host - 9 "Inserting local temp file `%s'...done" local-copy) + (tramp-message v 4 "Inserting local temp file `%s'...done" local-copy) (delete-file local-copy) (when (boundp 'last-coding-system-used) (set 'last-coding-system-used coding-system-used)) @@ -3921,7 +3709,7 @@ (defun tramp-handle-find-backup-file-name (filename) - "Like `find-backup-file-name' for tramp files." + "Like `find-backup-file-name' for Tramp files." (with-parsed-tramp-file-name filename nil ;; We set both variables. It doesn't matter whether it is ;; Emacs or XEmacs @@ -3936,8 +3724,7 @@ (if (and (stringp (cdr x)) (file-name-absolute-p (cdr x)) (not (tramp-file-name-p (cdr x)))) - (tramp-make-tramp-file-name - multi-method method user host (cdr x)) + (tramp-make-tramp-file-name method user host (cdr x)) (cdr x)))) (symbol-value 'tramp-backup-directory-alist)) (symbol-value 'backup-directory-alist)))) @@ -3955,7 +3742,7 @@ (file-name-absolute-p (car (cdr x))) (not (tramp-file-name-p (car (cdr x))))) (tramp-make-tramp-file-name - multi-method method user host (car (cdr x))) + method user host (car (cdr x))) (car (cdr x)))) (cdr (cdr x)))) (symbol-value 'tramp-bkup-backup-directory-info)) @@ -3964,9 +3751,18 @@ (tramp-run-real-handler 'find-backup-file-name (list filename))))) (defun tramp-handle-make-auto-save-file-name () - "Like `make-auto-save-file-name' for tramp files. + "Like `make-auto-save-file-name' for Tramp files. Returns a file name in `tramp-auto-save-directory' for autosaving this file." - (let ((tramp-auto-save-directory tramp-auto-save-directory)) + (let ((tramp-auto-save-directory tramp-auto-save-directory) + (buffer-file-name + (tramp-subst-strs-in-string + '(("_" . "|") + ("/" . "_a") + (":" . "_b") + ("|" . "__") + ("[" . "_l") + ("]" . "_r")) + (buffer-file-name)))) ;; File name must be unique. This is ensured with Emacs 22 (see ;; UNIQUIFY element of `auto-save-file-name-transforms'); but for ;; all other cases we must do it ourselves. @@ -3981,68 +3777,49 @@ (symbol-value 'auto-save-file-name-transforms))) ;; Create directory. (when tramp-auto-save-directory + (setq buffer-file-name + (expand-file-name buffer-file-name tramp-auto-save-directory)) (unless (file-exists-p tramp-auto-save-directory) (make-directory tramp-auto-save-directory t))) - ;; jka-compr doesn't like auto-saving, so by appending "~" to the - ;; file name we make sure that jka-compr isn't used for the - ;; auto-save file. - (let ((buffer-file-name - (if tramp-auto-save-directory - (expand-file-name - (tramp-subst-strs-in-string - '(("_" . "|") - ("/" . "_a") - (":" . "_b") - ("|" . "__") - ("[" . "_l") - ("]" . "_r")) - (buffer-file-name)) - tramp-auto-save-directory) - (buffer-file-name)))) - ;; Run plain `make-auto-save-file-name'. There might be an advice when - ;; it is not a magic file name operation (since Emacs 22). - ;; We must deactivate it temporarily. - (if (not (ad-is-active 'make-auto-save-file-name)) - (tramp-run-real-handler - 'make-auto-save-file-name nil) - ;; else - (ad-deactivate 'make-auto-save-file-name) - (prog1 - (tramp-run-real-handler - 'make-auto-save-file-name nil) - (ad-activate 'make-auto-save-file-name)))))) - - -;; CCC grok APPEND, LOCKNAME, CONFIRM + ;; Run plain `make-auto-save-file-name'. There might be an advice when + ;; it is not a magic file name operation (since Emacs 22). + ;; We must deactivate it temporarily. + (if (not (ad-is-active 'make-auto-save-file-name)) + (tramp-run-real-handler 'make-auto-save-file-name nil) + ;; else + (ad-deactivate 'make-auto-save-file-name) + (prog1 + (tramp-run-real-handler 'make-auto-save-file-name nil) + (ad-activate 'make-auto-save-file-name))))) + +(defvar tramp-handle-write-region-hook nil + "Normal hook to be run at the end of `tramp-handle-write-region'.") + +;; CCC grok APPEND, LOCKNAME (defun tramp-handle-write-region (start end filename &optional append visit lockname confirm) - "Like `write-region' for tramp files." - (unless (eq append nil) - (error "Cannot append to file using tramp (`%s')" filename)) + "Like `write-region' for Tramp files." (setq filename (expand-file-name filename)) - ;; Following part commented out because we don't know what to do about - ;; file locking, and it does not appear to be a problem to ignore it. - ;; Ange-ftp ignores it, too. - ;; (when (and lockname (stringp lockname)) - ;; (setq lockname (expand-file-name lockname))) - ;; (unless (or (eq lockname nil) - ;; (string= lockname filename)) - ;; (error - ;; "tramp-handle-write-region: LOCKNAME must be nil or equal FILENAME")) - ;; XEmacs takes a coding system as the seventh argument, not `confirm' - (when (and (not (featurep 'xemacs)) - confirm (file-exists-p filename)) - (unless (y-or-n-p (format "File %s exists; overwrite anyway? " - filename)) - (error "File not overwritten"))) (with-parsed-tramp-file-name filename nil - (let ((curbuf (current-buffer)) - (rem-enc (tramp-get-remote-encoding multi-method method user host)) - (rem-dec (tramp-get-remote-decoding multi-method method user host)) - (loc-enc (tramp-get-local-encoding multi-method method user host)) - (loc-dec (tramp-get-local-decoding multi-method method user host)) - (trampbuf (get-buffer-create "*tramp output*")) - (modes (file-modes filename)) + (unless (null append) + (tramp-error + v 'file-error "Cannot append to file using Tramp (`%s')" filename)) + ;; Following part commented out because we don't know what to do about + ;; file locking, and it does not appear to be a problem to ignore it. + ;; Ange-ftp ignores it, too. + ;; (when (and lockname (stringp lockname)) + ;; (setq lockname (expand-file-name lockname))) + ;; (unless (or (eq lockname nil) + ;; (string= lockname filename)) + ;; (error + ;; "tramp-handle-write-region: LOCKNAME must be nil or equal FILENAME")) + ;; XEmacs takes a coding system as the seventh argument, not `confirm' + (when (and (not (featurep 'xemacs)) confirm (file-exists-p filename)) + (unless (y-or-n-p (format "File %s exists; overwrite anyway? " filename)) + (tramp-error v 'file-error "File not overwritten"))) + (let ((rem-dec (tramp-get-remote-coding v "remote-decoding")) + (loc-enc (tramp-get-local-coding v "local-encoding")) + (modes (save-excursion (file-modes filename))) ;; We use this to save the value of `last-coding-system-used' ;; after writing the tmp file. At the end of the function, ;; we set `last-coding-system-used' to this saved value. @@ -4050,14 +3827,10 @@ ;; talking to the remote shell or suchlike won't hose this ;; variable. This approach was snarfed from ange-ftp.el. coding-system-used - tmpfil) - ;; Write region into a tmp file. This isn't really needed if we - ;; use an encoding function, but currently we use it always - ;; because this makes the logic simpler. - (setq tmpfil (tramp-make-temp-file filename)) - ;; Set current buffer. If connection wasn't open, `file-modes' has - ;; changed it accidently. - (set-buffer curbuf) + ;; Write region into a tmp file. This isn't really needed if we + ;; use an encoding function, but currently we use it always + ;; because this makes the logic simpler. + (tmpfil (tramp-make-temp-file filename))) ;; We say `no-message' here because we don't want the visited file ;; modtime data to be clobbered from the temp file. We call ;; `set-visited-file-modtime' ourselves later on. @@ -4080,96 +3853,106 @@ ;; decoding command must be specified. However, if the method ;; _also_ specifies an encoding function, then that is used for ;; encoding the contents of the tmp file. - (cond ((tramp-method-out-of-band-p multi-method method user host) + (cond ((and (tramp-method-out-of-band-p v) + (integerp start) + (> (- end start) tramp-copy-size-limit)) ;; `copy-file' handles out-of-band methods (copy-file tmpfil filename t t)) - ((and rem-enc rem-dec) + (rem-dec ;; Use inline file transfer - (let ((tmpbuf (get-buffer-create " *tramp file transfer*"))) - (save-excursion - ;; Encode tmpfil into tmpbuf - (tramp-message-for-buffer multi-method method user host - 5 "Encoding region...") - (set-buffer tmpbuf) - (erase-buffer) - ;; Use encoding function or command. - (if (and (symbolp loc-enc) (fboundp loc-enc)) - (progn - (tramp-message-for-buffer - multi-method method user host - 6 "Encoding region using function `%s'..." - (symbol-name loc-enc)) - (insert-file-contents-literally tmpfil) - ;; CCC. The following `let' is a workaround for - ;; the base64.el that comes with pgnus-0.84. If - ;; both of the following conditions are - ;; satisfied, it tries to write to a local file - ;; in default-directory, but at this point, - ;; default-directory is remote. - ;; (CALL-PROCESS-REGION can't write to remote - ;; files, it seems.) The file in question is a - ;; tmp file anyway. - (let ((default-directory - (tramp-temporary-file-directory))) - (funcall loc-enc (point-min) (point-max))) - (goto-char (point-max)) - (unless (bolp) - (newline))) - (tramp-message-for-buffer - multi-method method user host - 6 "Encoding region using command `%s'..." loc-enc) - (unless (equal 0 (tramp-call-local-coding-command - loc-enc tmpfil t)) - (pop-to-buffer trampbuf) - (error (concat "Cannot write to `%s', local encoding" - " command `%s' failed") - filename loc-enc))) - ;; Send tmpbuf into remote decoding command which - ;; writes to remote file. Because this happens on the - ;; remote host, we cannot use the function. - (tramp-message-for-buffer - multi-method method user host - 5 "Decoding region into remote file %s..." filename) - (tramp-send-command - multi-method method user host - (format "%s >%s <<'EOF'" - rem-dec - (tramp-shell-quote-argument localname))) - (set-buffer tmpbuf) - (tramp-message-for-buffer - multi-method method user host - 6 "Sending data to remote host...") - (tramp-send-string multi-method method user host - (buffer-string)) - ;; wait for remote decoding to complete - (tramp-message-for-buffer - multi-method method user host - 6 "Sending end of data token...") - (tramp-send-command - multi-method method user host "EOF" nil t) - (tramp-message-for-buffer - multi-method method user host 6 - "Waiting for remote host to process data...") - (set-buffer (tramp-get-buffer multi-method method user host)) - (tramp-wait-for-output) - (tramp-barf-unless-okay - multi-method method user host nil nil 'file-error - (concat "Couldn't write region to `%s'," - " decode using `%s' failed") - filename rem-dec) - (tramp-message 5 "Decoding region into remote file %s...done" - filename) - (kill-buffer tmpbuf)))) + ;; Encode tmpfil + (tramp-message v 5 "Encoding region...") + (unwind-protect + (with-temp-buffer + ;; Use encoding function or command. + (if (and (symbolp loc-enc) (fboundp loc-enc)) + (progn + (tramp-message + v 5 "Encoding region using function `%s'..." + (symbol-name loc-enc)) + (let ((coding-system-for-read 'binary)) + (insert-file-contents-literally tmpfil)) + ;; CCC. The following `let' is a workaround for + ;; the base64.el that comes with pgnus-0.84. If + ;; both of the following conditions are + ;; satisfied, it tries to write to a local file + ;; in default-directory, but at this point, + ;; default-directory is remote. + ;; (CALL-PROCESS-REGION can't write to remote + ;; files, it seems.) The file in question is a + ;; tmp file anyway. + (let ((default-directory + (tramp-temporary-file-directory))) + (funcall loc-enc (point-min) (point-max)))) + + (tramp-message + v 5 "Encoding region using command `%s'..." loc-enc) + (unless (equal 0 (tramp-call-local-coding-command + loc-enc tmpfil t)) + (tramp-error + v 'file-error + (concat "Cannot write to `%s', local encoding" + " command `%s' failed") + filename loc-enc))) + + ;; Send buffer into remote decoding command which + ;; writes to remote file. Because this happens on the + ;; remote host, we cannot use the function. + (goto-char (point-max)) + (unless (bolp) (newline)) + (tramp-message + v 5 "Decoding region into remote file %s..." filename) + (tramp-send-command + v + (format + "%s >%s <<'EOF'\n%sEOF" + rem-dec + (tramp-shell-quote-argument localname) + (buffer-string))) + (tramp-barf-unless-okay + v nil + (concat "Couldn't write region to `%s'," + " decode using `%s' failed") + filename rem-dec) + ;; When `file-precious-flag' is set, the region is + ;; written to a temporary file. Check that the + ;; checksum is equal to that from the local tmpfil. + (when file-precious-flag + (erase-buffer) + (and + ;; cksum runs locally + (let ((default-directory + (tramp-temporary-file-directory))) + (zerop (call-process "cksum" tmpfil t))) + ;; cksum runs remotely + (zerop + (tramp-send-command-and-check + v + (format + "cksum <%s" + (tramp-shell-quote-argument localname)))) + ;; ... they are different + (not + (string-equal + (buffer-string) + (with-current-buffer (tramp-get-buffer v) + (buffer-string)))) + (tramp-error + v 'file-error + (concat "Couldn't write region to `%s'," + " decode using `%s' failed") + filename rem-dec))) + (tramp-message + v 5 "Decoding region into remote file %s...done" filename) + (tramp-flush-file-property v localname)))) (t - (error + (tramp-error + v 'file-error (concat "Method `%s' should specify both encoding and " "decoding command or an rcp program") method))) (delete-file tmpfil) - (unless (equal curbuf (current-buffer)) - (error "Buffer has changed from `%s' to `%s'" - curbuf (current-buffer))) (when (or (eq visit t) (stringp visit)) (set-visited-file-modtime ;; We must pass modtime explicitely, because filename can be different @@ -4178,41 +3961,9 @@ ;; Make `last-coding-system-used' have the right value. (when (boundp 'last-coding-system-used) (set 'last-coding-system-used coding-system-used)) - (when (or (eq visit t) - (eq visit nil) - (stringp visit)) - (message "Wrote %s" filename))))) - -;; Call down to the real handler. -;; Because EFS does not play nicely with TRAMP (both systems match a -;; TRAMP file name) it is needed to disable efs as well as tramp for the -;; operation. -;; -;; Other than that, this is the canon file-handler code that the doco -;; says should be used here. Which is nice. -;; -;; Under XEmacs current, EFS also hooks in as -;; efs-sifn-handler-function to handle any filename with environment -;; variables. This has two implications: -;; 1) That EFS may not be completely dead (yet) for TRAMP filenames -;; 2) That TRAMP might want to do the same thing. -;; Details as they come in. -;; -;; Daniel Pittman - -;; (defun tramp-run-real-handler (operation args) -;; "Invoke normal file name handler for OPERATION. -;; This inhibits EFS and Ange-FTP, too, because they conflict with tramp. -;; First arg specifies the OPERATION, remaining ARGS are passed to the -;; OPERATION." -;; (let ((inhibit-file-name-handlers -;; (list 'tramp-file-name-handler -;; 'efs-file-handler-function -;; 'ange-ftp-hook-function -;; (and (eq inhibit-file-name-operation operation) -;; inhibit-file-name-handlers))) -;; (inhibit-file-name-operation operation)) -;; (apply operation args))) + (when (or (eq visit t) (null visit) (stringp visit)) + (tramp-message v 0 "Wrote %s" filename)) + (run-hooks 'tramp-handle-write-region-hook)))) ;;;###autoload (progn (defun tramp-run-real-handler (operation args) @@ -4230,10 +3981,6 @@ (inhibit-file-name-operation operation)) (apply operation args)))) -;; This function is used from `tramp-completion-file-name-handler' functions -;; only, if `tramp-completion-mode' is true. But this cannot be checked here -;; because the check is based on a full filename, not available for all -;; basic I/O operations. ;;;###autoload (progn (defun tramp-completion-run-real-handler (operation args) "Invoke `tramp-file-name-handler' for OPERATION. @@ -4306,28 +4053,37 @@ (nth 2 args)) ; BUF ((member operation - (list 'make-auto-save-file-name - 'set-visited-file-modtime 'verify-visited-file-modtime - ; XEmacs only + (list 'set-visited-file-modtime 'verify-visited-file-modtime + ; Emacs 22 only + 'make-auto-save-file-name + ; XEmacs only 'backup-buffer)) (buffer-file-name (if (bufferp (nth 0 args)) (nth 0 args) (current-buffer)))) ; COMMAND ((member operation - (list 'dired-call-process + (list ; not in Emacs 23 + 'dired-call-process ; Emacs only 'shell-command - ; Emacs 22 only + ; since Emacs 22 only 'process-file + ; since Emacs 23 only + 'start-file-process ; XEmacs only - 'dired-print-file 'dired-shell-call-process)) + 'dired-print-file 'dired-shell-call-process + ; nowhere yet + 'executable-find 'start-process 'call-process)) default-directory) ; unknown file primitive (t (error "unknown file I/O primitive: %s" operation)))) (defun tramp-find-foreign-file-name-handler (filename) "Return foreign file name handler if exists." - (when (tramp-tramp-file-p filename) + (when (and (stringp filename) (tramp-tramp-file-p filename) + (or (not (tramp-completion-mode)) + (not (string-match + tramp-completion-file-name-regexp filename)))) (let (elt res (handler-alist tramp-foreign-file-name-handler-alist)) @@ -4344,27 +4100,25 @@ (defun tramp-file-name-handler (operation &rest args) "Invoke Tramp file name handler. Falls back to normal file name handler if no tramp file name handler exists." -;; (setq edebug-trace t) -;; (edebug-trace "%s" (with-output-to-string (backtrace))) (save-match-data (let* ((filename (apply 'tramp-file-name-for-operation operation args)) - (completion (tramp-completion-mode filename)) + (completion (tramp-completion-mode)) (foreign (tramp-find-foreign-file-name-handler filename))) (with-parsed-tramp-file-name filename nil (cond - ;; When we are in completion mode, some operations shouldn' be + ;; When we are in completion mode, some operations shouldn't be ;; handled by backend. - ((and completion (memq operation '(expand-file-name))) - (tramp-run-real-handler operation args)) ((and completion (zerop (length localname)) (memq operation '(file-exists-p file-directory-p))) t) + ((and completion (zerop (length localname)) + (memq operation '(file-name-as-directory))) + filename) ;; Call the backend function. (foreign (apply foreign operation args)) ;; Nothing to do for us. (t (tramp-run-real-handler operation args))))))) - ;; In Emacs, there is some concurrency due to timers. If a timer ;; interrupts Tramp and wishes to use the same connection buffer as ;; the "main" Emacs, then garbage might occur in the connection @@ -4396,7 +4150,7 @@ "Invoke remote-shell Tramp file name handler. Fall back to normal file name handler if no Tramp handler exists." (when (and tramp-locked (not tramp-locker)) - (signal 'file-error "Forbidden reentrant call of Tramp")) + (signal 'file-error (list "Forbidden reentrant call of Tramp"))) (let ((tl tramp-locked)) (unwind-protect (progn @@ -4415,6 +4169,11 @@ Falls back to normal file name handler if no tramp file name handler exists." ;; (setq edebug-trace t) ;; (edebug-trace "%s" (with-output-to-string (backtrace))) + +;; (mapcar 'trace-function-background +;; (mapcar 'intern +;; (all-completions "tramp-" obarray 'functionp))) + (let ((fn (assoc operation tramp-completion-file-name-handler-alist))) (if fn (save-match-data (apply (cdr fn) args)) @@ -4423,6 +4182,11 @@ ;;;###autoload (defsubst tramp-register-file-name-handler () "Add tramp file name handler to `file-name-handler-alist'." + ;; Remove autoloaded handler from file name handler alist. Useful, + ;; if `tramp-syntax' has been changed. + (let ((a1 (rassq 'tramp-file-name-handler file-name-handler-alist))) + (setq file-name-handler-alist (delete a1 file-name-handler-alist))) + ;; Add the handler. (add-to-list 'file-name-handler-alist (cons tramp-file-name-regexp 'tramp-file-name-handler)) ;; If jka-compr is already loaded, move it to the front of @@ -4432,9 +4196,20 @@ (setq file-name-handler-alist (cons jka (delete jka file-name-handler-alist)))))) +;; `tramp-file-name-handler' must be registered before evaluation of +;; site-start and init files, because there might exist remote files +;; already, f.e. files kept via recentf-mode. +;;;###autoload(tramp-register-file-name-handler) +(tramp-register-file-name-handler) + ;;;###autoload (defsubst tramp-register-completion-file-name-handler () "Add tramp completion file name handler to `file-name-handler-alist'." + ;; Remove autoloaded handler from file name handler alist. Useful, + ;; if `tramp-syntax' has been changed. + (let ((a1 (rassq + 'tramp-completion-file-name-handler file-name-handler-alist))) + (setq file-name-handler-alist (delete a1 file-name-handler-alist))) ;; `partial-completion-mode' is unknown in XEmacs. So we should ;; load it unconditionally there. In the GNU Emacs case, method/ ;; user/host name completion shall be bound to `partial-completion-mode'. @@ -4452,17 +4227,12 @@ (setq file-name-handler-alist (cons jka (delete jka file-name-handler-alist)))))) -;; `tramp-file-name-handler' must be registered before evaluation of -;; site-start and init files, because there might exist remote files -;; already, f.e. files kept via recentf-mode. -;;;###autoload(tramp-register-file-name-handler) ;; During autoload, it shall be checked whether ;; `partial-completion-mode' is active. Therefore registering of ;; `tramp-completion-file-name-handler' will be delayed. ;;;###autoload(add-hook ;;;###autoload 'after-init-hook ;;;###autoload '(lambda () (tramp-register-completion-file-name-handler))) -(tramp-register-file-name-handler) (tramp-register-completion-file-name-handler) ;;;###autoload @@ -4476,20 +4246,19 @@ (add-hook 'tramp-unload-hook 'tramp-unload-file-name-handlers) - ;;; Interactions with other packages: ;; -- complete.el -- ;; This function contributed by Ed Sabol (defun tramp-handle-expand-many-files (name) - "Like `PC-expand-many-files' for tramp files." + "Like `PC-expand-many-files' for Tramp files." (with-parsed-tramp-file-name name nil (save-match-data (if (or (string-match "\\*" name) (string-match "\\?" name) (string-match "\\[.*\\]" name)) - (save-excursion + (progn (let (bufstr) ;; CCC: To do it right, we should quote certain characters ;; in the file name, but since the echo command is going to @@ -4499,37 +4268,34 @@ ;;- (set-difference tramp-file-name-quote-list ;;- '(?\* ?\? ?[ ?])))) ;;- (tramp-send-command - ;;- multi-method method user host - ;;- (format "echo %s" (comint-quote-filename localname))) - ;;- (tramp-wait-for-output)) - (tramp-send-command multi-method method user host - (format "echo %s" localname)) - (tramp-wait-for-output) + ;;- method user host + ;;- (format "echo %s" (comint-quote-filename localname)))) + (tramp-send-command v (format "echo %s" localname)) (setq bufstr (buffer-substring (point-min) (tramp-line-end-position))) - (goto-char (point-min)) - (if (string-equal localname bufstr) - nil - (insert "(\"") - (while (search-forward " " nil t) + (with-current-buffer (tramp-get-buffer v) + (goto-char (point-min)) + (if (string-equal localname bufstr) + nil + (insert "(\"") + (while (search-forward " " nil t) + (delete-backward-char 1) + (insert "\" \"")) + (goto-char (point-max)) (delete-backward-char 1) - (insert "\" \"")) - (goto-char (point-max)) - (delete-backward-char 1) - (insert "\")") - (goto-char (point-min)) - (mapcar - (function (lambda (x) - (tramp-make-tramp-file-name multi-method method - user host x))) - (read (current-buffer)))))) + (insert "\")") + (goto-char (point-min)) + (mapcar + (function (lambda (x) + (tramp-make-tramp-file-name method user host x))) + (read (current-buffer))))))) (list (expand-file-name name)))))) (eval-after-load "complete" '(progn (defadvice PC-expand-many-files (around tramp-advice-PC-expand-many-files (name) activate) - "Invoke `tramp-handle-expand-many-files' for tramp files." + "Invoke `tramp-handle-expand-many-files' for Tramp files." (if (tramp-tramp-file-p name) (setq ad-return-value (tramp-handle-expand-many-files name)) ad-do-it)) @@ -4538,142 +4304,118 @@ ;;; File name handler functions for completion mode -(defvar tramp-completion-mode nil - "If non-nil, we are in file name completion mode.") - ;; Necessary because `tramp-file-name-regexp-unified' and -;; `tramp-completion-file-name-regexp-unified' aren't different. -;; If nil, `tramp-completion-run-real-handler' is called (i.e. forwarding to -;; `tramp-file-name-handler'). Otherwise, it takes `tramp-run-real-handler'. -;; Using `last-input-event' is a little bit risky, because completing a file -;; might require loading other files, like "~/.netrc", and for them it -;; shouldn't be decided based on that variable. On the other hand, those files -;; shouldn't have partial tramp file name syntax. Maybe another variable should -;; be introduced overwriting this check in such cases. Or we change tramp -;; file name syntax in order to avoid ambiguities, like in XEmacs ... -;; In case of non unified file names it can be always true (and wouldn't be -;; necessary, because there are different regexp). -(defun tramp-completion-mode (file) +;; `tramp-completion-file-name-regexp-unified' aren't different. If +;; nil, `tramp-completion-run-real-handler' is called (i.e. forwarding +;; to `tramp-file-name-handler'). Otherwise, it takes +;; `tramp-run-real-handler'. Using `last-input-event' is a little bit +;; risky, because completing a file might require loading other files, +;; like "~/.netrc", and for them it shouldn't be decided based on that +;; variable. On the other hand, those files shouldn't have partial +;; tramp file name syntax. Maybe another variable should be introduced +;; overwriting this check in such cases. Or we change tramp file name +;; syntax in order to avoid ambiguities, like in XEmacs ... +(defun tramp-completion-mode () "Checks whether method / user name / host name completion is active." - (cond - (tramp-completion-mode t) - ((string-match "^/.*:.*:$" file) nil) - ((string-match - (concat tramp-prefix-regexp - "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp "$") - file) - (member (match-string 1 file) (mapcar 'car tramp-methods))) - ((or (equal last-input-event 'tab) - ;; Emacs - (and (natnump last-input-event) - (or - ;; ?\t has event-modifier 'control - (char-equal last-input-event ?\t) - (and (not (event-modifiers last-input-event)) - (or (char-equal last-input-event ?\?) - (char-equal last-input-event ?\ ))))) - ;; XEmacs - (and (featurep 'xemacs) - ;; `last-input-event' might be nil. - (not (null last-input-event)) - ;; `last-input-event' may have no character approximation. - (funcall (symbol-function 'event-to-character) last-input-event) - (or - ;; ?\t has event-modifier 'control - (char-equal - (funcall (symbol-function 'event-to-character) - last-input-event) ?\t) - (and (not (event-modifiers last-input-event)) - (or (char-equal - (funcall (symbol-function 'event-to-character) - last-input-event) ?\?) - (char-equal - (funcall (symbol-function 'event-to-character) - last-input-event) ?\ )))))) - t))) + (or (equal last-input-event 'tab) + ;; Emacs + (and (natnump last-input-event) + (or + ;; ?\t has event-modifier 'control + (char-equal last-input-event ?\t) + (and (not (event-modifiers last-input-event)) + (or (char-equal last-input-event ?\?) + (char-equal last-input-event ?\ ))))) + ;; XEmacs + (and (featurep 'xemacs) + ;; `last-input-event' might be nil. + (not (null last-input-event)) + ;; `last-input-event' may have no character approximation. + (funcall (symbol-function 'event-to-character) last-input-event) + (or + ;; ?\t has event-modifier 'control + (char-equal + (funcall (symbol-function 'event-to-character) + last-input-event) ?\t) + (and (not (event-modifiers last-input-event)) + (or (char-equal + (funcall (symbol-function 'event-to-character) + last-input-event) ?\?) + (char-equal + (funcall (symbol-function 'event-to-character) + last-input-event) ?\ ))))))) ;; Method, host name and user name completion. ;; `tramp-completion-dissect-file-name' returns a list of ;; tramp-file-name structures. For all of them we return possible completions. ;;;###autoload (defun tramp-completion-handle-file-name-all-completions (filename directory) - "Like `file-name-all-completions' for partial tramp files." - - (unwind-protect - ;; We need to reset `tramp-completion-mode'. - (progn - (setq tramp-completion-mode t) - (let* - ((fullname (concat directory filename)) - ;; possible completion structures - (v (tramp-completion-dissect-file-name fullname)) - result result1) - - (while v - (let* ((car (car v)) - (multi-method (tramp-file-name-multi-method car)) - (method (tramp-file-name-method car)) - (user (tramp-file-name-user car)) - (host (tramp-file-name-host car)) - (localname (tramp-file-name-localname car)) - (m (tramp-find-method multi-method method user host)) - (tramp-current-user user) ; see `tramp-parse-passwd' - all-user-hosts) - - (unless (or multi-method ;; Not handled (yet). - localname) ;; Nothing to complete - - (if (or user host) - - ;; Method dependent user / host combinations - (progn - (mapcar - (lambda (x) - (setq all-user-hosts - (append all-user-hosts - (funcall (nth 0 x) (nth 1 x))))) - (tramp-get-completion-function m)) - - (setq result (append result - (mapcar - (lambda (x) - (tramp-get-completion-user-host - method user host (nth 0 x) (nth 1 x))) - (delq nil all-user-hosts))))) - - ;; Possible methods - (setq result - (append result (tramp-get-completion-methods m))))) - - (setq v (cdr v)))) - - ;; unify list, remove nil elements - (while result - (let ((car (car result))) - (when car (add-to-list - 'result1 (substring car (length directory)))) - (setq result (cdr result)))) - - ;; Complete local parts - (append - result1 - (condition-case nil - (if result1 - ;; "/ssh:" does not need to be expanded as hostname. - (tramp-run-real-handler - 'file-name-all-completions (list filename directory)) - ;; No method/user/host found to be expanded. - (tramp-completion-run-real-handler - 'file-name-all-completions (list filename directory))) - (error nil))))) - ;; unwindform - (setq tramp-completion-mode nil))) + "Like `file-name-all-completions' for partial Tramp files." + + (let* ((fullname (tramp-drop-volume-letter + (expand-file-name filename directory))) + ;; Possible completion structures. + (v (tramp-completion-dissect-file-name fullname)) + result result1) + + (while v + (let* ((car (car v)) + (method (tramp-file-name-method car)) + (user (tramp-file-name-user car)) + (host (tramp-file-name-host car)) + (localname (tramp-file-name-localname car)) + (m (tramp-find-method method user host)) + (tramp-current-user user) ; see `tramp-parse-passwd' + all-user-hosts) + + (unless localname ;; Nothing to complete. + + (if (or user host) + + ;; Method dependent user / host combinations. + (progn + (mapcar + (lambda (x) + (setq all-user-hosts + (append all-user-hosts + (funcall (nth 0 x) (nth 1 x))))) + (tramp-get-completion-function m)) + + (setq result (append result + (mapcar + (lambda (x) + (tramp-get-completion-user-host + method user host (nth 0 x) (nth 1 x))) + (delq nil all-user-hosts))))) + + ;; Possible methods. + (setq result + (append result (tramp-get-completion-methods m))))) + + (setq v (cdr v)))) + + ;; Unify list, remove nil elements. + (while result + (let ((car (car result))) + (when car + (add-to-list + 'result1 + (substring car (length (tramp-drop-volume-letter directory))))) + (setq result (cdr result)))) + + ;; Complete local parts. + (append + result1 + (condition-case nil + (tramp-completion-run-real-handler + 'file-name-all-completions (list filename directory)) + (error nil))))) ;; Method, host name and user name completion for a file. ;;;###autoload (defun tramp-completion-handle-file-name-completion (filename directory &optional predicate) - "Like `file-name-completion' for tramp files." + "Like `file-name-completion' for Tramp files." (try-completion filename (mapcar 'list (file-name-all-completions filename directory)) @@ -4683,26 +4425,26 @@ ;; I misuse a little bit the tramp-file-name structure in order to handle ;; completion possibilities for partial methods / user names / host names. ;; Return value is a list of tramp-file-name structures according to possible -;; completions. If "multi-method" or "localname" is non-nil it means there +;; completions. If "localname" is non-nil it means there ;; shouldn't be a completion anymore. ;; Expected results: -;; "/x" "/[x" "/x@" "/[x@" "/x@y" "/[x@y" -;; [nil nil nil "x" nil] [nil nil "x" nil nil] [nil nil "x" "y" nil] -;; [nil nil "x" nil nil] -;; [nil "x" nil nil nil] - -;; "/x:" "/x:y" "/x:y:" -;; [nil nil nil "x" ""] [nil nil nil "x" "y"] [nil "x" nil "y" ""] -;; "/[x/" "/[x/y" -;; [nil "x" nil "" nil] [nil "x" nil "y" nil] -;; [nil "x" "" nil nil] [nil "x" "y" nil nil] - -;; "/x:y@" "/x:y@z" "/x:y@z:" -;; [nil nil nil "x" "y@"] [nil nil nil "x" "y@z"] [nil "x" "y" "z" ""] -;; "/[x/y@" "/[x/y@z" -;; [nil "x" nil "y" nil] [nil "x" "y" "z" nil] +;; "/x" "/[x" "/x@" "/[x@" "/x@y" "/[x@y" +;; [nil nil "x" nil] [nil "x" nil nil] [nil "x" "y" nil] +;; [nil "x" nil nil] +;; ["x" nil nil nil] + +;; "/x:" "/x:y" "/x:y:" +;; [nil nil "x" ""] [nil nil "x" "y"] ["x" nil "y" ""] +;; "/[x/" "/[x/y" +;; ["x" nil "" nil] ["x" nil "y" nil] +;; ["x" "" nil nil] ["x" "y" nil nil] + +;; "/x:y@" "/x:y@z" "/x:y@z:" +;; [nil nil "x" "y@"] [nil nil "x" "y@z"] ["x" "y" "z" ""] +;; "/[x/y@" "/[x/y@z" +;; ["x" nil "y" nil] ["x" "y" "z" nil] (defun tramp-completion-dissect-file-name (name) "Returns a list of `tramp-file-name' structures. They are collected by `tramp-completion-dissect-file-name1'." @@ -4727,25 +4469,49 @@ "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp "\\(" tramp-host-regexp x-nil "\\)$") nil 1 2 nil)) - ;; "/method:user" "/[method/user" + ;; "/method:user" "/[method/user" "/method://user" (tramp-completion-file-name-structure5 (list (concat tramp-prefix-regexp - "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp + "\\(" tramp-method-regexp "\\)" tramp-postfix-method-regexp "\\(" tramp-user-regexp x-nil "\\)$") 1 2 nil nil)) - ;; "/method:host" "/[method/host" + ;; "/method:host" "/[method/host" "/method://host" (tramp-completion-file-name-structure6 (list (concat tramp-prefix-regexp - "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp + "\\(" tramp-method-regexp "\\)" tramp-postfix-method-regexp "\\(" tramp-host-regexp x-nil "\\)$") 1 nil 2 nil)) - ;; "/method:user@host" "/[method/user@host" + ;; "/method:user@host" "/[method/user@host" "/method://user@host" (tramp-completion-file-name-structure7 (list (concat tramp-prefix-regexp - "\\(" tramp-method-regexp "\\)" tramp-postfix-single-method-regexp + "\\(" tramp-method-regexp "\\)" tramp-postfix-method-regexp "\\(" tramp-user-regexp "\\)" tramp-postfix-user-regexp "\\(" tramp-host-regexp x-nil "\\)$") - 1 2 3 nil))) + 1 2 3 nil)) + ;; "/method: "/method:/" + (tramp-completion-file-name-structure8 + (list + (if (equal tramp-syntax 'url) + (concat tramp-prefix-regexp + "\\(" tramp-method-regexp "\\)" + "\\(" (substring tramp-postfix-method-regexp 0 1) + "\\|" (substring tramp-postfix-method-regexp 1 2) "\\)" + "\\(" "\\)$") + ;; Should not match if not URL syntax. + (concat tramp-prefix-regexp "/$")) + 1 3 nil nil)) + ;; "/method: "/method:/" + (tramp-completion-file-name-structure9 + (list + (if (equal tramp-syntax 'url) + (concat tramp-prefix-regexp + "\\(" tramp-method-regexp "\\)" + "\\(" (substring tramp-postfix-method-regexp 0 1) + "\\|" (substring tramp-postfix-method-regexp 1 2) "\\)" + "\\(" "\\)$") + ;; Should not match if not URL syntax. + (concat tramp-prefix-regexp "/$")) + 1 nil 3 nil))) (mapcar (lambda (regexp) (add-to-list 'result @@ -4758,30 +4524,28 @@ tramp-completion-file-name-structure5 tramp-completion-file-name-structure6 tramp-completion-file-name-structure7 + tramp-completion-file-name-structure8 + tramp-completion-file-name-structure9 tramp-file-name-structure)) (delq nil result))) (defun tramp-completion-dissect-file-name1 (structure name) "Returns a `tramp-file-name' structure matching STRUCTURE. -The structure consists of multi-method, remote method, remote user, +The structure consists of remote method, remote user, remote host and localname (filename on remote host)." - (let (method) - (save-match-data - (when (string-match (nth 0 structure) name) - (setq method (and (nth 1 structure) - (match-string (nth 1 structure) name))) - (if (and method (member method tramp-multi-methods)) - ;; Not handled (yet). - (vector method nil nil nil nil) - (let ((user (and (nth 2 structure) - (match-string (nth 2 structure) name))) - (host (and (nth 3 structure) - (match-string (nth 3 structure) name))) - (localname (and (nth 4 structure) - (match-string (nth 4 structure) name)))) - (vector nil method user host localname))))))) + (save-match-data + (when (string-match (nth 0 structure) name) + (let ((method (and (nth 1 structure) + (match-string (nth 1 structure) name))) + (user (and (nth 2 structure) + (match-string (nth 2 structure) name))) + (host (and (nth 3 structure) + (match-string (nth 3 structure) name))) + (localname (and (nth 4 structure) + (match-string (nth 4 structure) name)))) + (vector method user host localname))))) ;; This function returns all possible method completions, adding the ;; trailing method delimeter. @@ -4791,8 +4555,8 @@ (lambda (method) (and method (string-match (concat "^" (regexp-quote partial-method)) method) - (tramp-make-tramp-file-name nil method nil nil nil))) - (delete "multi" (mapcar 'car tramp-methods)))) + (tramp-completion-make-tramp-file-name method nil nil nil))) + (mapcar 'car tramp-methods))) ;; Compares partial user and host names with possible completions. (defun tramp-get-completion-user-host (method partial-user partial-host user host) @@ -4824,13 +4588,15 @@ host nil))) (unless (zerop (+ (length user) (length host))) - (tramp-make-tramp-file-name nil method user host nil))) + (tramp-completion-make-tramp-file-name method user host nil))) (defun tramp-parse-rhosts (filename) "Return a list of (user host) tuples allowed to access. Either user or host may be nil." - - (let (res) + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let ((default-directory (tramp-temporary-file-directory)) + res) (when (file-readable-p filename) (with-temp-buffer (insert-file-contents filename) @@ -4839,24 +4605,15 @@ (push (tramp-parse-rhosts-group) res)))) res)) -;; Taken from gnus/netrc.el -(eval-and-compile - (defalias 'tramp-point-at-eol - (if (fboundp 'point-at-eol) - 'point-at-eol - 'line-end-position))) - (defun tramp-parse-rhosts-group () "Return a (user host) tuple allowed to access. Either user or host may be nil." - (let ((result) (regexp (concat "^\\(" tramp-host-regexp "\\)" "\\([ \t]+" "\\(" tramp-user-regexp "\\)" "\\)?"))) - - (narrow-to-region (point) (tramp-point-at-eol)) + (narrow-to-region (point) (tramp-line-end-position)) (when (re-search-forward regexp nil t) (setq result (append (list (match-string 3) (match-string 1))))) (widen) @@ -4866,8 +4623,10 @@ (defun tramp-parse-shosts (filename) "Return a list of (user host) tuples allowed to access. User is always nil." - - (let (res) + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let ((default-directory (tramp-temporary-file-directory)) + res) (when (file-readable-p filename) (with-temp-buffer (insert-file-contents filename) @@ -4879,11 +4638,9 @@ (defun tramp-parse-shosts-group () "Return a (user host) tuple allowed to access. User is always nil." - (let ((result) (regexp (concat "^\\(" tramp-host-regexp "\\)"))) - - (narrow-to-region (point) (tramp-point-at-eol)) + (narrow-to-region (point) (tramp-line-end-position)) (when (re-search-forward regexp nil t) (setq result (list nil (match-string 1)))) (widen) @@ -4895,8 +4652,10 @@ (defun tramp-parse-sconfig (filename) "Return a list of (user host) tuples allowed to access. User is always nil." - - (let (res) + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let ((default-directory (tramp-temporary-file-directory)) + res) (when (file-readable-p filename) (with-temp-buffer (insert-file-contents filename) @@ -4908,11 +4667,9 @@ (defun tramp-parse-sconfig-group () "Return a (user host) tuple allowed to access. User is always nil." - (let ((result) (regexp (concat "^[ \t]*Host[ \t]+" "\\(" tramp-host-regexp "\\)"))) - - (narrow-to-region (point) (tramp-point-at-eol)) + (narrow-to-region (point) (tramp-line-end-position)) (when (re-search-forward regexp nil t) (setq result (list nil (match-string 1)))) (widen) @@ -4924,11 +4681,12 @@ (defun tramp-parse-shostkeys (dirname) "Return a list of (user host) tuples allowed to access. User is always nil." - - (let ((regexp (concat "^key_[0-9]+_\\(" tramp-host-regexp "\\)\\.pub$")) - (files (when (file-directory-p dirname) (directory-files dirname))) - result) - + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let* ((default-directory (tramp-temporary-file-directory)) + (regexp (concat "^key_[0-9]+_\\(" tramp-host-regexp "\\)\\.pub$")) + (files (when (file-directory-p dirname) (directory-files dirname))) + result) (while files (when (string-match regexp (car files)) (push (list nil (match-string 1 (car files))) result)) @@ -4938,12 +4696,13 @@ (defun tramp-parse-sknownhosts (dirname) "Return a list of (user host) tuples allowed to access. User is always nil." - - (let ((regexp (concat "^\\(" tramp-host-regexp - "\\)\\.ssh-\\(dss\\|rsa\\)\\.pub$")) - (files (when (file-directory-p dirname) (directory-files dirname))) - result) - + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let* ((default-directory (tramp-temporary-file-directory)) + (regexp (concat "^\\(" tramp-host-regexp + "\\)\\.ssh-\\(dss\\|rsa\\)\\.pub$")) + (files (when (file-directory-p dirname) (directory-files dirname))) + result) (while files (when (string-match regexp (car files)) (push (list nil (match-string 1 (car files))) result)) @@ -4953,8 +4712,10 @@ (defun tramp-parse-hosts (filename) "Return a list of (user host) tuples allowed to access. User is always nil." - - (let (res) + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let ((default-directory (tramp-temporary-file-directory)) + res) (when (file-readable-p filename) (with-temp-buffer (insert-file-contents filename) @@ -4966,11 +4727,9 @@ (defun tramp-parse-hosts-group () "Return a (user host) tuple allowed to access. User is always nil." - (let ((result) (regexp (concat "^\\(" tramp-host-regexp "\\)"))) - - (narrow-to-region (point) (tramp-point-at-eol)) + (narrow-to-region (point) (tramp-line-end-position)) (when (re-search-forward regexp nil t) (unless (char-equal (or (char-after) ?\n) ?:) ; no IPv6 (setq result (list nil (match-string 1))))) @@ -4982,13 +4741,15 @@ ;; For su-alike methods it would be desirable to return "root@localhost" ;; as default. Unfortunately, we have no information whether any user name -;; has been typed already. So we (mis-)use tramp-current-user as indication, +;; has been typed already. So we use `tramp-current-user' as indication, ;; assuming it is set in `tramp-completion-handle-file-name-all-completions'. (defun tramp-parse-passwd (filename) "Return a list of (user host) tuples allowed to access. Host is always \"localhost\"." - - (let (res) + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let ((default-directory (tramp-temporary-file-directory)) + res) (if (zerop (length tramp-current-user)) '(("root" nil)) (when (file-readable-p filename) @@ -5002,11 +4763,9 @@ (defun tramp-parse-passwd-group () "Return a (user host) tuple allowed to access. Host is always \"localhost\"." - (let ((result) (regexp (concat "^\\(" tramp-user-regexp "\\):"))) - - (narrow-to-region (point) (tramp-point-at-eol)) + (narrow-to-region (point) (tramp-line-end-position)) (when (re-search-forward regexp nil t) (setq result (list (match-string 1) "localhost"))) (widen) @@ -5016,8 +4775,10 @@ (defun tramp-parse-netrc (filename) "Return a list of (user host) tuples allowed to access. User may be nil." - - (let (res) + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let ((default-directory (tramp-temporary-file-directory)) + res) (when (file-readable-p filename) (with-temp-buffer (insert-file-contents filename) @@ -5029,49 +4790,63 @@ (defun tramp-parse-netrc-group () "Return a (user host) tuple allowed to access. User may be nil." - (let ((result) (regexp (concat "^[ \t]*machine[ \t]+" "\\(" tramp-host-regexp "\\)" "\\([ \t]+login[ \t]+" "\\(" tramp-user-regexp "\\)" "\\)?"))) - - (narrow-to-region (point) (tramp-point-at-eol)) + (narrow-to-region (point) (tramp-line-end-position)) (when (re-search-forward regexp nil t) (setq result (list (match-string 3) (match-string 1)))) (widen) (forward-line 1) result)) +(defun tramp-parse-putty (registry) + "Return a list of (user host) tuples allowed to access. +User is always nil." + ;; On Windows, there are problems in completion when + ;; `default-directory' is remote. + (let ((default-directory (tramp-temporary-file-directory)) + res) + (with-temp-buffer + (when (zerop (call-process "reg" nil t nil "query" registry)) + (goto-char (point-min)) + (while (not (eobp)) + (push (tramp-parse-putty-group registry) res)))) + res)) + +(defun tramp-parse-putty-group (registry) + "Return a (user host) tuple allowed to access. +User is always nil." + (let ((result) + (regexp (concat (regexp-quote registry) "\\\\\\(.+\\)"))) + (narrow-to-region (point) (tramp-line-end-position)) + (when (re-search-forward regexp nil t) + (setq result (list nil (match-string 1)))) + (widen) + (forward-line 1) + result)) + ;;; Internal Functions: -(defun tramp-maybe-send-perl-script (multi-method method user host script name) - "Define in remote shell function NAME implemented as perl SCRIPT. -Only send the definition if it has not already been done. -Function may have 0-3 parameters." - (let ((remote-perl (tramp-get-remote-perl multi-method method user host))) - (unless remote-perl (error "No remote perl")) - (let ((perl-scripts (tramp-get-connection-property "perl-scripts" nil - multi-method method user host))) - (unless (memq name perl-scripts) - (with-current-buffer (tramp-get-buffer multi-method method user host) - (tramp-message 5 (concat "Sending the Perl script `" name "'...")) - (tramp-send-string multi-method method user host - (concat name - " () {\n" - remote-perl - " -e '" - script - "' \"$1\" \"$2\" \"$3\" 2>/dev/null\n}")) - (tramp-wait-for-output) - (tramp-set-connection-property "perl-scripts" (cons name perl-scripts) - multi-method method user host) - (tramp-message 5 (concat "Sending the Perl script `" name "'...done."))))))) +(defun tramp-maybe-send-script (vec script name) + "Define in remote shell function NAME implemented as SCRIPT. +Only send the definition if it has not already been done." + (let* ((p (tramp-get-connection-process vec)) + (scripts (tramp-get-connection-property p "scripts" nil))) + (unless (memq name scripts) + (tramp-message vec 5 "Sending script `%s'..." name) + ;; The script could contain a call of Perl. This is masked with `%s'. + (tramp-send-command-and-check + vec + (format "%s () {\n%s\n}" name + (format script (tramp-get-remote-perl vec)))) + (tramp-set-connection-property p "scripts" (cons name scripts)) + (tramp-message vec 5 "Sending script `%s'...done." name)))) (defun tramp-set-auto-save () - (when (and (buffer-file-name) - (tramp-tramp-file-p (buffer-file-name)) - ;; ange-ftp has its own auto-save mechanism + (when (and ;; ange-ftp has its own auto-save mechanism (eq (tramp-find-foreign-file-name-handler (buffer-file-name)) 'tramp-sh-file-name-handler) auto-save-default) @@ -5084,46 +4859,32 @@ (defun tramp-run-test (switch filename) "Run `test' on the remote system, given a SWITCH and a FILENAME. Returns the exit code of the `test' program." - (let ((v (tramp-dissect-file-name filename))) - (save-excursion - (tramp-send-command-and-check - (tramp-file-name-multi-method v) (tramp-file-name-method v) - (tramp-file-name-user v) (tramp-file-name-host v) - (format "test %s %s" switch - (tramp-shell-quote-argument (tramp-file-name-localname v))))))) - -(defun tramp-run-test2 (program file1 file2 &optional switch) - "Run `test'-like PROGRAM on the remote system, given FILE1, FILE2. -The optional SWITCH is inserted between the two files. -Returns the exit code of the `test' PROGRAM. Barfs if the methods, + (with-parsed-tramp-file-name filename nil + (tramp-send-command-and-check + v + (format + "%s %s %s" + (tramp-get-test-command v) + switch + (tramp-shell-quote-argument localname))))) + +(defun tramp-run-test2 (format-string file1 file2) + "Run `test'-like program on the remote system, given FILE1, FILE2. +FORMAT-STRING contains the program name, switches, and place holders. +Returns the exit code of the `test' program. Barfs if the methods, hosts, or files, disagree." - (let* ((v1 (tramp-dissect-file-name file1)) - (v2 (tramp-dissect-file-name file2)) - (mmethod1 (tramp-file-name-multi-method v1)) - (mmethod2 (tramp-file-name-multi-method v2)) - (method1 (tramp-file-name-method v1)) - (method2 (tramp-file-name-method v2)) - (user1 (tramp-file-name-user v1)) - (user2 (tramp-file-name-user v2)) - (host1 (tramp-file-name-host v1)) - (host2 (tramp-file-name-host v2)) - (localname1 (tramp-file-name-localname v1)) - (localname2 (tramp-file-name-localname v2))) - (unless (and method1 method2 host1 host2 - (equal mmethod1 mmethod2) - (equal method1 method2) - (equal user1 user2) - (equal host1 host2)) - (error "tramp-run-test2: %s" - "only implemented for same method, same user, same host")) - (save-excursion + (unless (tramp-equal-remote file1 file2) + (with-parsed-tramp-file-name (if (tramp-tramp-file-p file1) file1 file2) nil + (tramp-error + v 'file-error + "tramp-run-test2 only implemented for same method, user, host"))) + (with-parsed-tramp-file-name file1 v1 + (with-parsed-tramp-file-name file1 v2 (tramp-send-command-and-check - mmethod1 method1 user1 host1 - (format "%s %s %s %s" - program - (tramp-shell-quote-argument localname1) - (or switch "") - (tramp-shell-quote-argument localname2)))))) + v1 + (format format-string + (tramp-shell-quote-argument v1-localname) + (tramp-shell-quote-argument v2-localname)))))) (defun tramp-touch (file time) "Set the last-modified timestamp of the given file. @@ -5132,291 +4893,313 @@ ;; With GNU Emacs, `format-time-string' has an optional ;; parameter UNIVERSAL. This is preferred. (and (functionp 'subr-arity) + (subrp (symbol-function 'format-time-string)) (= 3 (cdr (funcall (symbol-function 'subr-arity) (symbol-function 'format-time-string)))))) (touch-time (if utc (format-time-string "%Y%m%d%H%M.%S" time t) - (format-time-string "%Y%m%d%H%M.%S" time)))) - (if (tramp-tramp-file-p file) + (format-time-string "%Y%m%d%H%M.%S" time))) + (default-directory (file-name-directory file))) + + (if (eq (tramp-find-foreign-file-name-handler file) + 'tramp-sh-file-name-handler) (with-parsed-tramp-file-name file nil - (let ((buf (tramp-get-buffer multi-method method user host))) - (unless (zerop (tramp-send-command-and-check - multi-method method user host - (format "%s touch -t %s %s" - (if utc "TZ=UTC; export TZ;" "") - touch-time - (tramp-shell-quote-argument localname)) - t)) - (pop-to-buffer buf) - (error "tramp-touch: touch failed, see buffer `%s' for details" - buf)))) - ;; It's a local file + (tramp-send-command + v (format "%s touch -t %s %s" + (if utc "TZ=UTC; export TZ;" "") + touch-time + (tramp-shell-quote-argument localname)))) (with-temp-buffer - (unless (zerop (call-process - "touch" nil (current-buffer) nil "-t" touch-time file)) - (pop-to-buffer (current-buffer)) - (error "tramp-touch: touch failed")))))) - -(defun tramp-buffer-name (multi-method method user host) - "A name for the connection buffer for USER at HOST using METHOD." - (if multi-method - (tramp-buffer-name-multi-method "tramp" multi-method method user host) - (let ((method (tramp-find-method multi-method method user host))) - (if user - (format "*tramp/%s %s@%s*" method user host) - (format "*tramp/%s %s*" method host))))) - -(defun tramp-buffer-name-multi-method (prefix multi-method method user host) - "A name for the multi method connection buffer. -MULTI-METHOD gives the multi method, METHOD the array of methods, -USER the array of user names, HOST the array of host names." - (unless (and (= (length method) (length user)) - (= (length method) (length host))) - (error "Syntax error in multi method (implementation error)")) - (let ((len (length method)) - (i 0) - string-list) - (while (< i len) - (setq string-list - (cons (if (aref user i) - (format "%s#%s@%s:" (aref method i) - (aref user i) (aref host i)) - (format "%s@%s:" (aref method i) (aref host i))) - string-list)) - (setq i (1+ i))) - (format "*%s/%s %s*" - prefix multi-method - (apply 'concat (reverse string-list))))) - -(defun tramp-get-buffer (multi-method method user host) - "Get the connection buffer to be used for USER at HOST using METHOD." + (shell-command + (format "%s touch -t %s %s" + (if utc "TZ=UTC; export TZ;" "") + touch-time + (tramp-shell-quote-argument + (if (tramp-tramp-file-p file) + (with-parsed-tramp-file-name file nil localname) file))) + (current-buffer)))))) + +(defun tramp-buffer-name (vec) + "A name for the connection buffer VEC." + ;; We must use `tramp-file-name-real-host', because for gateway + ;; methods the default port will be expanded later on, which would + ;; tamper the name. + (let ((method (tramp-file-name-method vec)) + (user (tramp-file-name-user vec)) + (host (tramp-file-name-real-host vec))) + (if (not (zerop (length user))) + (format "*tramp/%s %s@%s*" method user host) + (format "*tramp/%s %s*" method host)))) + +(defun tramp-get-buffer (vec) + "Get the connection buffer to be used for VEC." + (or (get-buffer (tramp-buffer-name vec)) + (with-current-buffer (get-buffer-create (tramp-buffer-name vec)) + (setq buffer-undo-list t) + (setq default-directory + (tramp-make-tramp-file-name + (tramp-file-name-method vec) + (tramp-file-name-user vec) + (tramp-file-name-host vec) + "/")) + (current-buffer)))) + +(defun tramp-get-connection-buffer (vec) + "Get the connection buffer to be used for VEC. +In case a second asynchronous communication has been started, it is different +from `tramp-get-buffer'." + (or (tramp-get-connection-property vec "process-buffer" nil) + (tramp-get-buffer vec))) + +(defun tramp-get-connection-process (vec) + "Get the connection process to be used for VEC. +In case a second asynchronous communication has been started, it is different +from the default one." + (get-process + (or (tramp-get-connection-property vec "process-name" nil) + (tramp-buffer-name vec)))) + +(defun tramp-debug-buffer-name (vec) + "A name for the debug buffer for VEC." + ;; We must use `tramp-file-name-real-host', because for gateway + ;; methods the default port will be expanded later on, which would + ;; tamper the name. + (let ((method (tramp-file-name-method vec)) + (user (tramp-file-name-user vec)) + (host (tramp-file-name-real-host vec))) + (if (not (zerop (length user))) + (format "*debug tramp/%s %s@%s*" method user host) + (format "*debug tramp/%s %s*" method host)))) + +(defun tramp-get-debug-buffer (vec) + "Get the debug buffer for VEC." (with-current-buffer - (get-buffer-create (tramp-buffer-name multi-method method user host)) - (setq buffer-undo-list t) + (get-buffer-create (tramp-debug-buffer-name vec)) + (when (bobp) + (setq buffer-undo-list t) + ;; Activate outline-mode + (make-local-variable 'outline-regexp) + (make-local-variable 'outline-level) + ;; This runs `text-mode-hook' and `outline-mode-hook'. We must + ;; prevent that local processes die. Yes: I've seen + ;; `flyspell-mode', which starts "ispell" ... + (let ((default-directory (tramp-temporary-file-directory))) + (outline-mode)) + (setq outline-regexp "[0-9]+:[0-9]+:[0-9]+ [a-z0-9-]+ (\\([0-9]+\\)) #") +; (setq outline-regexp "[a-z.-]+:[0-9]+: [a-z0-9-]+ (\\([0-9]+\\)) #") + (setq outline-level 'tramp-outline-level)) (current-buffer))) -(defun tramp-debug-buffer-name (multi-method method user host) - "A name for the debug buffer for USER at HOST using METHOD." - (if multi-method - (tramp-buffer-name-multi-method "debug tramp" - multi-method method user host) - (let ((method (tramp-find-method multi-method method user host))) - (if user - (format "*debug tramp/%s %s@%s*" method user host) - (format "*debug tramp/%s %s*" method host))))) - -(defun tramp-get-debug-buffer (multi-method method user host) - "Get the debug buffer for USER at HOST using METHOD." - (with-current-buffer - (get-buffer-create - (tramp-debug-buffer-name multi-method method user host)) - (setq buffer-undo-list t) - (current-buffer))) - -(defun tramp-find-executable (multi-method method user host - progname dirlist ignore-tilde) - "Searches for PROGNAME in all directories mentioned in DIRLIST. -First args METHOD, USER and HOST specify the connection, PROGNAME -is the program to search for, and DIRLIST gives the list of directories -to search. If IGNORE-TILDE is non-nil, directory names starting -with `~' will be ignored. +(defun tramp-outline-level () + "Return the depth to which a statement is nested in the outline. +Point must be at the beginning of a header line. + +The outline level is equal to the verbosity of the Tramp message." + (1+ (string-to-number (match-string 1)))) + +(defun tramp-find-executable + (vec progname dirlist &optional ignore-tilde ignore-path) + "Searches for PROGNAME in $PATH and all directories mentioned in DIRLIST. +First arg VEC specifies the connection, PROGNAME is the program +to search for, and DIRLIST gives the list of directories to +search. If IGNORE-TILDE is non-nil, directory names starting +with `~' will be ignored. If IGNORE-PATH is non-nil, searches +only in DIRLIST. Returns the absolute file name of PROGNAME, if found, and nil otherwise. This function expects to be in the right *tramp* buffer." - (let (result) - (when ignore-tilde - ;; Remove all ~/foo directories from dirlist. In Emacs 20, - ;; `remove' is in CL, and we want to avoid CL dependencies. - (let (newdl d) - (while dirlist - (setq d (car dirlist)) - (setq dirlist (cdr dirlist)) - (unless (char-equal ?~ (aref d 0)) - (setq newdl (cons d newdl)))) - (setq dirlist (nreverse newdl)))) + (with-current-buffer (tramp-get-buffer vec) + (let (result) + ;; Check whether the executable is in $PATH. "which(1)" does not + ;; report always a correct error code; therefore we check the + ;; number of words it returns. + (unless ignore-path + (tramp-send-command vec (format "which \\%s | wc -w" progname)) + (goto-char (point-min)) + (if (looking-at "^1$") + (setq result (concat "\\" progname)))) + (unless result + (when ignore-tilde + ;; Remove all ~/foo directories from dirlist. In Emacs 20, + ;; `remove' is in CL, and we want to avoid CL dependencies. + (let (newdl d) + (while dirlist + (setq d (car dirlist)) + (setq dirlist (cdr dirlist)) + (unless (char-equal ?~ (aref d 0)) + (setq newdl (cons d newdl)))) + (setq dirlist (nreverse newdl)))) + (tramp-send-command + vec + (format (concat "while read d; " + "do if test -x $d/%s -a -f $d/%s; " + "then echo tramp_executable $d/%s; " + "break; fi; done <<'EOF'\n" + "%s\nEOF") + progname progname progname (mapconcat 'identity dirlist "\n"))) + (goto-char (point-max)) + (when (search-backward "tramp_executable " nil t) + (skip-chars-forward "^ ") + (skip-chars-forward " ") + (setq result (buffer-substring (point) (tramp-line-end-position))))) + result))) + +(defun tramp-set-remote-path (vec) + "Sets the remote environment PATH to existing directories. +I.e., for each directory in `tramp-remote-path', it is tested +whether it exists and if so, it is added to the environment +variable PATH." + (tramp-message vec 5 (format "Setting $PATH environment variable")) + + (with-current-buffer (tramp-get-connection-buffer vec) + (set (make-local-variable 'tramp-remote-path) + (copy-tree tramp-remote-path)) + (let* ((elt (memq 'tramp-default-remote-path tramp-remote-path)) + (tramp-default-remote-path + (with-connection-property vec "default-remote-path" + (when elt + (condition-case nil + (symbol-name + (tramp-send-command-and-read vec "getconf PATH")) + ;; Default if "getconf" is not available. + (error + (tramp-message + vec 3 + "`getconf PATH' not successful, using default value \"%s\"." + "/bin:/usr/bin") + "/bin:/usr/bin")))))) + (when elt + ;; Replace place holder `tramp-default-remote-path'. + (setcdr elt + (append + (tramp-split-string tramp-default-remote-path ":") + (cdr elt))) + (setq tramp-remote-path + (delq 'tramp-default-remote-path tramp-remote-path)))) + + ;; Check for existence of directories. + (setq tramp-remote-path + (delq + nil + (mapcar + (lambda (x) + (and + (with-connection-property vec x + (file-directory-p + (tramp-make-tramp-file-name + (tramp-file-name-method vec) + (tramp-file-name-user vec) + (tramp-file-name-host vec) + x))) + x)) + tramp-remote-path))) (tramp-send-command - multi-method method user host - (format (concat "while read d; " - "do if test -x $d/%s -a -f $d/%s; " - "then echo tramp_executable $d/%s; " - "break; fi; done <<'EOF'") - progname progname progname)) - (mapcar (lambda (d) - (tramp-send-command multi-method method user host d)) - dirlist) - (tramp-send-command multi-method method user host "EOF") - (tramp-wait-for-output) - (goto-char (point-max)) - (when (search-backward "tramp_executable " nil t) - (skip-chars-forward "^ ") - (skip-chars-forward " ") - (buffer-substring (point) (tramp-line-end-position))))) - -(defun tramp-set-remote-path (multi-method method user host var dirlist) - "Sets the remote environment VAR to existing directories from DIRLIST. -I.e., for each directory in DIRLIST, it is tested whether it exists and if -so, it is added to the environment variable VAR." - (let ((existing-dirs - (mapcar - (lambda (x) - (when (and - (file-exists-p - (tramp-make-tramp-file-name multi-method method user host x)) - (file-directory-p - (tramp-make-tramp-file-name multi-method method user host x))) - x)) - dirlist))) - (tramp-send-command - multi-method method user host - (concat var "=" - (mapconcat 'identity (delq nil existing-dirs) ":") - "; export " var)) - (tramp-wait-for-output))) + vec + (format "PATH=%s; export PATH" + (mapconcat 'identity tramp-remote-path ":"))))) ;; -- communication with external shell -- -(defun tramp-find-file-exists-command (multi-method method user host) +(defun tramp-find-file-exists-command (vec) "Find a command on the remote host for checking if a file exists. Here, we are looking for a command which has zero exit status if the file exists and nonzero exit status otherwise." - (make-local-variable 'tramp-file-exists-command) - (tramp-message 9 "Finding command to check if file exists") - (let ((existing - (tramp-make-tramp-file-name - multi-method method user host - "/")) ;assume this file always exists + (let ((existing "/") (nonexisting - (tramp-make-tramp-file-name - multi-method method user host - "/ this file does not exist "))) ;assume this never exists + (tramp-shell-quote-argument "/ this file does not exist ")) + result) ;; The algorithm is as follows: we try a list of several commands. ;; For each command, we first run `$cmd /' -- this should return ;; true, as the root directory always exists. And then we run - ;; `$cmd /this\ file\ does\ not\ exist', hoping that the file indeed + ;; `$cmd /this\ file\ does\ not\ exist ', hoping that the file indeed ;; does not exist. This should return false. We use the first ;; command we find that seems to work. ;; The list of commands to try is as follows: - ;; `ls -d' This works on most systems, but NetBSD 1.4 - ;; has a bug: `ls' always returns zero exit - ;; status, even for files which don't exist. - ;; `test -e' Some Bourne shells have a `test' builtin - ;; which does not know the `-e' option. - ;; `/bin/test -e' For those, the `test' binary on disk normally - ;; provides the option. Alas, the binary - ;; is sometimes `/bin/test' and sometimes it's - ;; `/usr/bin/test'. - ;; `/usr/bin/test -e' In case `/bin/test' does not exist. + ;; `ls -d' This works on most systems, but NetBSD 1.4 + ;; has a bug: `ls' always returns zero exit + ;; status, even for files which don't exist. + ;; `test -e' Some Bourne shells have a `test' builtin + ;; which does not know the `-e' option. + ;; `/bin/test -e' For those, the `test' binary on disk normally + ;; provides the option. Alas, the binary + ;; is sometimes `/bin/test' and sometimes it's + ;; `/usr/bin/test'. + ;; `/usr/bin/test -e' In case `/bin/test' does not exist. (unless (or - (and (setq tramp-file-exists-command "test -e %s") - (file-exists-p existing) - (not (file-exists-p nonexisting))) - (and (setq tramp-file-exists-command "/bin/test -e %s") - (file-exists-p existing) - (not (file-exists-p nonexisting))) - (and (setq tramp-file-exists-command "/usr/bin/test -e %s") - (file-exists-p existing) - (not (file-exists-p nonexisting))) - (and (setq tramp-file-exists-command "ls -d %s") - (file-exists-p existing) - (not (file-exists-p nonexisting)))) - (error "Couldn't find command to check if file exists")))) + (and (setq result (format "%s -e" (tramp-get-test-command vec))) + (zerop (tramp-send-command-and-check + vec (format "%s %s" result existing))) + (not (zerop (tramp-send-command-and-check + vec (format "%s %s" result nonexisting))))) + (and (setq result "/bin/test -e") + (zerop (tramp-send-command-and-check + vec (format "%s %s" result existing))) + (not (zerop (tramp-send-command-and-check + vec (format "%s %s" result nonexisting))))) + (and (setq result "/usr/bin/test -e") + (zerop (tramp-send-command-and-check + vec (format "%s %s" result existing))) + (not (zerop (tramp-send-command-and-check + vec (format "%s %s" result nonexisting))))) + (and (setq result (format "%s -d" (tramp-get-ls-command vec))) + (zerop (tramp-send-command-and-check + vec (format "%s %s" result existing))) + (not (zerop (tramp-send-command-and-check + vec (format "%s %s" result nonexisting)))))) + (tramp-error + vec 'file-error "Couldn't find command to check if file exists")) + result)) ;; CCC test ksh or bash found for tilde expansion? -(defun tramp-find-shell (multi-method method user host) - "Find a shell on the remote host which groks tilde expansion." - (let ((shell nil)) - (tramp-send-command multi-method method user host "echo ~root") - (tramp-wait-for-output) - (cond - ((string-match "^~root$" (buffer-string)) - (setq shell - (or (tramp-find-executable multi-method method user host - "bash" tramp-remote-path t) - (tramp-find-executable multi-method method user host - "ksh" tramp-remote-path t))) - (unless shell - (error "Couldn't find a shell which groks tilde expansion")) - ;; Find arguments for this shell. - (let ((alist tramp-sh-extra-args) - item extra-args) - (while (and alist (null extra-args)) - (setq item (pop alist)) - (when (string-match (car item) shell) - (setq extra-args (cdr item)))) - (when extra-args (setq shell (concat shell " " extra-args)))) - (tramp-message - 5 "Starting remote shell `%s' for tilde expansion..." shell) - (tramp-send-command - multi-method method user host - (concat "PS1='$ ' exec " shell)) ; - (tramp-barf-if-no-shell-prompt - (get-buffer-process (current-buffer)) - 60 "Couldn't find remote `%s' prompt" shell) - (tramp-message - 9 "Setting remote shell prompt...") - ;; Douglas Gray Stephens says that we - ;; must use "\n" here, not tramp-rsh-end-of-line. Kai left the - ;; last tramp-rsh-end-of-line, Douglas wanted to replace that, - ;; as well. - (process-send-string nil (format "PS1='%s%s%s'; PS2=''; PS3=''%s" - tramp-rsh-end-of-line - tramp-end-of-output - tramp-rsh-end-of-line - tramp-rsh-end-of-line)) - (tramp-wait-for-output) - (tramp-message - 9 "Setting remote shell prompt...done") - ) - (t (tramp-message 5 "Remote `%s' groks tilde expansion, good" - (tramp-get-method-parameter - multi-method method user host 'tramp-remote-sh)))))) - -(defun tramp-check-ls-command (multi-method method user host cmd) - "Checks whether the given `ls' executable groks `-n'. -METHOD, USER and HOST specify the connection, CMD (the absolute file name of) -the `ls' executable. Returns t if CMD supports the `-n' option, nil -otherwise." - (tramp-message 9 "Checking remote `%s' command for `-n' option" cmd) - (when (file-executable-p - (tramp-make-tramp-file-name multi-method method user host cmd)) - (let ((result nil)) - (tramp-message 7 "Testing remote command `%s' for -n..." cmd) - (setq result - (tramp-send-command-and-check - multi-method method user host - (format "%s -lnd / >/dev/null" - cmd))) - (tramp-message 7 "Testing remote command `%s' for -n...%s" - cmd - (if (zerop result) "okay" "failed")) - (zerop result)))) - -(defun tramp-check-ls-commands (multi-method method user host cmd dirlist) - "Checks whether the given `ls' executable in one of the dirs groks `-n'. -Returns nil if none was found, else the command is returned." - (let ((dl dirlist) - (result nil)) - (tramp-let-maybe directory-sep-char ?/ ;for XEmacs - ;; It would be better to use the CL function `find', but - ;; we don't want run-time dependencies on CL. - (while (and dl (not result)) - (let ((x (concat (file-name-as-directory (car dl)) cmd))) - (when (tramp-check-ls-command multi-method method user host x) - (setq result x))) - (setq dl (cdr dl))) - result))) - -(defun tramp-find-ls-command (multi-method method user host) - "Finds an `ls' command which groks the `-n' option, returning nil if failed. -\(This option prints numeric user and group ids in a long listing.)" - (tramp-message 9 "Finding a suitable `ls' command") - (or - (tramp-check-ls-commands multi-method method user host "ls" tramp-remote-path) - (tramp-check-ls-commands multi-method method user host "gnuls" tramp-remote-path) - (tramp-check-ls-commands multi-method method user host "gls" tramp-remote-path))) +(defun tramp-find-shell (vec) + "Opens a shell on the remote host which groks tilde expansion." + (unless (tramp-get-connection-property vec "remote-shell" nil) + (let (shell) + (with-current-buffer (tramp-get-buffer vec) + (tramp-send-command vec "echo ~root") + (cond + ((string-match "^~root$" (buffer-string)) + (setq shell + (or (tramp-find-executable vec "bash" tramp-remote-path t) + (tramp-find-executable vec "ksh" tramp-remote-path t))) + (unless shell + (tramp-error + vec 'file-error + "Couldn't find a shell which groks tilde expansion")) + ;; Find arguments for this shell. + (let ((alist tramp-sh-extra-args) + item extra-args) + (while (and alist (null extra-args)) + (setq item (pop alist)) + (when (string-match (car item) shell) + (setq extra-args (cdr item)))) + (when extra-args (setq shell (concat shell " " extra-args)))) + (tramp-message + vec 5 "Starting remote shell `%s' for tilde expansion..." shell) + (tramp-send-command-internal vec (concat "PS1='$ ' exec " shell)) + (tramp-message vec 5 "Setting remote shell prompt...") + ;; Douglas Gray Stephens says that we + ;; must use "\n" here, not tramp-rsh-end-of-line. Kai left the + ;; last tramp-rsh-end-of-line, Douglas wanted to replace that, + ;; as well. + (tramp-send-command + vec + (format "PS1='%s%s%s'; PS2=''; PS3=''" + tramp-rsh-end-of-line + tramp-end-of-output + tramp-rsh-end-of-line)) + (tramp-message vec 5 "Setting remote shell prompt...done")) + (t (tramp-message + vec 5 "Remote `%s' groks tilde expansion, good" + (tramp-get-method-parameter + (tramp-file-name-method vec) 'tramp-remote-sh)) + (tramp-set-connection-property + vec "remote-shell" + (tramp-get-method-parameter + (tramp-file-name-method vec) 'tramp-remote-sh)))))))) ;; ------------------------------------------------------------ ;; -- Functions for establishing connection -- @@ -5426,635 +5209,208 @@ ;; prompts from the remote host. See the variable ;; `tramp-actions-before-shell' for usage of these functions. -(defun tramp-action-login (p multi-method method user host) +(defun tramp-action-login (proc vec) "Send the login name." - (tramp-message 9 "Sending login name `%s'" - (or user (user-login-name))) - (erase-buffer) - (process-send-string nil (concat (or user (user-login-name)) - tramp-rsh-end-of-line))) - -(defun tramp-action-password (p multi-method method user host) + (when (not (stringp tramp-current-user)) + (save-window-excursion + (let ((enable-recursive-minibuffers t)) + (pop-to-buffer (tramp-get-connection-buffer vec)) + (setq tramp-current-user (read-string (match-string 0)))))) + (tramp-message vec 3 "Sending login name `%s'" tramp-current-user) + (with-current-buffer (tramp-get-connection-buffer vec) + (tramp-message vec 6 "\n%s" (buffer-string))) + (tramp-send-string vec tramp-current-user)) + +(defun tramp-action-password (proc vec) "Query the user for a password." - (let ((pw-prompt - (format "Password for %s " - (tramp-make-tramp-file-name - nil method user host "")))) - (tramp-message 9 "Sending password") - (tramp-enter-password p pw-prompt user host))) - -(defun tramp-action-succeed (p multi-method method user host) + (tramp-message vec 3 "Sending password") + (tramp-enter-password proc)) + +(defun tramp-action-succeed (proc vec) "Signal success in finding shell prompt." - (tramp-message 9 "Found remote shell prompt.") - (erase-buffer) (throw 'tramp-action 'ok)) -(defun tramp-action-permission-denied (p multi-method method user host) +(defun tramp-action-permission-denied (proc vec) "Signal permission denied." - (pop-to-buffer (tramp-get-buffer multi-method method user host)) - (tramp-message 9 "Permission denied by remote host.") - (kill-process p) + (kill-process proc) (throw 'tramp-action 'permission-denied)) -(defun tramp-action-copy-failed (p multi-method method user host) - "Signal copy failed." - (kill-process p) - (error "%s" (match-string 1))) - -(defun tramp-action-yesno (p multi-method method user host) +(defun tramp-action-yesno (proc vec) "Ask the user for confirmation using `yes-or-no-p'. Send \"yes\" to remote process on confirmation, abort otherwise. See also `tramp-action-yn'." (save-window-excursion - (pop-to-buffer (tramp-get-buffer multi-method method user host)) - (unless (yes-or-no-p (match-string 0)) - (kill-process p) - (erase-buffer) - (throw 'tramp-action 'permission-denied)) - (process-send-string p (concat "yes" tramp-rsh-end-of-line)) - (erase-buffer))) - -(defun tramp-action-yn (p multi-method method user host) + (let ((enable-recursive-minibuffers t)) + (save-match-data (pop-to-buffer (tramp-get-connection-buffer vec))) + (unless (yes-or-no-p (match-string 0)) + (kill-process proc) + (throw 'tramp-action 'permission-denied)) + (with-current-buffer (tramp-get-connection-buffer vec) + (tramp-message vec 6 "\n%s" (buffer-string))) + (tramp-send-string vec "yes")))) + +(defun tramp-action-yn (proc vec) "Ask the user for confirmation using `y-or-n-p'. Send \"y\" to remote process on confirmation, abort otherwise. See also `tramp-action-yesno'." (save-window-excursion - (pop-to-buffer (tramp-get-buffer multi-method method user host)) - (unless (y-or-n-p (match-string 0)) - (kill-process p) - (throw 'tramp-action 'permission-denied)) - (erase-buffer) - (process-send-string p (concat "y" tramp-rsh-end-of-line)))) - -(defun tramp-action-terminal (p multi-method method user host) + (let ((enable-recursive-minibuffers t)) + (save-match-data (pop-to-buffer (tramp-get-connection-buffer vec))) + (unless (y-or-n-p (match-string 0)) + (kill-process proc) + (throw 'tramp-action 'permission-denied)) + (with-current-buffer (tramp-get-connection-buffer vec) + (tramp-message vec 6 "\n%s" (buffer-string))) + (tramp-send-string vec "y")))) + +(defun tramp-action-terminal (proc vec) "Tell the remote host which terminal type to use. The terminal type can be configured with `tramp-terminal-type'." - (tramp-message 9 "Setting `%s' as terminal type." - tramp-terminal-type) - (erase-buffer) - (process-send-string nil (concat tramp-terminal-type - tramp-rsh-end-of-line))) - -(defun tramp-action-process-alive (p multi-method method user host) + (tramp-message vec 5 "Setting `%s' as terminal type." tramp-terminal-type) + (tramp-send-string vec tramp-terminal-type)) + +(defun tramp-action-process-alive (proc vec) "Check whether a process has finished." - (unless (memq (process-status p) '(run open)) + (unless (memq (process-status proc) '(run open)) (throw 'tramp-action 'process-died))) -(defun tramp-action-out-of-band (p multi-method method user host) +(defun tramp-action-out-of-band (proc vec) "Check whether an out-of-band copy has finished." - (cond ((and (memq (process-status p) '(stop exit)) - (zerop (process-exit-status p))) - (tramp-message 9 "Process has finished.") + (cond ((and (memq (process-status proc) '(stop exit)) + (zerop (process-exit-status proc))) + (tramp-message vec 3 "Process has finished.") (throw 'tramp-action 'ok)) - ((or (and (memq (process-status p) '(stop exit)) - (not (zerop (process-exit-status p)))) - (memq (process-status p) '(signal))) + ((or (and (memq (process-status proc) '(stop exit)) + (not (zerop (process-exit-status proc)))) + (memq (process-status proc) '(signal))) ;; `scp' could have copied correctly, but set modes could have failed. ;; This can be ignored. - (goto-char (point-min)) - (if (re-search-forward tramp-operation-not-permitted-regexp nil t) - (progn - (tramp-message 10 "'set mode' error ignored.") - (tramp-message 9 "Process has finished.") - (throw 'tramp-action 'ok)) - (tramp-message 9 "Process has died.") - (throw 'tramp-action 'process-died))) + (with-current-buffer (process-buffer proc) + (goto-char (point-min)) + (if (re-search-forward tramp-operation-not-permitted-regexp nil t) + (progn + (tramp-message vec 5 "'set mode' error ignored.") + (tramp-message vec 3 "Process has finished.") + (throw 'tramp-action 'ok)) + (tramp-message vec 3 "Process has died.") + (throw 'tramp-action 'process-died)))) (t nil))) -;; The following functions are specifically for multi connections. - -(defun tramp-multi-action-login (p method user host) - "Send the login name." - (tramp-message 9 "Sending login name `%s'" user) - (erase-buffer) - (process-send-string p (concat user tramp-rsh-end-of-line))) - -(defun tramp-multi-action-password (p method user host) - "Query the user for a password." - (let ((pw-prompt - (format "Password for %s " - (tramp-make-tramp-file-name - nil method user host "")))) - (tramp-message 9 "Sending password") - (tramp-enter-password p pw-prompt user host))) - -(defun tramp-multi-action-succeed (p method user host) - "Signal success in finding shell prompt." - (tramp-message 9 "Found shell prompt on `%s'" host) - (erase-buffer) - (throw 'tramp-action 'ok)) - -(defun tramp-multi-action-permission-denied (p method user host) - "Signal permission denied." - (tramp-message 9 "Permission denied by remote host `%s'" host) - (kill-process p) - (erase-buffer) - (throw 'tramp-action 'permission-denied)) - -(defun tramp-multi-action-process-alive (p method user host) - "Check whether a process has finished." - (unless (memq (process-status p) '(run open)) - (throw 'tramp-action 'process-died))) - ;; Functions for processing the actions. -(defun tramp-process-one-action (p multi-method method user host actions) +(defun tramp-process-one-action (proc vec actions) "Wait for output from the shell and perform one action." - (let (found item pattern action todo) - (erase-buffer) - (tramp-message 9 "Waiting 60s for prompt from remote shell") + (let (found todo item pattern action) (while (not found) - (tramp-accept-process-output p 1) - (goto-char (point-min)) + ;; Reread output once all actions have been performed. + ;; Obviously, the output was not complete. + (tramp-accept-process-output proc 1) (setq todo actions) (while todo - (goto-char (point-min)) (setq item (pop todo)) - (setq pattern (symbol-value (nth 0 item))) + (setq pattern (concat (symbol-value (nth 0 item)) "\\'")) (setq action (nth 1 item)) - (tramp-message 10 "Looking for regexp \"%s\" from remote shell" - pattern) - (when (re-search-forward (concat pattern "\\'") nil t) - (setq found (funcall action p multi-method method user host))))) + (tramp-message + vec 5 "Looking for regexp \"%s\" from remote shell" pattern) + (when (tramp-check-for-regexp proc pattern) + (tramp-message vec 5 "Call `%s'" (symbol-name action)) + (setq found (funcall action proc vec))))) found)) -(defun tramp-process-actions - (p multi-method method user host actions &optional timeout) +(defun tramp-process-actions (proc vec actions &optional timeout) "Perform actions until success or TIMEOUT." - (tramp-message 10 "%s" (mapconcat 'identity (process-command p) " ")) (let (exit) (while (not exit) - (tramp-message 9 "Waiting for prompts from remote shell") + (tramp-message proc 3 "Waiting for prompts from remote shell") (setq exit (catch 'tramp-action (if timeout (with-timeout (timeout) - (tramp-process-one-action - p multi-method method user host actions)) - (tramp-process-one-action - p multi-method method user host actions)) - nil))) - (unless (eq exit 'ok) - (tramp-clear-passwd user host) - (error "Login failed")))) - -;; For multi-actions. - -(defun tramp-process-one-multi-action (p method user host actions) - "Wait for output from the shell and perform one action." - (let (found item pattern action todo) - (erase-buffer) - (tramp-message 9 "Waiting 60s for prompt from remote shell") - (with-timeout (60 (throw 'tramp-action 'timeout)) - (while (not found) - (tramp-accept-process-output p 1) - (setq todo actions) - (goto-char (point-min)) - (while todo - (goto-char (point-min)) - (setq item (pop todo)) - (setq pattern (symbol-value (nth 0 item))) - (setq action (nth 1 item)) - (tramp-message 10 "Looking for regexp \"%s\" from remote shell" - pattern) - (when (re-search-forward (concat pattern "\\'") nil t) - (setq found (funcall action p method user host))))) - found))) - -(defun tramp-process-multi-actions (p method user host actions) - "Perform actions until success." - (let (exit) - (while (not exit) - (tramp-message 9 "Waiting for prompts from remote shell") - (setq exit - (catch 'tramp-action - (tramp-process-one-multi-action p method user host actions) - nil))) + (tramp-process-one-action proc vec actions)) + (tramp-process-one-action proc vec actions))))) + (with-current-buffer (tramp-get-connection-buffer vec) + (tramp-message vec 6 "\n%s" (buffer-string))) (unless (eq exit 'ok) - (tramp-clear-passwd user host) - (error "Login failed")))) - -;; Functions to execute when we have seen the remote shell prompt but -;; before we exec the Bourne-ish shell. Note that these commands -;; might be sent to any shell, not just a Bourne-ish shell. This -;; means that the commands need to work in all shells. (It is also -;; okay for some commands to just fail with an error message, but -;; please make sure that they at least don't crash the odd shell people -;; might be running...) -(defun tramp-process-initial-commands (p - multi-method method user host - commands) - "Send list of commands to remote host, in order." - (let (cmd) - (while commands - (setq cmd (pop commands)) - (erase-buffer) - (tramp-message 10 "Sending command to remote shell: %s" - cmd) - (tramp-send-command multi-method method user host cmd nil t) - (tramp-barf-if-no-shell-prompt - p 60 "Remote shell command failed: %s" cmd)) - (erase-buffer))) - -;; The actual functions for opening connections. - -(defun tramp-open-connection-telnet (multi-method method user host) - "Open a connection using a telnet METHOD. -This starts the command `telnet HOST ARGS'[*], then waits for a remote -login prompt, then sends the user name USER, then waits for a remote -password prompt. It queries the user for the password, then sends the -password to the remote host. - -If USER is nil, uses value returned by `(user-login-name)' instead. - -Recognition of the remote shell prompt is based on the variables -`shell-prompt-pattern' and `tramp-shell-prompt-pattern' which must be -set up correctly. - -Please note that it is NOT possible to use this connection method -together with an out-of-band transfer method! You must use an inline -transfer method. - -Maybe the different regular expressions need to be tuned. - -* Actually, the telnet program as well as the args to be used can be - specified in the method parameters, see the variable `tramp-methods'." - (save-match-data - (when (tramp-method-out-of-band-p multi-method method user host) - (error "Cannot use out-of-band method `%s' with telnet connection method" - method)) - (when multi-method - (error "Cannot multi-connect using telnet connection method")) - (tramp-pre-connection multi-method method user host tramp-chunksize) - (tramp-message 7 "Opening connection for %s@%s using %s..." - (or user (user-login-name)) host method) - (let ((process-environment (copy-sequence process-environment))) - (setenv "TERM" tramp-terminal-type) - (setenv "PS1" "$ ") - (let* ((default-directory (tramp-temporary-file-directory)) - ;; If we omit the conditional here, then we would use - ;; `undecided-dos' in some cases. With the conditional, - ;; we use nil in these cases. Which one is right? - (coding-system-for-read (unless (and (not (featurep 'xemacs)) - (> emacs-major-version 20)) - tramp-dos-coding-system)) - (p (apply 'start-process - (tramp-buffer-name multi-method method user host) - (tramp-get-buffer multi-method method user host) - (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-login-program) - host - (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-login-args))) - (found nil) - (pw nil)) - (tramp-set-process-query-on-exit-flag p nil) - (set-buffer (tramp-get-buffer multi-method method user host)) - (erase-buffer) - (tramp-process-actions p multi-method method user host - tramp-actions-before-shell 60) - (tramp-open-connection-setup-interactive-shell - p multi-method method user host) - (tramp-post-connection multi-method method user host))))) - - -(defun tramp-open-connection-rsh (multi-method method user host) - "Open a connection using an rsh METHOD. -This starts the command `rsh HOST -l USER'[*], then waits for a remote -password or shell prompt. If a password prompt is seen, the user is -queried for a password, this function sends the password to the remote -host and waits for a shell prompt. - -If USER is nil, start the command `rsh HOST'[*] instead - -Recognition of the remote shell prompt is based on the variables -`shell-prompt-pattern' and `tramp-shell-prompt-pattern' which must be -set up correctly. - -Kludgy feature: if HOST has the form \"xx#yy\", then yy is assumed to -be a port number for ssh, and \"-p yy\" will be added to the list of -arguments, and xx will be used as the host name to connect to. - -* Actually, the rsh program to be used can be specified in the - method parameters, see the variable `tramp-methods'." - (save-match-data - (when multi-method - (error "Cannot multi-connect using rsh connection method")) - (tramp-pre-connection multi-method method user host tramp-chunksize) - (if (and user (not (string= user ""))) - (tramp-message 7 "Opening connection for %s@%s using %s..." - user host method) - (tramp-message 7 "Opening connection at %s using %s..." host method)) - (let ((process-environment (copy-sequence process-environment)) - (bufnam (tramp-buffer-name multi-method method user host)) - (buf (tramp-get-buffer multi-method method user host)) - (login-program (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-login-program)) - (login-args (mapcar - (lambda (x) - (format-spec - x `((?t . ,(format "/tmp/%s" tramp-temp-name-prefix))))) - (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-login-args))) - (real-host host)) - ;; The following should be changed. We need a more general - ;; mechanism to parse extra host args. - (when (string-match "\\([^#]*\\)#\\(.*\\)" host) - (setq login-args (cons "-p" (cons (match-string 2 host) login-args))) - (setq real-host (match-string 1 host))) - (setenv "TERM" tramp-terminal-type) - (setenv "PS1" "$ ") - (let* ((default-directory (tramp-temporary-file-directory)) - ;; If we omit the conditional, we would use - ;; `undecided-dos' in some cases. With the conditional, - ;; we use nil in these cases. Which one is right? - (coding-system-for-read (unless (and (not (featurep 'xemacs)) - (> emacs-major-version 20)) - tramp-dos-coding-system)) - (p (if (and user (not (string= user ""))) - (apply #'start-process bufnam buf login-program - real-host "-l" user login-args) - (apply #'start-process bufnam buf login-program - real-host login-args))) - (found nil)) - (tramp-set-process-query-on-exit-flag p nil) - - (set-buffer buf) - (tramp-process-actions p multi-method method user host - tramp-actions-before-shell 60) - (tramp-message 7 "Initializing remote shell") - (tramp-open-connection-setup-interactive-shell - p multi-method method user host) - (tramp-post-connection multi-method method user host))))) - -(defun tramp-open-connection-su (multi-method method user host) - "Open a connection using the `su' program with METHOD. -This starts `su - USER', then waits for a password prompt. The HOST -name must be equal to the local host name or to `localhost'. - -If USER is nil, uses value returned by user-login-name instead. - -Recognition of the remote shell prompt is based on the variables -`shell-prompt-pattern' and `tramp-shell-prompt-pattern' which must be -set up correctly. Note that the other user may have a different shell -prompt than you do, so it is not at all unlikely that the variable -`shell-prompt-pattern' is set up wrongly!" - (save-match-data - (when (tramp-method-out-of-band-p multi-method method user host) - (error "Cannot use out-of-band method `%s' with `su' connection method" - method)) - (unless (or (string-match (concat "^" (regexp-quote host)) - (system-name)) - (string= "localhost" host) - (string= "" host)) - (error - "Cannot connect to different host `%s' with `su' connection method" - host)) - (tramp-pre-connection multi-method method user host tramp-chunksize) - (tramp-message 7 "Opening connection for `%s' using `%s'..." - (or user "") method) - (let ((process-environment (copy-sequence process-environment))) - (setenv "TERM" tramp-terminal-type) - (setenv "PS1" "$ ") - (let* ((default-directory (tramp-temporary-file-directory)) - ;; If we omit the conditional, we use `undecided-dos' in - ;; some cases. With the conditional, we use nil in these - ;; cases. What's the difference? Which one is right? - (coding-system-for-read (unless (and (not (featurep 'xemacs)) - (> emacs-major-version 20)) - tramp-dos-coding-system)) - (p (apply 'start-process - (tramp-buffer-name multi-method method user host) - (tramp-get-buffer multi-method method user host) - (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-login-program) - (mapcar - (lambda (x) - (format-spec x `((?u . ,(or user "root"))))) - (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-login-args)))) - (found nil) - (pw nil)) - (tramp-set-process-query-on-exit-flag p nil) - (set-buffer (tramp-get-buffer multi-method method user host)) - (tramp-process-actions p multi-method method user host - tramp-actions-before-shell 60) - (tramp-open-connection-setup-interactive-shell - p multi-method method user host) - (tramp-post-connection multi-method method - user host))))) - -;; HHH: Not Changed. Multi method. It is not clear to me how this can -;; handle not giving a user name in the "file name". -;; -;; This is more difficult than for the single-hop method. In the -;; multi-hop-method, the desired behaviour should be that the -;; user must specify names for the telnet hops of which the user -;; name is different than the "original" name (or different from -;; the previous hop. -(defun tramp-open-connection-multi (multi-method method user host) - "Open a multi-hop connection using METHOD. -This uses a slightly changed file name syntax. The idea is to say - [multi/telnet:u1@h1/rsh:u2@h2]/path/to/file -This will use telnet to log in as u1 to h1, then use rsh from there to -log in as u2 to h2." - (save-match-data - (unless multi-method - (error "Multi-hop open connection function called on non-multi method")) - (when (tramp-method-out-of-band-p multi-method method user host) - (error "No out of band multi-hop connections")) - (unless (and (arrayp method) (not (stringp method))) - (error "METHOD must be an array of strings for multi methods")) - (unless (and (arrayp user) (not (stringp user))) - (error "USER must be an array of strings for multi methods")) - (unless (and (arrayp host) (not (stringp host))) - (error "HOST must be an array of strings for multi methods")) - (unless (and (= (length method) (length user)) - (= (length method) (length host))) - (error "Arrays METHOD, USER, HOST must have equal length")) - (tramp-pre-connection multi-method method user host tramp-chunksize) - (tramp-message 7 "Opening `%s' connection..." multi-method) - (let ((process-environment (copy-sequence process-environment))) - (setenv "TERM" tramp-terminal-type) - (setenv "PS1" "$ ") - (let* ((default-directory (tramp-temporary-file-directory)) - ;; If we omit the conditional, we use `undecided-dos' in - ;; some cases. With the conditional, we use nil in these - ;; cases. What's the difference? Which one is right? - (coding-system-for-read (unless (and (not (featurep 'xemacs)) - (> emacs-major-version 20)) - tramp-dos-coding-system)) - (p (start-process (tramp-buffer-name multi-method method user host) - (tramp-get-buffer multi-method method user host) - tramp-multi-sh-program)) - (num-hops (length method)) - (i 0)) - (tramp-set-process-query-on-exit-flag p nil) - (tramp-message 9 "Waiting 60s for local shell to come up...") - (unless (tramp-wait-for-regexp - p 60 (format "\\(%s\\)\\'\\|\\(%s\\)\\'" - shell-prompt-pattern tramp-shell-prompt-pattern)) - (pop-to-buffer (buffer-name)) - (kill-process p) - (error "Couldn't find local shell prompt")) - ;; Now do all the connections as specified. - (while (< i num-hops) - (let* ((m (aref method i)) - (u (aref user i)) - (h (aref host i)) - (entry (assoc m tramp-multi-connection-function-alist)) - (multi-func (nth 1 entry)) - (command (nth 2 entry))) - ;; The multi-funcs don't need to do save-match-data, as that - ;; is done here. - (funcall multi-func p m u h command) - (erase-buffer) - (setq i (1+ i)))) - (tramp-open-connection-setup-interactive-shell - p multi-method method user host) - (tramp-post-connection multi-method method user host))))) - -;; HHH: Changed. Multi method. Don't know how to handle this in the case -;; of no user name provided. Hack to make it work as it did before: -;; changed `user' to `(or user (user-login-name))' in the places where -;; the value is actually used. -(defun tramp-multi-connect-telnet (p method user host command) - "Issue `telnet' command. -Uses shell COMMAND to issue a `telnet' command to log in as USER to -HOST. You can use percent escapes in COMMAND: `%h' is replaced with -the host name, and `%n' is replaced with an end of line character, as -set in `tramp-rsh-end-of-line'. Use `%%' if you want a literal percent -character. - -If USER is nil, uses the return value of (user-login-name) instead." - (let ((cmd (format-spec command - `((?h . ,host) (?n . ,tramp-rsh-end-of-line)))) - (cmd1 (format-spec command `((?h . ,host) (?n . "")))) - found pw) - (erase-buffer) - (tramp-message 9 "Sending telnet command `%s'" cmd1) - (process-send-string p cmd) - (tramp-process-multi-actions p method user host - tramp-multi-actions))) - -;; HHH: Changed. Multi method. Don't know how to handle this in the case -;; of no user name provided. Hack to make it work as it did before: -;; changed `user' to `(or user (user-login-name))' in the places where -;; the value is actually used. -(defun tramp-multi-connect-rlogin (p method user host command) - "Issue `rlogin' command. -Uses shell COMMAND to issue an `rlogin' command to log in as USER to -HOST. You can use percent escapes in COMMAND. `%u' will be replaced -with the user name, `%h' will be replaced with the host name, and `%n' -will be replaced with the value of `tramp-rsh-end-of-line'. You can use -`%%' if you want to use a literal percent character. - -If USER is nil, uses the return value of (user-login-name) instead." - (let ((cmd (format-spec command `((?h . ,host) - (?u . ,(or user (user-login-name))) - (?n . ,tramp-rsh-end-of-line)))) - (cmd1 (format-spec command `((?h . ,host) - (?u . ,(or user (user-login-name))) - (?n . "")))) - found) - (erase-buffer) - (tramp-message 9 "Sending rlogin command `%s'" cmd1) - (process-send-string p cmd) - (tramp-process-multi-actions p method user host - tramp-multi-actions))) - -;; HHH: Changed. Multi method. Don't know how to handle this in the case -;; of no user name provided. Hack to make it work as it did before: -;; changed `user' to `(or user (user-login-name))' in the places where -;; the value is actually used. -(defun tramp-multi-connect-su (p method user host command) - "Issue `su' command. -Uses shell COMMAND to issue a `su' command to log in as USER on -HOST. The HOST name is ignored, this just changes the user id on the -host currently logged in to. - -If USER is nil, uses the return value of (user-login-name) instead. - -You can use percent escapes in the COMMAND. `%u' is replaced with the -user name, and `%n' is replaced with the value of -`tramp-rsh-end-of-line'. Use `%%' if you want a literal percent -character." - (let ((cmd (format-spec command `((?u . ,(or user (user-login-name))) - (?n . ,tramp-rsh-end-of-line)))) - (cmd1 (format-spec command `((?u . ,(or user (user-login-name))) - (?n . "")))) - found) - (erase-buffer) - (tramp-message 9 "Sending su command `%s'" cmd1) - (process-send-string p cmd) - (tramp-process-multi-actions p method user host - tramp-multi-actions))) + (tramp-clear-passwd) + (tramp-error-with-buffer + nil vec 'file-error + (cond + ((eq exit 'permission-denied) "Permission denied") + ((eq exit 'process-died) "Process died") + (t "Login failed")))))) ;; Utility functions. -(defun tramp-accept-process-output - (&optional process timeout timeout-msecs) +(defun tramp-accept-process-output (&optional proc timeout timeout-msecs) "Like `accept-process-output' for Tramp processes. This is needed in order to hide `last-coding-system-used', which is set for process communication also." - (let (last-coding-system-used) - (accept-process-output process timeout timeout-msecs))) + (with-current-buffer (process-buffer proc) + (tramp-message proc 10 "%s %s" proc (process-status proc)) + (let (buffer-read-only last-coding-system-used) + ;; Under Windows XP, accept-process-output doesn't return + ;; sometimes. So we add an additional timeout. + (with-timeout ((or timeout 1)) + (accept-process-output proc timeout timeout-msecs))) + (tramp-message proc 10 "\n%s" (buffer-string)))) + +(defun tramp-check-for-regexp (proc regexp) + "Check whether REGEXP is contained in process buffer of PROC. +Erase echoed commands if exists." + (with-current-buffer (process-buffer proc) + (goto-char (point-min)) + ;; Check whether we need to remove echo output. + (when (and (tramp-get-connection-property proc "check-remote-echo" nil) + (re-search-forward tramp-echoed-echo-mark-regexp nil t)) + (let ((begin (match-beginning 0))) + (when (re-search-forward tramp-echoed-echo-mark-regexp nil t) + ;; Discard echo from remote output. + (tramp-set-connection-property proc "check-remote-echo" nil) + (tramp-message proc 5 "echo-mark found") + (forward-line) + (delete-region begin (point)) + (goto-char (point-min))))) + ;; No echo to be handled, now we can look for the regexp. + (when (not (tramp-get-connection-property proc "check-remote-echo" nil)) + (re-search-forward regexp nil t)))) (defun tramp-wait-for-regexp (proc timeout regexp) "Wait for a REGEXP to appear from process PROC within TIMEOUT seconds. Expects the output of PROC to be sent to the current buffer. Returns the string that matched, or nil. Waits indefinitely if TIMEOUT is nil." - (let ((found nil) - (start-time (current-time))) - (cond (timeout - ;; Work around a bug in XEmacs 21, where the timeout - ;; expires faster than it should. This degenerates - ;; to polling for buggy XEmacsen, but oh, well. - (while (and (not found) - (< (tramp-time-diff (current-time) start-time) - timeout)) - (with-timeout (timeout) - (while (not found) - (tramp-accept-process-output proc 1) - (unless (memq (process-status proc) '(run open)) - (error "Process has died")) - (goto-char (point-min)) - (setq found (re-search-forward regexp nil t)))))) - (t - (while (not found) - (tramp-accept-process-output proc 1) - (unless (memq (process-status proc) '(run open)) - (error "Process has died")) - (goto-char (point-min)) - (setq found (re-search-forward regexp nil t))))) - (when tramp-debug-buffer - (append-to-buffer - (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method - tramp-current-user tramp-current-host) - (point-min) (point-max)) + (with-current-buffer (process-buffer proc) + (let ((found (tramp-check-for-regexp proc regexp)) + (start-time (current-time))) + (cond (timeout + ;; Work around a bug in XEmacs 21, where the timeout + ;; expires faster than it should. This degenerates + ;; to polling for buggy XEmacsen, but oh, well. + (while (and (not found) + (< (tramp-time-diff (current-time) start-time) + timeout)) + (with-timeout (timeout) + (while (not found) + (tramp-accept-process-output proc 1) + (unless (memq (process-status proc) '(run open)) + (tramp-error-with-buffer + nil proc 'file-error "Process has died")) + (setq found (tramp-check-for-regexp proc regexp)))))) + (t + (while (not found) + (tramp-accept-process-output proc 1) + (unless (memq (process-status proc) '(run open)) + (tramp-error-with-buffer + nil proc 'file-error "Process has died")) + (setq found (tramp-check-for-regexp proc regexp))))) + (tramp-message proc 6 "\n%s" (buffer-string)) (when (not found) - (save-excursion - (set-buffer - (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method - tramp-current-user tramp-current-host)) - (goto-char (point-max)) - (insert "[[Regexp `" regexp "' not found" - (if timeout (format " in %d secs" timeout) "") - "]]")))) - found)) + (if timeout + (tramp-error + proc 'file-error "[[Regexp `%s' not found in %d secs]]" + regexp timeout) + (tramp-error proc 'file-error "[[Regexp `%s' not found]]" regexp))) + found))) (defun tramp-wait-for-shell-prompt (proc timeout) "Wait for the shell prompt to appear from process PROC within TIMEOUT seconds. @@ -6071,51 +5427,23 @@ Looks at process PROC to see if a shell prompt appears in TIMEOUT seconds. If not, it produces an error message with the given ERROR-ARGS." (unless (tramp-wait-for-shell-prompt proc timeout) - (pop-to-buffer (buffer-name)) - (apply 'error error-args))) - -(defun tramp-enter-password (p prompt user host) - "Prompt for a password and send it to the remote end. -Uses PROMPT as a prompt and sends the password to process P." - (let ((pw (tramp-read-passwd user host prompt))) - (erase-buffer) - (process-send-string - p (concat pw - (or (tramp-get-method-parameter - tramp-current-multi-method - tramp-current-method - tramp-current-user - tramp-current-host - 'tramp-password-end-of-line) - tramp-default-password-end-of-line))))) - -;; HHH: Not Changed. This might handle the case where USER is not -;; given in the "File name" very poorly. Then, the local -;; variable tramp-current-user will be set to nil. -(defun tramp-pre-connection (multi-method method user host chunksize) - "Do some setup before actually logging in. -METHOD, USER and HOST specify the connection." - (set-buffer (tramp-get-buffer multi-method method user host)) - (set (make-local-variable 'tramp-current-multi-method) multi-method) - (set (make-local-variable 'tramp-current-method) method) - (set (make-local-variable 'tramp-current-user) user) - (set (make-local-variable 'tramp-current-host) host) - (set (make-local-variable 'tramp-chunksize) chunksize) - (set (make-local-variable 'inhibit-eol-conversion) nil) - (erase-buffer)) - -(defun tramp-open-connection-setup-interactive-shell - (p multi-method method user host) + (apply 'tramp-error-with-buffer nil proc 'file-error error-args))) + +;; We don't call `tramp-send-string' in order to hide the password from the +;; debug buffer, and because end-of-line handling of the string. +(defun tramp-enter-password (p) + "Prompt for a password and send it to the remote end." + (process-send-string + p (concat (tramp-read-passwd p) + (or (tramp-get-method-parameter + tramp-current-method + 'tramp-password-end-of-line) + tramp-default-password-end-of-line)))) + +(defun tramp-open-connection-setup-interactive-shell (proc vec) "Set up an interactive shell. -Mainly sets the prompt and the echo correctly. P is the shell process -to set up. METHOD, USER and HOST specify the connection." - ;; Wait a bit in case the remote end feels like sending a little - ;; junk first. It seems that fencepost.gnu.org does this when doing - ;; a Kerberos login. - (sit-for 1) - (tramp-discard-garbage-erase-buffer p multi-method method user host) - (tramp-process-initial-commands p multi-method method user host - tramp-initial-commands) +Mainly sets the prompt and the echo correctly. PROC is the shell +process to set up. VEC specifies the connection." ;; It is useful to set the prompt in the following command because ;; some people have a setting for $PS1 which /bin/sh doesn't know ;; about and thus /bin/sh will display a strange prompt. For @@ -6129,116 +5457,84 @@ ;; called as sh) on startup; this way, we avoid the startup file ;; clobbering $PS1. (tramp-send-command-internal - multi-method method user host + vec (format "exec env 'ENV=' 'PS1=$ ' %s" (tramp-get-method-parameter - multi-method method user host 'tramp-remote-sh)) - (format "remote `%s' to come up" - (tramp-get-method-parameter - multi-method method user host 'tramp-remote-sh))) - (tramp-barf-if-no-shell-prompt - p 30 - "Remote `%s' didn't come up. See buffer `%s' for details" - (tramp-get-method-parameter multi-method method user host 'tramp-remote-sh) - (buffer-name)) - (tramp-message 8 "Setting up remote shell environment") - (tramp-discard-garbage-erase-buffer p multi-method method user host) - (tramp-send-command-internal multi-method method user host - "stty -inlcr -echo kill '^U'") - (erase-buffer) - ;; Ignore garbage after stty command. - (tramp-send-command-internal multi-method method user host - "echo foo") - (erase-buffer) - (tramp-send-command-internal multi-method method user host - "TERM=dumb; export TERM") - (erase-buffer) + (tramp-file-name-method vec) 'tramp-remote-sh))) + (tramp-message vec 5 "Setting up remote shell environment") + (tramp-send-command-internal vec "stty -inlcr -echo kill '^U' erase '^H'") + ;; Check whether the echo has really been disabled. Some + ;; implementations, like busybox of embedded GNU/Linux, don't + ;; support disabling. + (tramp-send-command-internal vec "echo foo") + (with-current-buffer (process-buffer proc) + (goto-char (point-min)) + (when (looking-at "echo foo") + (tramp-set-connection-property vec "remote-echo" t) + (tramp-message vec 5 "Remote echo still on. Ok.") + ;; Make sure backspaces and their echo are enabled and no line + ;; width magic interferes with them. + (tramp-send-command-internal vec "stty icanon erase ^H cols 32767"))) + ;; Try to set up the coding system correctly. + ;; CCC this can't be the right way to do it. Hm. + (tramp-message vec 5 "Determining coding system") + (tramp-send-command-internal vec "echo foo ; echo bar") + (with-current-buffer (process-buffer proc) + (goto-char (point-min)) + (if (featurep 'mule) + ;; Use MULE to select the right EOL convention for communicating + ;; with the process. + (let* ((cs (or (process-coding-system proc) + (cons 'undecided 'undecided))) + cs-decode cs-encode) + (when (symbolp cs) (setq cs (cons cs cs))) + (setq cs-decode (car cs)) + (setq cs-encode (cdr cs)) + (unless cs-decode (setq cs-decode 'undecided)) + (unless cs-encode (setq cs-encode 'undecided)) + (setq cs-encode (tramp-coding-system-change-eol-conversion + cs-encode 'unix)) + (when (search-forward "\r" nil t) + (setq cs-decode (tramp-coding-system-change-eol-conversion + cs-decode 'dos))) + (set-buffer-process-coding-system cs-decode cs-encode)) + ;; Look for ^M and do something useful if found. + (when (search-forward "\r" nil t) + ;; We have found a ^M but cannot frob the process coding system + ;; because we're running on a non-MULE Emacs. Let's try + ;; stty, instead. + (tramp-send-command-internal vec "stty -onlcr")))) + (tramp-send-command-internal vec "set +o vi +o emacs") + (tramp-message vec 5 "Setting shell prompt") + ;; Douglas Gray Stephens says that we must + ;; use "\n" here, not tramp-rsh-end-of-line. We also manually frob + ;; the last time we sent a command, to avoid `tramp-send-command' to + ;; send "echo are you awake". + (tramp-send-command + vec + (format "PS1='%s%s%s'; PS2=''; PS3=''" + tramp-rsh-end-of-line + tramp-end-of-output + tramp-rsh-end-of-line)) ;; Check whether the remote host suffers from buggy `send-process-string'. ;; This is known for FreeBSD (see comment in `send_process', file process.c). ;; I've tested sending 624 bytes successfully, sending 625 bytes failed. ;; Emacs makes a hack when this host type is detected locally. It cannot ;; handle remote hosts, though. - (when (or (not tramp-chunksize) (zerop tramp-chunksize)) - (tramp-message 9 "Checking remote host type for `send-process-string' bug") - (tramp-send-command-internal multi-method method user host - "(uname -sr) 2>/dev/null") - (goto-char (point-min)) - (when (looking-at "FreeBSD") - (setq tramp-chunksize 500))) - - ;; Try to set up the coding system correctly. - ;; CCC this can't be the right way to do it. Hm. - (save-excursion - (erase-buffer) - (tramp-message 9 "Determining coding system") - (tramp-send-command-internal multi-method method user host - "echo foo ; echo bar") - (goto-char (point-min)) - (if (featurep 'mule) - ;; Use MULE to select the right EOL convention for communicating - ;; with the process. - (let* ((cs (or (process-coding-system p) (cons 'undecided 'undecided))) - cs-decode cs-encode) - (when (symbolp cs) (setq cs (cons cs cs))) - (setq cs-decode (car cs)) - (setq cs-encode (cdr cs)) - (unless cs-decode (setq cs-decode 'undecided)) - (unless cs-encode (setq cs-encode 'undecided)) - (setq cs-encode (tramp-coding-system-change-eol-conversion - cs-encode 'unix)) - (when (search-forward "\r" nil t) - (setq cs-decode (tramp-coding-system-change-eol-conversion - cs-decode 'dos))) - (set-buffer-process-coding-system cs-decode cs-encode)) - ;; Look for ^M and do something useful if found. - (when (search-forward "\r" nil t) - ;; We have found a ^M but cannot frob the process coding system - ;; because we're running on a non-MULE Emacs. Let's try - ;; stty, instead. - (erase-buffer) - (tramp-message 9 "Trying `stty -onlcr'") - (tramp-send-command-internal multi-method method user host - "stty -onlcr")))) - (erase-buffer) - (tramp-message - 9 "Waiting 30s for `HISTFILE=$HOME/.tramp_history; HISTSIZE=1; export HISTFILE; export HISTSIZE'") - (tramp-send-command-internal - multi-method method user host - "HISTFILE=$HOME/.tramp_history; HISTSIZE=1; export HISTFILE; export HISTSIZE") - (erase-buffer) - (tramp-message 9 "Waiting 30s for `set +o vi +o emacs'") - (tramp-send-command-internal multi-method method user host - "set +o vi +o emacs") - (erase-buffer) - (tramp-message 9 "Waiting 30s for `unset MAIL MAILCHECK MAILPATH'") - (tramp-send-command-internal - multi-method method user host - "unset MAIL MAILCHECK MAILPATH 1>/dev/null 2>/dev/null") - (erase-buffer) - (tramp-message 9 "Waiting 30s for `unset CDPATH'") - (tramp-send-command-internal multi-method method user host - "unset CDPATH") - (erase-buffer) - (tramp-message 9 "Setting shell prompt") - ;; Douglas Gray Stephens says that we must - ;; use "\n" here, not tramp-rsh-end-of-line. We also manually frob - ;; the last time we sent a command, to avoid tramp-send-command to send - ;; "echo are you awake". - (setq tramp-last-cmd-time (current-time)) - (tramp-send-command - multi-method method user host - (format "PS1='%s%s%s'; PS2=''; PS3=''" - tramp-rsh-end-of-line - tramp-end-of-output - tramp-rsh-end-of-line)) - (tramp-wait-for-output)) - -(defun tramp-post-connection (multi-method method user host) - "Prepare a remote shell before being able to work on it. -METHOD, USER and HOST specify the connection. -Among other things, this finds a shell which groks tilde expansion, -tries to find an `ls' command which groks the `-n' option, sets the -locale to C and sets up the remote shell search path." + (with-connection-property proc "chunksize" + (cond + ((and (integerp tramp-chunksize) (> tramp-chunksize 0)) + tramp-chunksize) + (t + (tramp-message + vec 5 "Checking remote host type for `send-process-string' bug") + (if (string-match + "^FreeBSD" + (with-connection-property vec "uname" + (tramp-send-command-and-read vec "echo \\\"`uname -sr`\\\""))) + 500 0)))) + ;; Set remote PATH variable. + (tramp-set-remote-path vec) ;; Search for a good shell before searching for a command which ;; checks if a file exists. This is done because Tramp wants to use ;; "test foo; echo $?" to check if various conditions hold, and @@ -6247,168 +5543,23 @@ ;; the Solaris /bin/sh is a problem. I'm betting that all systems ;; with buggy /bin/sh implementations will have a working bash or ;; ksh. Whee... - (tramp-find-shell multi-method method user host) - ;; Without (sit-for 0.1) at least, my machine will almost always blow - ;; up on 'not numberp /root' - a race that causes the 'echo ~root' - ;; output of (tramp-find-shell) to show up along with the output of - ;; (tramp-find-ls-command) testing. - ;; - ;; I can't work out why this is a problem though. The (tramp-wait-for-output) - ;; call in (tramp-find-shell) *should* make this not happen, I thought. - ;; - ;; After much debugging I couldn't find any problem with the implementation - ;; of that function though. The workaround stays for me at least. :/ - ;; - ;; Daniel Pittman - (sleep-for 1) - (erase-buffer) - (tramp-find-file-exists-command multi-method method user host) - (make-local-variable 'tramp-ls-command) - (setq tramp-ls-command (tramp-find-ls-command multi-method method user host)) - (unless tramp-ls-command - (tramp-message - 1 - "Danger! Couldn't find ls which groks -n. Muddling through anyway") - (setq tramp-ls-command - (tramp-find-executable multi-method method user host - "ls" tramp-remote-path nil))) - (unless tramp-ls-command - (error "Fatal error: Couldn't find remote executable `ls'")) - (tramp-message 5 "Using remote command `%s' for getting directory listings" - tramp-ls-command) - (tramp-send-command multi-method method user host - (concat "tramp_set_exit_status () {" tramp-rsh-end-of-line - "return $1" tramp-rsh-end-of-line - "}")) - (tramp-wait-for-output) - ;; Set remote PATH variable. - (tramp-set-remote-path multi-method method user host "PATH" tramp-remote-path) - ;; Tell remote shell to use standard time format, needed for - ;; parsing `ls -l' output. - (tramp-send-command multi-method method user host - "LC_TIME=C; export LC_TIME; echo huhu") - (tramp-wait-for-output) - (tramp-send-command multi-method method user host - "mesg n; echo huhu") - (tramp-wait-for-output) - (tramp-send-command multi-method method user host - "biff n ; echo huhu") - (tramp-wait-for-output) - ;; Unalias ls(1) to work around issues with those silly people who make it - ;; spit out ANSI escapes or whatever. - (tramp-send-command multi-method method user host - "unalias ls; echo huhu") - (tramp-wait-for-output) - ;; Does `test A -nt B' work? Use abominable `find' construct if it - ;; doesn't. BSD/OS 4.0 wants the parentheses around the command, - ;; for otherwise the shell crashes. - (erase-buffer) - (make-local-variable 'tramp-test-groks-nt) - (tramp-send-command multi-method method user host - "( test / -nt / )") - (tramp-wait-for-output) - (goto-char (point-min)) - (setq tramp-test-groks-nt - (looking-at (format "\n%s\r?\n" (regexp-quote tramp-end-of-output)))) - (unless tramp-test-groks-nt - (tramp-send-command - multi-method method user host - (concat "tramp_test_nt () {" tramp-rsh-end-of-line - "test -n \"`find $1 -prune -newer $2 -print`\"" tramp-rsh-end-of-line - "}"))) - (tramp-wait-for-output) - ;; Send the fallback `uudecode' script. - (erase-buffer) - (tramp-send-string multi-method method user host tramp-uudecode) - (tramp-wait-for-output) - ;; Find a `perl'. - (erase-buffer) - (tramp-set-connection-property "perl-scripts" nil multi-method method user host) - (let ((tramp-remote-perl - (or (tramp-find-executable multi-method method user host - "perl5" tramp-remote-path nil) - (tramp-find-executable multi-method method user host - "perl" tramp-remote-path nil)))) - (when tramp-remote-perl - (tramp-set-connection-property "perl" tramp-remote-perl - multi-method method user host) - (unless (tramp-method-out-of-band-p multi-method method user host) - (tramp-message 5 "Sending the Perl `mime-encode' implementations.") - (tramp-send-string - multi-method method user host - (concat "tramp_encode () {\n" - (format tramp-perl-encode tramp-remote-perl) - " 2>/dev/null" - "\n}")) - (tramp-wait-for-output) - (tramp-send-string - multi-method method user host - (concat "tramp_encode_with_module () {\n" - (format tramp-perl-encode-with-module tramp-remote-perl) - " 2>/dev/null" - "\n}")) - (tramp-wait-for-output) - (tramp-message 5 "Sending the Perl `mime-decode' implementations.") - (tramp-send-string - multi-method method user host - (concat "tramp_decode () {\n" - (format tramp-perl-decode tramp-remote-perl) - " 2>/dev/null" - "\n}")) - (tramp-wait-for-output) - (tramp-send-string - multi-method method user host - (concat "tramp_decode_with_module () {\n" - (format tramp-perl-decode-with-module tramp-remote-perl) - " 2>/dev/null" - "\n}")) - (tramp-wait-for-output)))) - ;; Find ln(1) - (erase-buffer) - (let ((ln (tramp-find-executable multi-method method user host - "ln" tramp-remote-path nil))) - (when ln - (tramp-set-connection-property "ln" ln multi-method method user host))) - ;; Set uid and gid. - (erase-buffer) - (tramp-send-command multi-method method user host "id -u; id -g") - (tramp-wait-for-output) - (goto-char (point-min)) - (tramp-set-connection-property - "uid" (read (current-buffer)) multi-method method user host) - (tramp-set-connection-property - "gid" (read (current-buffer)) multi-method method user host) - ;; Find the right encoding/decoding commands to use. - (erase-buffer) - (unless (tramp-method-out-of-band-p multi-method method user host) - (tramp-find-inline-encoding multi-method method user host)) - ;; If encoding/decoding command are given, test to see if they work. - ;; CCC: Maybe it would be useful to run the encoder both locally and - ;; remotely to see if they produce the same result. - (let ((rem-enc (tramp-get-remote-encoding multi-method method user host)) - (rem-dec (tramp-get-remote-decoding multi-method method user host)) - (magic-string "xyzzy")) - (when (and (or rem-dec rem-enc) (not (and rem-dec rem-enc))) - (tramp-kill-process multi-method method user host) - ;; Improve error message and/or error check. - (error - "Must give both decoding and encoding command in method definition")) - (when (and rem-enc rem-dec) - (tramp-message - 5 - "Checking to see if encoding/decoding commands work on remote host...") + (tramp-find-shell vec) + ;; Disable unexpected output. + (tramp-send-command vec "mesg n; biff n") + ;; Set the environment. + (tramp-message vec 5 "Setting default environment") + (let ((env (copy-sequence tramp-remote-process-environment)) + unset item) + (while env + (setq item (split-string (car env) "=")) + (if (and (stringp (cadr item)) (not (string-equal (cadr item) ""))) + (tramp-send-command + vec (format "%s=%s; export %s" (car item) (cadr item) (car item))) + (push (car item) unset)) + (setq env (cdr env))) + (when unset (tramp-send-command - multi-method method user host - (format "echo %s | %s | %s" - (tramp-shell-quote-argument magic-string) rem-enc rem-dec)) - (tramp-wait-for-output) - (unless (looking-at (regexp-quote magic-string)) - (tramp-kill-process multi-method method user host) - (error "Remote host cannot execute de/encoding commands. See buffer `%s' for details" - (buffer-name))) - (erase-buffer) - (tramp-message - 5 "Checking to see if encoding/decoding commands work on remote host...done")))) + vec (format "unset %s" (mapconcat 'identity unset " ")))))) ;; CCC: We should either implement a Perl version of base64 encoding ;; and decoding. Then we just use that in the last item. The other @@ -6428,38 +5579,22 @@ ;; ;; For Irix, no solution is known yet. -(defvar tramp-coding-commands - '(("mimencode -b" "mimencode -u -b" - base64-encode-region base64-decode-region) - ("mmencode -b" "mmencode -u -b" - base64-encode-region base64-decode-region) - ("recode data..base64" "recode base64..data" - base64-encode-region base64-decode-region) - ("uuencode xxx" "uudecode -o /dev/stdout" - tramp-uuencode-region uudecode-decode-region) - ("uuencode xxx" "uudecode -o -" - tramp-uuencode-region uudecode-decode-region) - ("uuencode xxx" "uudecode -p" - tramp-uuencode-region uudecode-decode-region) - ("uuencode xxx" "tramp_uudecode" - tramp-uuencode-region uudecode-decode-region) - ("tramp_encode_with_module" "tramp_decode_with_module" - base64-encode-region base64-decode-region) - ("tramp_encode" "tramp_decode" - base64-encode-region base64-decode-region)) - "List of coding commands for inline transfer. +(defconst tramp-local-coding-commands + '((b64 base64-encode-region base64-decode-region) + (uu tramp-uuencode-region uudecode-decode-region) + (pack + "perl -e 'binmode STDIN; binmode STDOUT; print pack(q{u*}, join q{}, <>)'" + "perl -e 'binmode STDIN; binmode STDOUT; print unpack(q{u*}, join q{}, <>)'")) + "List of local coding commands for inline transfer. Each item is a list that looks like this: -\(REMOTE-ENCODING REMOTE-DECODING LOCAL-ENCODING LOCAL-DECODING) - -The REMOTE-ENCODING should be a string, giving a command accepting a -plain file on standard input and writing the encoded file to standard -output. The REMOTE-DECODING should also be a string, giving a command -accepting an encoded file on standard input and writing the decoded -file to standard output. - -LOCAL-ENCODING and LOCAL-DECODING can be strings, giving commands, or -symbols, giving functions. If they are strings, then they can contain +\(FORMAT ENCODING DECODING) + +FORMAT is symbol describing the encoding/decoding format. It can be +`b64' for base64 encoding, `uu' for uu encoding, or `pack' for simple packing. + +ENCODING and DECODING can be strings, giving commands, or symbols, +giving functions. If they are strings, then they can contain the \"%s\" format specifier. If that specifier is present, the input filename will be put into the command line at that spot. If the specifier is not present, the input should be read from standard @@ -6469,83 +5604,139 @@ and end of region, and are expected to replace the region contents with the encoded or decoded results, respectively.") -(defun tramp-find-inline-encoding (multi-method method user host) +(defconst tramp-remote-coding-commands + '((b64 "mimencode -b" "mimencode -u -b") + (b64 "mmencode -b" "mmencode -u -b") + (b64 "recode data..base64" "recode base64..data") + (b64 tramp-perl-encode-with-module tramp-perl-decode-with-module) + (b64 tramp-perl-encode tramp-perl-decode) + (uu "uuencode xxx" "uudecode -o /dev/stdout") + (uu "uuencode xxx" "uudecode -o -") + (uu "uuencode xxx" "uudecode -p") + (uu "uuencode xxx" tramp-uudecode) + (pack + "perl -e 'binmode STDIN; binmode STDOUT; print pack(q{u*}, join q{}, <>)'" + "perl -e 'binmode STDIN; binmode STDOUT; print unpack(q{u*}, join q{}, <>)'")) + "List of remote coding commands for inline transfer. +Each item is a list that looks like this: + +\(FORMAT ENCODING DECODING) + +FORMAT is symbol describing the encoding/decoding format. It can be +`b64' for base64 encoding, `uu' for uu encoding, or `pack' for simple packing. + +ENCODING and DECODING can be strings, giving commands, or symbols, +giving variables. If they are strings, then they can contain +the \"%s\" format specifier. If that specifier is present, the input +filename will be put into the command line at that spot. If the +specifier is not present, the input should be read from standard +input. + +If they are variables, this variable is a string containing a Perl +implementation for this functionality. This Perl program will be transferred +to the remote host, and it is avalible as shell function with the same name.") + +(defun tramp-find-inline-encoding (vec) "Find an inline transfer encoding that works. -Goes through the list `tramp-coding-commands'." - (let ((commands tramp-coding-commands) - (magic "xyzzy") - item found) - (while (and commands (null found)) - (setq item (pop commands)) - (catch 'wont-work - (let ((rem-enc (nth 0 item)) - (rem-dec (nth 1 item)) - (loc-enc (nth 2 item)) - (loc-dec (nth 3 item))) - ;; Check if remote encoding and decoding commands can be - ;; called remotely with null input and output. This makes - ;; sure there are no syntax errors and the command is really - ;; found. Note that we do not redirect stdout to /dev/null, - ;; for two reaons: when checking the decoding command, we - ;; actually check the output it gives. And also, when - ;; redirecting "mimencode" output to /dev/null, then as root - ;; it might change the permissions of /dev/null! - (tramp-message-for-buffer - multi-method method user host 9 - "Checking remote encoding command `%s' for sanity" rem-enc) - (unless (zerop (tramp-send-command-and-check - multi-method method user host - (format "%s " output) "")))) - -(defun tramp-maybe-open-connection (multi-method method user host) - "Maybe open a connection to HOST, logging in as USER, using METHOD. + (let ((default-directory (tramp-temporary-file-directory))) + (call-process + tramp-encoding-shell ;program + (when (and input (not (string-match "%s" cmd))) + input) ;input + (if (eq output t) t nil) ;output + nil ;redisplay + tramp-encoding-command-switch + ;; actual shell command + (concat + (if (string-match "%s" cmd) (format cmd input) cmd) + (if (stringp output) (concat "> " output) ""))))) + +(defun tramp-compute-multi-hops (vec) + "Expands VEC according to `tramp-default-proxies-alist'. +Gateway hops are already opened." + (let ((target-alist `(,vec)) + (choices tramp-default-proxies-alist) + item proxy) + + ;; Look for proxy hosts to be passed. + (while choices + (setq item (pop choices) + proxy (nth 2 item)) + (when (and + ;; host + (string-match (or (nth 0 item) "") + (or (tramp-file-name-host (car target-alist)) "")) + ;; user + (string-match (or (nth 1 item) "") + (or (tramp-file-name-user (car target-alist)) ""))) + (if (null proxy) + ;; No more hops needed. + (setq choices nil) + ;; Replace placeholders. + (setq proxy + (format-spec + proxy + `((?u . ,(or (tramp-file-name-user (car target-alist)) "")) + (?h . ,(or (tramp-file-name-host (car target-alist)) ""))))) + (with-parsed-tramp-file-name proxy l + ;; Add the hop. + (add-to-list 'target-alist l) + ;; Start next search. + (setq choices tramp-default-proxies-alist))))) + + ;; Handle gateways. + (when (string-match (format + "^\\(%s\\|%s\\)$" + tramp-gw-tunnel-method tramp-gw-socks-method) + (tramp-file-name-method (car target-alist))) + (let ((gw (pop target-alist)) + (hop (pop target-alist))) + ;; Is the method prepared for gateways? + (unless (tramp-get-method-parameter + (tramp-file-name-method hop) 'tramp-default-port) + (tramp-error + vec 'file-error + "Method `%s' is not supported for gateway access." + (tramp-file-name-method hop))) + ;; Add default port if needed. + (unless + (string-match + tramp-host-with-port-regexp (tramp-file-name-host hop)) + (aset hop 2 + (concat + (tramp-file-name-host hop) tramp-prefix-port-format + (number-to-string + (tramp-get-method-parameter + (tramp-file-name-method hop) 'tramp-default-port))))) + ;; Open the gateway connection. + (add-to-list + 'target-alist + (vector + (tramp-file-name-method hop) (tramp-file-name-user hop) + (tramp-gw-open-connection vec gw hop) nil)) + ;; For the password prompt, we need the correct values. + ;; Therefore, we must remember the gateway vector. But we + ;; cannot do it as connection property, because it shouldn't + ;; be persistent. And we have no started process yet either. + (tramp-set-file-property (car target-alist) "" "gateway" hop))) + + ;; Foreign and out-of-band methods are not supported for multi-hops. + (when (cdr target-alist) + (setq choices target-alist) + (while choices + (setq item (pop choices)) + (when + (or + (not + (tramp-get-method-parameter + (tramp-file-name-method item) 'tramp-login-program)) + (tramp-get-method-parameter + (tramp-file-name-method item) 'tramp-copy-program)) + (tramp-error + vec 'file-error + "Method `%s' is not supported for multi-hops." + (tramp-file-name-method item))))) + + ;; Result. + target-alist)) + +(defun tramp-maybe-open-connection (vec) + "Maybe open a connection VEC. Does not do anything if a connection is already open, but re-opens the connection if a previous connection has died for some reason." - (let ((p (get-buffer-process - (tramp-get-buffer multi-method method user host))) - last-cmd-time) + (let ((p (tramp-get-connection-process vec))) + ;; If too much time has passed since last command was sent, look ;; whether process is still alive. If it isn't, kill it. When ;; using ssh, it can sometimes happen that the remote end has hung @@ -6581,239 +5861,276 @@ ;; tries to send some data to the remote end. So that's why we ;; try to send a command from time to time, then look again ;; whether the process is really alive. - (save-excursion - (set-buffer (tramp-get-buffer multi-method method user host)) - (when (and tramp-last-cmd-time - (> (tramp-time-diff (current-time) tramp-last-cmd-time) 60) - p (processp p) (memq (process-status p) '(run open))) - (tramp-send-command - multi-method method user host "echo are you awake" nil t) - (unless (and (memq (process-status p) '(run open)) - (tramp-wait-for-output 10)) - (delete-process p) - (setq p nil)) - (erase-buffer))) + (when (and (> (tramp-time-diff + (current-time) + (tramp-get-connection-property p "last-cmd-time" '(0 0 0))) + 60) + p (processp p) (memq (process-status p) '(run open))) + (tramp-send-command vec "echo are you awake" t t) + (unless (and (memq (process-status p) '(run open)) + (tramp-wait-for-output p 10)) + (delete-process p) + (setq p nil))) + + ;; New connection must be opened. (unless (and p (processp p) (memq (process-status p) '(run open))) + + ;; We call `tramp-get-buffer' in order to get a debug buffer for + ;; messages from the beginning. + (tramp-get-buffer vec) + (if (zerop (length (tramp-file-name-user vec))) + (tramp-message + vec 3 "Opening connection for %s using %s..." + (tramp-file-name-host vec) + (tramp-file-name-method vec)) + (tramp-message + vec 3 "Opening connection for %s@%s using %s..." + (tramp-file-name-user vec) + (tramp-file-name-host vec) + (tramp-file-name-method vec))) + + ;; Start new process. (when (and p (processp p)) - (delete-process p)) - (let ((process-connection-type tramp-process-connection-type)) - (funcall (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-connection-function) - multi-method method user host))))) - -(defun tramp-send-command - (multi-method method user host command &optional noerase neveropen) - "Send the COMMAND to USER at HOST (logged in using METHOD). -Erases temporary buffer before sending the command (unless NOERASE -is true). -If optional seventh arg NEVEROPEN is non-nil, never try to open the -connection. This is meant to be used from -`tramp-maybe-open-connection' only." - (or neveropen - (tramp-maybe-open-connection multi-method method user host)) - (setq tramp-last-cmd-time (current-time)) - (setq tramp-last-cmd command) - (when tramp-debug-buffer - (save-excursion - (set-buffer (tramp-get-debug-buffer multi-method method user host)) - (goto-char (point-max)) - (tramp-insert-with-face 'bold (format "$ %s\n" command)))) - (let ((proc nil)) - (set-buffer (tramp-get-buffer multi-method method user host)) - (unless noerase (erase-buffer)) - (setq proc (get-buffer-process (current-buffer))) - (process-send-string proc - (concat command tramp-rsh-end-of-line)))) - -(defun tramp-send-command-internal - (multi-method method user host command &optional msg) + (delete-process p)) + (setenv "TERM" tramp-terminal-type) + (setenv "PS1" "$ ") + (let* ((target-alist (tramp-compute-multi-hops vec)) + (process-environment (copy-sequence process-environment)) + (process-connection-type tramp-process-connection-type) + (coding-system-for-read nil) + ;; This must be done in order to avoid our file name handler. + (p (let ((default-directory (tramp-temporary-file-directory))) + (start-process + (or (tramp-get-connection-property vec "process-name" nil) + (tramp-buffer-name vec)) + (tramp-get-connection-buffer vec) + tramp-encoding-shell))) + (first-hop t)) + + (tramp-message + vec 6 "%s" (mapconcat 'identity (process-command p) " ")) + + ;; Check whether process is alive. + (set-process-sentinel p 'tramp-flush-connection-property) + (tramp-set-process-query-on-exit-flag p nil) + (tramp-message vec 3 "Waiting 60s for local shell to come up...") + (tramp-barf-if-no-shell-prompt + p 60 "Couldn't find local shell prompt %s" tramp-encoding-shell) + + ;; Now do all the connections as specified. + (while target-alist + (let* ((hop (car target-alist)) + (l-method (tramp-file-name-method hop)) + (l-user (tramp-file-name-user hop)) + (l-host (tramp-file-name-host hop)) + (l-port nil) + (login-program + (tramp-get-method-parameter l-method 'tramp-login-program)) + (login-args + (tramp-get-method-parameter l-method 'tramp-login-args)) + (gw-args + (tramp-get-method-parameter l-method 'tramp-gw-args)) + (gw (tramp-get-file-property hop "" "gateway" nil)) + (g-method (and gw (tramp-file-name-method gw))) + (g-user (and gw (tramp-file-name-user gw))) + (g-host (and gw (tramp-file-name-host gw))) + (command login-program) + spec) + + ;; Add gateway arguments if necessary. + (when (and gw gw-args) + (setq login-args (append login-args gw-args))) + + ;; Check for port number. Until now, there's no need for handling + ;; like method, user, host. + (when (string-match tramp-host-with-port-regexp l-host) + (setq l-port (match-string 2 l-host) + l-host (match-string 1 l-host))) + + ;; Set variables for computing the prompt for reading password. + ;; They can also be derived from a gatewy. + (setq tramp-current-method (or g-method l-method) + tramp-current-user (or g-user l-user) + tramp-current-host (or g-host l-host)) + + ;; Replace login-args place holders. + (setq + l-host (or l-host "") + l-user (or l-user "") + l-port (or l-port "") + spec `((?h . ,l-host) (?u . ,l-user) (?p . ,l-port) + (?t . ,(tramp-make-tramp-temp-file vec))) + command + (concat + command " " + (mapconcat + '(lambda (x) + (setq x (mapcar '(lambda (y) (format-spec y spec)) x)) + (unless (member "" x) (mapconcat 'identity x " "))) + login-args " ") + ;; String to detect failed connection. Every single word must + ;; be enclosed with '\"'; otherwise it is detected + ;; during connection setup. + ;; Local shell could be a Windows COMSPEC. It doesn't know + ;; the ";" syntax, but we must exit always for `start-process'. + ;; "exec" does not work either. + (if first-hop + " && exit || exit" + "; echo \"Tramp\" \"connection\" \"closed\"; sleep 1")) + ;; We don't reach a Windows shell. Could be initial only. + first-hop nil) + + ;; Send the command. + (tramp-message vec 3 "Sending command `%s'" command) + (tramp-send-command vec command t t) + (tramp-process-actions p vec tramp-actions-before-shell 60) + (tramp-message vec 3 "Found remote shell prompt on `%s'" l-host)) + ;; Next hop. + (setq target-alist (cdr target-alist))) + + ;; Make initial shell settings. + (tramp-open-connection-setup-interactive-shell p vec))))) + +(defun tramp-send-command (vec command &optional neveropen nooutput) + "Send the COMMAND to connection VEC. +Erases temporary buffer before sending the command. If optional +arg NEVEROPEN is non-nil, never try to open the connection. This +is meant to be used from `tramp-maybe-open-connection' only. The +function waits for output unless NOOUTPUT is set." + (unless neveropen (tramp-maybe-open-connection vec)) + (let ((p (tramp-get-connection-process vec))) + (when (tramp-get-connection-property vec "remote-echo" nil) + ;; We mark the command string that it can be erased in the output buffer. + (tramp-set-connection-property p "check-remote-echo" t) + (setq command (format "%s%s%s" tramp-echo-mark command tramp-echo-mark))) + (tramp-message vec 6 "%s" command) + (tramp-send-string vec command) + (unless nooutput (tramp-wait-for-output p)))) + +(defun tramp-send-command-internal (vec command) "Send command to remote host and wait for success. Sends COMMAND, then waits 30 seconds for shell prompt." - (tramp-send-command multi-method method user host command t t) - (when msg - (tramp-message 9 "Waiting 30s for %s..." msg)) - (tramp-barf-if-no-shell-prompt - nil 30 - "Couldn't `%s', see buffer `%s'" command (buffer-name))) - -(defun tramp-wait-for-output (&optional timeout) + (let ((p (tramp-get-connection-process vec))) + (when (tramp-get-connection-property vec "remote-echo" nil) + ;; We mark the command string that it can be erased in the output buffer. + (tramp-set-connection-property p "check-remote-echo" t) + (setq command (format "%s%s%s" tramp-echo-mark command tramp-echo-mark))) + (tramp-message vec 6 "%s" command) + (tramp-send-string vec command) + (tramp-barf-if-no-shell-prompt + p 30 "Couldn't `%s', see buffer `%s'" command (buffer-name)))) + +(defun tramp-wait-for-output (proc &optional timeout) "Wait for output from remote rsh command." - (let ((proc (get-buffer-process (current-buffer))) - (found nil) - (start-time (current-time)) - (start-point (point)) - (end-of-output (concat "^" - (regexp-quote tramp-end-of-output) - "\r?$"))) - ;; Algorithm: get waiting output. See if last line contains - ;; end-of-output sentinel. If not, wait a bit and again get - ;; waiting output. Repeat until timeout expires or end-of-output - ;; sentinel is seen. Will hang if timeout is nil and - ;; end-of-output sentinel never appears. - (save-match-data - (cond (timeout - ;; Work around an XEmacs bug, where the timeout expires - ;; faster than it should. This degenerates into polling - ;; for buggy XEmacsen, but oh, well. - (while (and (not found) - (< (tramp-time-diff (current-time) start-time) - timeout)) - (with-timeout (timeout) - (while (not found) - (tramp-accept-process-output proc 1) - (unless (memq (process-status proc) '(run open)) - (error "Process has died")) - (goto-char (point-max)) - (forward-line -1) - (setq found (looking-at end-of-output)))))) - (t - (while (not found) - (tramp-accept-process-output proc 1) - (unless (memq (process-status proc) '(run open)) - (error "Process has died")) - (goto-char (point-max)) - (forward-line -1) - (setq found (looking-at end-of-output)))))) - ;; At this point, either the timeout has expired or we have found - ;; the end-of-output sentinel. - (when found - (goto-char (point-max)) - (forward-line -2) - (delete-region (point) (point-max))) - ;; If processing echoes, look for it in the first line and delete. - (when tramp-process-echoes - (save-excursion - (goto-char start-point) - (when (looking-at (regexp-quote tramp-last-cmd)) - (delete-region (point) (progn (forward-line 1) (point)))))) - ;; Add output to debug buffer if appropriate. - (when tramp-debug-buffer - (append-to-buffer - (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method - tramp-current-user tramp-current-host) - (point-min) (point-max)) - (when (not found) - (save-excursion - (set-buffer - (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method - tramp-current-user tramp-current-host)) - (goto-char (point-max)) - (insert "[[Remote prompt `" end-of-output "' not found" - (if timeout (format " in %d secs" timeout) "") - "]]")))) - (goto-char (point-min)) - ;; Return value is whether end-of-output sentinel was found. - found)) - -(defun tramp-send-command-and-check (multi-method method user host command - &optional subshell) + (with-current-buffer (process-buffer proc) + (let ((found + (tramp-wait-for-regexp + proc timeout + (format "^%s\r?$" (regexp-quote tramp-end-of-output))))) + (if found + (let (buffer-read-only) + (goto-char (point-max)) + (forward-line -2) + (delete-region (point) (point-max))) + (if timeout + (tramp-error + proc 'file-error + "[[Remote prompt `%s' not found in %d secs]]" + tramp-end-of-output timeout) + (tramp-error + proc 'file-error + "[[Remote prompt `%s' not found]]" tramp-end-of-output))) + ;; Return value is whether end-of-output sentinel was found. + found))) + +(defun tramp-send-command-and-check (vec command &optional subshell) "Run COMMAND and check its exit status. -MULTI-METHOD and METHOD specify how to log in (as USER) to the remote HOST. Sends `echo $?' along with the COMMAND for checking the exit status. If COMMAND is nil, just sends `echo $?'. Returns the exit status found. If the optional argument SUBSHELL is non-nil, the command is executed in a subshell, ie surrounded by parentheses." - (tramp-send-command multi-method method user host - (concat (if subshell "( " "") - command - (if command " 2>/dev/null; " "") - "echo tramp_exit_status $?" - (if subshell " )" " "))) - (tramp-wait-for-output) - (goto-char (point-max)) - (unless (search-backward "tramp_exit_status " nil t) - (error "Couldn't find exit status of `%s'" command)) - (skip-chars-forward "^ ") - (read (current-buffer))) - -(defun tramp-barf-unless-okay (multi-method method user host command subshell - signal fmt &rest args) + (tramp-send-command + vec + (concat (if subshell "( " "") + command + (if command " 2>/dev/null; " "") + "echo tramp_exit_status $?" + (if subshell " )" " "))) + (with-current-buffer (tramp-get-connection-buffer vec) + (goto-char (point-max)) + (unless (re-search-backward "tramp_exit_status [0-9]+" nil t) + (tramp-error + vec 'file-error "Couldn't find exit status of `%s'" command)) + (skip-chars-forward "^ ") + (prog1 + (read (current-buffer)) + (let (buffer-read-only) (delete-region (match-beginning 0) (point-max)))))) + +(defun tramp-barf-unless-okay (vec command fmt &rest args) "Run COMMAND, check exit status, throw error if exit status not okay. Similar to `tramp-send-command-and-check' but accepts two more arguments FMT and ARGS which are passed to `error'." - (unless (zerop (tramp-send-command-and-check - multi-method method user host command subshell)) - ;; CCC: really pop-to-buffer? Maybe it's appropriate to be more - ;; silent. - (pop-to-buffer (current-buffer)) - (funcall 'signal signal (apply 'format fmt args)))) + (unless (zerop (tramp-send-command-and-check vec command)) + (apply 'tramp-error vec 'file-error fmt args))) + +(defun tramp-send-command-and-read (vec command) + "Run COMMAND and return the output, which must be a Lisp expression. +In case there is no valid Lisp expression, it raises an error" + (tramp-barf-unless-okay vec command "`%s' returns with error" command) + (with-current-buffer (tramp-get-connection-buffer vec) + ;; Read the expression. + (goto-char (point-min)) + (condition-case nil + (prog1 (read (current-buffer)) + ;; Error handling. + (when (re-search-forward "\\S-" nil t) (error))) + (error (tramp-error + vec 'file-error + "`%s' does not return a valid Lisp expression: `%s'" + command (buffer-string)))))) ;; It seems that Tru64 Unix does not like it if long strings are sent ;; to it in one go. (This happens when sending the Perl ;; `file-attributes' implementation, for instance.) Therefore, we ;; have this function which waits a bit at each line. -(defun tramp-send-string - (multi-method method user host string) - "Send the STRING to USER at HOST using METHOD. +(defun tramp-send-string (vec string) + "Send the STRING via connection VEC. The STRING is expected to use Unix line-endings, but the lines sent to the remote host use line-endings as defined in the variable -`tramp-rsh-end-of-line'." - (let ((proc (get-buffer-process - (tramp-get-buffer multi-method method user host)))) - (unless proc - (error "Can't send string to remote host -- not logged in")) - ;; debug message - (when tramp-debug-buffer - (save-excursion - (set-buffer (tramp-get-debug-buffer multi-method method user host)) - (goto-char (point-max)) - (tramp-insert-with-face 'bold (format "$ %s\n" string)))) - ;; replace "\n" by `tramp-rsh-end-of-line' - (setq string - (mapconcat 'identity - (split-string string "\n") - tramp-rsh-end-of-line)) - (unless (or (string= string "") - (string-equal (substring string -1) tramp-rsh-end-of-line)) - (setq string (concat string tramp-rsh-end-of-line))) - ;; send the string - (if (and tramp-chunksize (not (zerop tramp-chunksize))) - (let ((pos 0) - (end (length string))) - (while (< pos end) - (tramp-message-for-buffer - multi-method method user host 10 - "Sending chunk from %s to %s" - pos (min (+ pos tramp-chunksize) end)) - (process-send-string - proc (substring string pos (min (+ pos tramp-chunksize) end))) - (setq pos (+ pos tramp-chunksize)) - (sleep-for 0.1))) - (process-send-string proc string)))) - -(defun tramp-send-eof (multi-method method user host) - "Send EOF to the remote end. -METHOD, HOST and USER specify the connection." - (let ((proc (get-buffer-process - (tramp-get-buffer multi-method method user host)))) - (unless proc - (error "Can't send EOF to remote host -- not logged in")) - (process-send-eof proc))) -; (process-send-string proc "\^D"))) - -(defun tramp-kill-process (multi-method method user host) - "Kill the connection process used by Tramp. -MULTI-METHOD, METHOD, USER, and HOST specify the connection." - (let ((proc (get-buffer-process - (tramp-get-buffer multi-method method user host)))) - (kill-process proc))) - -(defun tramp-discard-garbage-erase-buffer (p multi-method method user host) - "Erase buffer, then discard subsequent garbage. -If `tramp-discard-garbage' is nil, just erase buffer." - (if (not tramp-discard-garbage) - (erase-buffer) - (while (prog1 (erase-buffer) (tramp-accept-process-output p 0.25)) - (when tramp-debug-buffer - (save-excursion - (set-buffer (tramp-get-debug-buffer multi-method method user host)) - (goto-char (point-max)) - (tramp-insert-with-face - 'bold (format "Additional characters detected\n"))))))) +`tramp-rsh-end-of-line'. The communication buffer is erased before sending." + (let* ((p (tramp-get-connection-process vec)) + (chunksize (tramp-get-connection-property p "chunksize" nil))) + (unless p + (tramp-error + vec 'file-error "Can't send string to remote host -- not logged in")) + (tramp-set-connection-property p "last-cmd-time" (current-time)) + (tramp-message vec 10 "%s" string) + (with-current-buffer (tramp-get-connection-buffer vec) + ;; Clean up the buffer. We cannot call `erase-buffer' because + ;; narrowing might be in effect. + (let (buffer-read-only) (delete-region (point-min) (point-max))) + ;; replace "\n" by `tramp-rsh-end-of-line' + (setq string + (mapconcat 'identity + (split-string string "\n") + tramp-rsh-end-of-line)) + (unless (or (string= string "") + (string-equal (substring string -1) tramp-rsh-end-of-line)) + (setq string (concat string tramp-rsh-end-of-line))) + ;; send the string + (if (and chunksize (not (zerop chunksize))) + (let ((pos 0) + (end (length string))) + (while (< pos end) + (tramp-message + vec 10 "Sending chunk from %s to %s" + pos (min (+ pos chunksize) end)) + (process-send-string + p (substring string pos (min (+ pos chunksize) end))) + (setq pos (+ pos chunksize)))) + (process-send-string p string))))) (defun tramp-mode-string-to-int (mode-string) "Converts a ten-letter `drwxrwxrwx'-style mode string into mode bits." @@ -6886,27 +6203,70 @@ (t (error "Tenth char `%c' must be one of `xtT-'" other-execute-or-sticky))))))) -(defun tramp-convert-file-attributes (multi-method method user host attr) - "Convert file-attributes ATTR generated by perl script or ls. +(defun tramp-convert-file-attributes (vec attr) + "Convert file-attributes ATTR generated by perl script, stat or ls. Convert file mode bits to string and set virtual device number. Return ATTR." + ;; Convert last access time. + (unless (listp (nth 4 attr)) + (setcar (nthcdr 4 attr) + (list (floor (nth 4 attr) 65536) + (floor (mod (nth 4 attr) 65536))))) + ;; Convert last modification time. + (unless (listp (nth 5 attr)) + (setcar (nthcdr 5 attr) + (list (floor (nth 5 attr) 65536) + (floor (mod (nth 5 attr) 65536))))) + ;; Convert last status change time. + (unless (listp (nth 6 attr)) + (setcar (nthcdr 6 attr) + (list (floor (nth 6 attr) 65536) + (floor (mod (nth 6 attr) 65536))))) ;; Convert file mode bits to string. (unless (stringp (nth 8 attr)) (setcar (nthcdr 8 attr) (tramp-file-mode-from-int (nth 8 attr)))) - ;; Set file's gid change bit. Possible only when id-format is 'integer. - (when (numberp (nth 3 attr)) - (setcar (nthcdr 9 attr) - (not (eql (nth 3 attr) - (tramp-get-remote-gid multi-method method user host))))) + ;; Convert directory indication bit. + (if (string-match "^d" (nth 8 attr)) + (setcar attr t) + (if (and (listp (car attr)) (stringp (caar attr)) + (string-match ".+ -> .\\(.+\\)." (caar attr))) + (setcar attr (match-string 1 (caar attr))) + (setcar attr nil))) + ;; Set file's gid change bit. + (setcar (nthcdr 9 attr) + (if (numberp (nth 3 attr)) + (not (= (nth 3 attr) + (tramp-get-remote-gid vec 'integer))) + (not (string-equal + (nth 3 attr) + (tramp-get-remote-gid vec 'string))))) + ;; Convert inode. + (unless (listp (nth 10 attr)) + (setcar (nthcdr 10 attr) + (list (floor (nth 10 attr) 65536) + (floor (mod (nth 10 attr) 65536))))) ;; Set virtual device number. (setcar (nthcdr 11 attr) - (tramp-get-device multi-method method user host)) + (tramp-get-device vec)) attr) -(defun tramp-get-device (multi-method method user host) +(defun tramp-get-inode (file) + "Returns the virtual inode number. +If it doesn't exist, generate a new one." + (let ((string (directory-file-name file))) + (unless (assoc string tramp-inodes) + (add-to-list 'tramp-inodes + (list string (length tramp-inodes)))) + (nth 1 (assoc string tramp-inodes)))) + +(defun tramp-get-device (vec) "Returns the virtual device number. If it doesn't exist, generate a new one." - (let ((string (tramp-make-tramp-file-name multi-method method user host ""))) + (let ((string (tramp-make-tramp-file-name + (tramp-file-name-method vec) + (tramp-file-name-user vec) + (tramp-file-name-host vec) + ""))) (unless (assoc string tramp-devices) (add-to-list 'tramp-devices (list string (length tramp-devices)))) @@ -6926,7 +6286,6 @@ (setq other (tramp-file-mode-permissions other sticky "t")) (concat type user group other))) - (defun tramp-file-mode-permissions (perm suid suid-text) "Convert a permission bitset into a string. This is used internally by `tramp-file-mode-from-int'." @@ -6939,7 +6298,6 @@ (and suid (upcase suid-text)) ; suid, !execute (and x "x") "-")))) ; !suid - (defun tramp-decimal-to-octal (i) "Return a string consisting of the octal digits of I. Not actually used. Use `(format \"%o\" i)' instead?" @@ -6950,16 +6308,6 @@ (number-to-string (% i 8)))))) -;;(defun tramp-octal-to-decimal (ostr) -;; "Given a string of octal digits, return a decimal number." -;; (cond ((null ostr) 0) -;; ((string= "" ostr) 0) -;; (t (let ((last (aref ostr (1- (length ostr)))) -;; (rest (substring ostr 0 (1- (length ostr))))) -;; (unless (and (>= last ?0) -;; (<= last ?7)) -;; (error "Not an octal digit: %c" last)) -;; (+ (- last ?0) (* 8 (tramp-octal-to-decimal rest))))))) ;; Kudos to Gerd Moellmann for this suggestion. (defun tramp-octal-to-decimal (ostr) "Given a string of octal digits, return a decimal number." @@ -6987,289 +6335,368 @@ ;; internal data structure. Convenience functions for internal ;; data structure. -(defun tramp-file-name-p (obj) - "Check whether TRAMP-FILE-NAME is a Tramp object." - (and (vectorp obj) (= 5 (length obj)))) - -(defun tramp-file-name-multi-method (obj) - "Return MULTI-METHOD component of TRAMP-FILE-NAME." - (and (tramp-file-name-p obj) (aref obj 0))) - -(defun tramp-file-name-method (obj) - "Return METHOD component of TRAMP-FILE-NAME." - (and (tramp-file-name-p obj) (aref obj 1))) - -(defun tramp-file-name-user (obj) - "Return USER component of TRAMP-FILE-NAME." - (and (tramp-file-name-p obj) (aref obj 2))) - -(defun tramp-file-name-host (obj) - "Return HOST component of TRAMP-FILE-NAME." - (and (tramp-file-name-p obj) (aref obj 3))) - -(defun tramp-file-name-localname (obj) - "Return LOCALNAME component of TRAMP-FILE-NAME." - (and (tramp-file-name-p obj) (aref obj 4))) +(defun tramp-file-name-p (vec) + "Check whether VEC is a Tramp object." + (and (vectorp vec) (= 4 (length vec)))) + +(defun tramp-file-name-method (vec) + "Return method component of VEC." + (and (tramp-file-name-p vec) (aref vec 0))) + +(defun tramp-file-name-user (vec) + "Return user component of VEC." + (and (tramp-file-name-p vec) (aref vec 1))) + +(defun tramp-file-name-host (vec) + "Return host component of VEC." + (and (tramp-file-name-p vec) (aref vec 2))) + +(defun tramp-file-name-localname (vec) + "Return localname component of VEC." + (and (tramp-file-name-p vec) (aref vec 3))) + +;; The host part of a Tramp file name vector can be of kind +;; "host#port". Sometimes, we must extract these parts. +(defsubst tramp-file-name-real-host (vec) + "Return the host name of VEC without port." + (let ((host (tramp-file-name-host vec))) + (if (and (stringp host) + (string-match tramp-host-with-port-regexp host)) + (match-string 1 host) + host))) + +(defsubst tramp-file-name-port (vec) + "Return the port number of VEC." + (let ((host (tramp-file-name-host vec))) + (and (stringp host) + (string-match tramp-host-with-port-regexp host) + (string-to-number (match-string 2 host))))) (defun tramp-tramp-file-p (name) "Return t iff NAME is a tramp file." (save-match-data (string-match tramp-file-name-regexp name))) -;; HHH: Changed. Used to assign the return value of (user-login-name) -;; to the `user' part of the structure if a user name was not -;; provided, now it assigns nil. +(defsubst tramp-find-method (method user host) + "Return the right method string to use. +This is METHOD, if non-nil. Otherwise, do a lookup in +`tramp-default-method-alist'." + (or method + (let ((choices tramp-default-method-alist) + lmethod item) + (while choices + (setq item (pop choices)) + (when (and (string-match (or (nth 0 item) "") (or host "")) + (string-match (or (nth 1 item) "") (or user ""))) + (setq lmethod (nth 2 item)) + (setq choices nil))) + lmethod) + tramp-default-method)) + +(defsubst tramp-find-user (method user host) + "Return the right user string to use. +This is USER, if non-nil. Otherwise, do a lookup in +`tramp-default-user-alist'." + (or user + (let ((choices tramp-default-user-alist) + luser item) + (while choices + (setq item (pop choices)) + (when (and (string-match (or (nth 0 item) "") (or method "")) + (string-match (or (nth 1 item) "") (or host ""))) + (setq luser (nth 2 item)) + (setq choices nil))) + luser) + tramp-default-user)) + +(defsubst tramp-find-host (method user host) + "Return the right host string to use. +This is HOST, if non-nil. Otherwise, it is `tramp-default-host'." + (or (and (> (length host) 0) host) + tramp-default-host)) + (defun tramp-dissect-file-name (name) - "Return an `tramp-file-name' structure. + "Return a `tramp-file-name' structure. The structure consists of remote method, remote user, remote host and localname (file name on remote host)." (save-match-data - (let* ((match (string-match (nth 0 tramp-file-name-structure) name)) - (method - ; single-hop - (if match (match-string (nth 1 tramp-file-name-structure) name) - ; maybe multi-hop - (string-match - (format (nth 0 tramp-multi-file-name-structure) - (nth 0 tramp-multi-file-name-hop-structure)) name) - (match-string (nth 1 tramp-multi-file-name-structure) name)))) - (if (and method (member method tramp-multi-methods)) - ;; If it's a multi method, the file name structure contains - ;; arrays of method, user and host. - (tramp-dissect-multi-file-name name) - ;; Normal method. First, find out default method. - (unless match (error "Not a tramp file name: %s" name)) - (let ((user (match-string (nth 2 tramp-file-name-structure) name)) - (host (match-string (nth 3 tramp-file-name-structure) name)) - (localname (match-string (nth 4 tramp-file-name-structure) name))) - (vector nil method (or user nil) host localname)))))) - -(defun tramp-find-default-method (user host) - "Look up the right method to use in `tramp-default-method-alist'." - (let ((choices tramp-default-method-alist) - (method tramp-default-method) - item) - (while choices - (setq item (pop choices)) - (when (and (string-match (or (nth 0 item) "") (or host "")) - (string-match (or (nth 1 item) "") (or user ""))) - (setq method (nth 2 item)) - (setq choices nil))) - method)) - -(defun tramp-find-method (multi-method method user host) - "Return the right method string to use. -This is MULTI-METHOD, if non-nil. Otherwise, it is METHOD, if non-nil. -If both MULTI-METHOD and METHOD are nil, do a lookup in -`tramp-default-method-alist'." - (or multi-method method (tramp-find-default-method user host))) - -;; HHH: Not Changed. Multi method. Will probably not handle the case where -;; a user name is not provided in the "file name" very well. -(defun tramp-dissect-multi-file-name (name) - "Not implemented yet." - (let ((regexp (nth 0 tramp-multi-file-name-structure)) - (method-index (nth 1 tramp-multi-file-name-structure)) - (hops-index (nth 2 tramp-multi-file-name-structure)) - (localname-index (nth 3 tramp-multi-file-name-structure)) - (hop-regexp (nth 0 tramp-multi-file-name-hop-structure)) - (hop-method-index (nth 1 tramp-multi-file-name-hop-structure)) - (hop-user-index (nth 2 tramp-multi-file-name-hop-structure)) - (hop-host-index (nth 3 tramp-multi-file-name-hop-structure)) - method hops len hop-methods hop-users hop-hosts localname) - (unless (string-match (format regexp hop-regexp) name) - (error "Not a multi tramp file name: %s" name)) - (setq method (match-string method-index name)) - (setq hops (match-string hops-index name)) - (setq len (/ (length (match-data t)) 2)) - (when (< localname-index 0) (setq localname-index (+ localname-index len))) - (setq localname (match-string localname-index name)) - (let ((index 0)) - (while (string-match hop-regexp hops index) - (setq index (match-end 0)) - (setq hop-methods - (cons (match-string hop-method-index hops) hop-methods)) - (setq hop-users - (cons (match-string hop-user-index hops) hop-users)) - (setq hop-hosts - (cons (match-string hop-host-index hops) hop-hosts)))) - (vector - method - (apply 'vector (reverse hop-methods)) - (apply 'vector (reverse hop-users)) - (apply 'vector (reverse hop-hosts)) - localname))) - -(defun tramp-make-tramp-file-name (multi-method method user host localname) - "Constructs a tramp file name from METHOD, USER, HOST and LOCALNAME." - (if multi-method - (tramp-make-tramp-multi-file-name multi-method method user host localname) - (format-spec - (concat tramp-prefix-format - (when method (concat "%m" tramp-postfix-single-method-format)) - (when user (concat "%u" tramp-postfix-user-format)) - (when host (concat "%h" tramp-postfix-host-format)) - (when localname (concat "%p"))) - `((?m . ,method) (?u . ,user) (?h . ,host) (?p . ,localname))))) - -;; CCC: Henrik Holm: Not Changed. Multi Method. What should be done -;; with this when USER is nil? -(defun tramp-make-tramp-multi-file-name (multi-method method user host localname) - "Constructs a tramp file name for a multi-hop method." - (unless tramp-make-multi-tramp-file-format - (error "`tramp-make-multi-tramp-file-format' is nil")) - (let* ((prefix-format (nth 0 tramp-make-multi-tramp-file-format)) - (hop-format (nth 1 tramp-make-multi-tramp-file-format)) - (localname-format (nth 2 tramp-make-multi-tramp-file-format)) - (prefix (format-spec prefix-format `((?m . ,multi-method)))) - (hops "") - (localname (format-spec localname-format `((?p . ,localname)))) - (i 0) - (len (length method))) - (while (< i len) - (let ((m (aref method i)) (u (aref user i)) (h (aref host i))) - (setq hops (concat hops (format-spec hop-format - `((?m . ,m) (?u . ,u) (?h . ,h))))) - (setq i (1+ i)))) - (concat prefix hops localname))) - -(defun tramp-make-copy-program-file-name (user host localname) + (let ((match (string-match (nth 0 tramp-file-name-structure) name))) + (unless match (error "Not a tramp file name: %s" name)) + (let ((method (match-string (nth 1 tramp-file-name-structure) name)) + (user (match-string (nth 2 tramp-file-name-structure) name)) + (host (match-string (nth 3 tramp-file-name-structure) name)) + (localname (match-string (nth 4 tramp-file-name-structure) name))) + (vector + (tramp-find-method method user host) + (tramp-find-user method user host) + (tramp-find-host method user host) + localname))))) + +(defun tramp-equal-remote (file1 file2) + "Checks, whether the remote parts of FILE1 and FILE2 are identical. +The check depends on method, user and host name of the files. If +one of the components is missing, the default values are used. +The local file name parts of FILE1 and FILE2 are not taken into +account. + +Example: + + (tramp-equal-remote \"/ssh::/etc\" \"/:/home\") + +would yield `t'. On the other hand, the following check results in nil: + + (tramp-equal-remote \"/sudo::/etc\" \"/su::/etc\")" + (and (stringp (file-remote-p file1)) + (stringp (file-remote-p file2)) + (string-equal (file-remote-p file1) (file-remote-p file2)))) + +(defun tramp-make-tramp-file-name (method user host localname) + "Constructs a Tramp file name from METHOD, USER, HOST and LOCALNAME." + (concat tramp-prefix-format + (when (not (zerop (length method))) + (concat method tramp-postfix-method-format)) + (when (not (zerop (length user))) + (concat user tramp-postfix-user-format)) + (when host host) tramp-postfix-host-format + (when localname localname))) + +(defun tramp-completion-make-tramp-file-name (method user host localname) + "Constructs a Tramp file name from METHOD, USER, HOST and LOCALNAME. +It must not be a complete Tramp file name, but as long as there are +necessary only. This function will be used in file name completion." + (concat tramp-prefix-format + (when (not (zerop (length method))) + (concat method tramp-postfix-method-format)) + (when (not (zerop (length user))) + (concat user tramp-postfix-user-format)) + (when (not (zerop (length host))) + (concat host tramp-postfix-host-format)) + (when localname localname))) + +(defun tramp-make-copy-program-file-name (vec) "Create a file name suitable to be passed to `rcp' and workalikes." - (if user - (format "%s@%s:%s" user host localname) - (format "%s:%s" host localname))) - -(defun tramp-method-out-of-band-p (multi-method method user host) + (let ((user (tramp-file-name-user vec)) + (host (car (split-string + (tramp-file-name-host vec) tramp-prefix-port-regexp))) + (localname (tramp-shell-quote-argument + (tramp-file-name-localname vec)))) + (if (not (zerop (length user))) + (format "%s@%s:%s" user host localname) + (format "%s:%s" host localname)))) + +(defun tramp-method-out-of-band-p (vec) "Return t if this is an out-of-band method, nil otherwise." - (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-copy-program)) + (tramp-get-method-parameter (tramp-file-name-method vec) 'tramp-copy-program)) ;; Variables local to connection. -(defun tramp-get-ls-command (multi-method method user host) - (or - (save-excursion - (tramp-maybe-open-connection multi-method method user host) - (set-buffer (tramp-get-buffer multi-method method user host)) - tramp-ls-command) - (error "Couldn't find remote `ls' command"))) - -(defun tramp-get-test-groks-nt (multi-method method user host) - (save-excursion - (tramp-maybe-open-connection multi-method method user host) - (set-buffer (tramp-get-buffer multi-method method user host)) - tramp-test-groks-nt)) - -(defun tramp-get-file-exists-command (multi-method method user host) - (or - (save-excursion - (tramp-maybe-open-connection multi-method method user host) - (set-buffer (tramp-get-buffer multi-method method user host)) - tramp-file-exists-command) - (error "Couldn't find remote `test -e' command"))) - -(defun tramp-get-remote-perl (multi-method method user host) - (tramp-get-connection-property "perl" nil multi-method method user host)) - -(defun tramp-get-remote-ln (multi-method method user host) - (or - (tramp-get-connection-property "ln" nil multi-method method user host) - (error "Couldn't find remote `ln' command"))) - -(defun tramp-get-remote-uid (multi-method method user host) - (tramp-get-connection-property "uid" nil multi-method method user host)) - -(defun tramp-get-remote-gid (multi-method method user host) - (tramp-get-connection-property "gid" nil multi-method method user host)) - -;; Get a property of a TRAMP connection. -(defun tramp-get-connection-property - (property default multi-method method user host) - "Get the named property for the connection. -If the value is not set for the connection, return `default'" - (tramp-maybe-open-connection multi-method method user host) - (with-current-buffer (tramp-get-buffer multi-method method user host) - (let (error) - (condition-case nil - (symbol-value (intern (concat "tramp-connection-property-" property))) - (error default))))) - -;; Set a property of a TRAMP connection. -(defun tramp-set-connection-property - (property value multi-method method user host) - "Set the named property of a TRAMP connection." - (tramp-maybe-open-connection multi-method method user host) - (with-current-buffer (tramp-get-buffer multi-method method user host) - (set (make-local-variable - (intern (concat "tramp-connection-property-" property))) - value))) +(defun tramp-get-ls-command (vec) + (with-connection-property vec "ls" + (with-current-buffer (tramp-get-buffer vec) + (tramp-message vec 5 "Finding a suitable `ls' command") + (or + (catch 'ls-found + (dolist (cmd '("ls" "gnuls" "gls")) + (let ((dl tramp-remote-path) + result) + (while + (and + dl + (setq result + (tramp-find-executable vec cmd dl t t))) + ;; Check parameter. + (when (zerop (tramp-send-command-and-check + vec (format "%s -lnd /" result))) + (throw 'ls-found result)) + ;; Remove unneeded directories from path. + (while + (and + dl + (not + (string-equal + result (expand-file-name-as-directory cmd (car dl))))) + (setq dl (cdr dl))) + (setq dl (cdr dl)))))) + (tramp-error vec 'file-error "Couldn't find a proper `ls' command"))))) + +(defun tramp-get-test-command (vec) + (with-connection-property vec "test" + (with-current-buffer (tramp-get-buffer vec) + (tramp-message vec 5 "Finding a suitable `test' command") + (if (zerop (tramp-send-command-and-check vec "test 0")) + "test" + (tramp-find-executable vec "test" tramp-remote-path))))) + +(defun tramp-get-test-nt-command (vec) + ;; Does `test A -nt B' work? Use abominable `find' construct if it + ;; doesn't. BSD/OS 4.0 wants the parentheses around the command, + ;; for otherwise the shell crashes. + (with-connection-property vec "test-nt" + (or + (progn + (tramp-send-command + vec (format "( %s / -nt / )" (tramp-get-test-command vec))) + (with-current-buffer (tramp-get-buffer vec) + (goto-char (point-min)) + (when (looking-at + (format "\n%s\r?\n" (regexp-quote tramp-end-of-output))) + (format "%s %%s -nt %%s" (tramp-get-test-command vec))))) + (progn + (tramp-send-command + vec + (format + "tramp_test_nt () {\n%s -n \"`find $1 -prune -newer $2 -print`\"\n}" + (tramp-get-test-command vec))) + "tramp_test_nt %s %s")))) + +(defun tramp-get-file-exists-command (vec) + (with-connection-property vec "file-exists" + (with-current-buffer (tramp-get-buffer vec) + (tramp-message vec 5 "Finding command to check if file exists") + (tramp-find-file-exists-command vec)))) + +(defun tramp-get-remote-ln (vec) + (with-connection-property vec "ln" + (with-current-buffer (tramp-get-buffer vec) + (tramp-message vec 5 "Finding a suitable `ln' command") + (tramp-find-executable vec "ln" tramp-remote-path)))) + +(defun tramp-get-remote-perl (vec) + (with-connection-property vec "perl" + (with-current-buffer (tramp-get-buffer vec) + (tramp-message vec 5 "Finding a suitable `perl' command") + (or (tramp-find-executable vec "perl5" tramp-remote-path) + (tramp-find-executable vec "perl" tramp-remote-path))))) + +(defun tramp-get-remote-stat (vec) + (with-connection-property vec "stat" + (with-current-buffer (tramp-get-buffer vec) + (tramp-message vec 5 "Finding a suitable `stat' command") + (let ((result (tramp-find-executable vec "stat" tramp-remote-path)) + tmp) + ;; Check whether stat(1) returns usable syntax. + (when result + (setq tmp + ;; We don't want to display an error message. + (with-temp-message (or (current-message) "") + (condition-case nil + (tramp-send-command-and-read + vec (format "%s -c '(\"%%N\")' /" result)) + (error nil)))) + (unless (and (listp tmp) (stringp (car tmp)) + (string-match "^./.$" (car tmp))) + (setq result nil))) + result)))) + +(defun tramp-get-remote-id (vec) + (with-connection-property vec "id" + (with-current-buffer (tramp-get-buffer vec) + (tramp-message vec 5 "Finding POSIX `id' command") + (or + (catch 'id-found + (let ((dl tramp-remote-path) + result) + (while + (and + dl + (setq result + (tramp-find-executable vec "id" dl t t))) + ;; Check POSIX parameter. + (when (zerop (tramp-send-command-and-check + vec (format "%s -u" result))) + (throw 'id-found result)) + ;; Remove unneeded directories from path. + (while + (and + dl + (not + (string-equal + result + (concat (file-name-as-directory (car dl)) "id")))) + (setq dl (cdr dl))) + (setq dl (cdr dl))))) + (tramp-error vec 'file-error "Couldn't find a POSIX `id' command"))))) + +(defun tramp-get-remote-uid (vec id-format) + (with-connection-property vec (format "uid-%s" id-format) + (let ((res (tramp-send-command-and-read + vec + (format "%s -u%s %s" + (tramp-get-remote-id vec) + (if (equal id-format 'integer) "" "n") + (if (equal id-format 'integer) + "" "| sed -e s/^/\\\"/ -e s/\$/\\\"/"))))) + ;; The command might not always return a number. + (if (and (equal id-format 'integer) (not (integerp res))) -1 res)))) + +(defun tramp-get-remote-gid (vec id-format) + (with-connection-property vec (format "gid-%s" id-format) + (let ((res (tramp-send-command-and-read + vec + (format "%s -g%s %s" + (tramp-get-remote-id vec) + (if (equal id-format 'integer) "" "n") + (if (equal id-format 'integer) + "" "| sed -e s/^/\\\"/ -e s/\$/\\\"/"))))) + ;; The command might not always return a number. + (if (and (equal id-format 'integer) (not (integerp res))) -1 res)))) ;; Some predefined connection properties. -(defun tramp-set-remote-encoding (multi-method method user host rem-enc) - (tramp-set-connection-property "remote-encoding" rem-enc - multi-method method user host)) -(defun tramp-get-remote-encoding (multi-method method user host) - (tramp-get-connection-property "remote-encoding" nil - multi-method method user host)) - -(defun tramp-set-remote-decoding (multi-method method user host rem-dec) - (tramp-set-connection-property "remote-decoding" rem-dec - multi-method method user host)) -(defun tramp-get-remote-decoding (multi-method method user host) - (tramp-get-connection-property "remote-decoding" nil - multi-method method user host)) - -(defun tramp-set-local-encoding (multi-method method user host loc-enc) - (tramp-set-connection-property "local-encoding" loc-enc - multi-method method user host)) -(defun tramp-get-local-encoding (multi-method method user host) - (tramp-get-connection-property "local-encoding" nil - multi-method method user host)) - -(defun tramp-set-local-decoding (multi-method method user host loc-dec) - (tramp-set-connection-property "local-decoding" loc-dec - multi-method method user host)) -(defun tramp-get-local-decoding (multi-method method user host) - (tramp-get-connection-property "local-decoding" nil - multi-method method user host)) - -(defun tramp-get-method-parameter (multi-method method user host param) +(defun tramp-get-remote-coding (vec prop) + ;; Local coding handles properties like remote coding. So we could + ;; call it without pain. + (let ((ret (tramp-get-local-coding vec prop))) + ;; The connection property might have been cached. So we must send + ;; the script - maybe. + (when (not (stringp ret)) + (let ((name (symbol-name ret))) + (while (string-match (regexp-quote "-") name) + (setq name (replace-match "_" nil t name))) + (tramp-maybe-send-script vec (symbol-value ret) name) + (setq ret name))) + ;; Return the value. + ret)) + +(defun tramp-get-local-coding (vec prop) + (or + (tramp-get-connection-property vec prop nil) + (progn + (tramp-find-inline-encoding vec) + (tramp-get-connection-property vec prop nil)))) + +(defun tramp-get-method-parameter (method param) "Return the method parameter PARAM. -If the `tramp-methods' entry does not exist, use the variable PARAM -as default." - (unless (boundp param) - (error "Non-existing method parameter `%s'" param)) - (let ((entry (assoc param - (assoc (tramp-find-method multi-method method user host) - tramp-methods)))) - (if entry - (cadr entry) - (symbol-value param)))) - +If the `tramp-methods' entry does not exist, return NIL." + (let ((entry (assoc param (assoc method tramp-methods)))) + (when entry (cadr entry)))) ;; Auto saving to a special directory. (defun tramp-exists-file-name-handler (operation &rest args) - (let ((buffer-file-name "/") - (fnha file-name-handler-alist) - (check-file-name-operation operation) - (file-name-handler-alist - (list - (cons "/" - '(lambda (operation &rest args) - "Returns OPERATION if it is the one to be checked" - (if (equal check-file-name-operation operation) - operation - (let ((file-name-handler-alist fnha)) - (apply operation args)))))))) - (eq (apply operation args) operation))) + "Checks whether OPERATION runs a file name handler." + ;; The file name handler is determined on base of either an + ;; argument, `buffer-file-name', or `default-directory'. + (condition-case nil + (let* ((buffer-file-name "/") + (default-directory "/") + (fnha file-name-handler-alist) + (check-file-name-operation operation) + (file-name-handler-alist + (list + (cons "/" + '(lambda (operation &rest args) + "Returns OPERATION if it is the one to be checked." + (if (equal check-file-name-operation operation) + operation + (let ((file-name-handler-alist fnha)) + (apply operation args)))))))) + (equal (apply operation args) operation)) + (error nil))) (unless (tramp-exists-file-name-handler 'make-auto-save-file-name) (defadvice make-auto-save-file-name (around tramp-advice-make-auto-save-file-name () activate) - "Invoke `tramp-handle-make-auto-save-file-name' for tramp files." + "Invoke `tramp-handle-make-auto-save-file-name' for Tramp files." (if (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name))) (setq ad-return-value (tramp-handle-make-auto-save-file-name)) ad-do-it)) @@ -7316,12 +6743,6 @@ (setq alist (cdr alist)))) string)) -(defun tramp-insert-with-face (face string) - "Insert text with a specific face." - (let ((start (point))) - (insert string) - (add-text-properties start (point) (list 'face face)))) - ;; ------------------------------------------------------------ ;; -- Compatibility functions section -- ;; ------------------------------------------------------------ @@ -7345,28 +6766,63 @@ "`temp-directory' is defined -- using /tmp.")) (file-name-as-directory "/tmp")))) -(defun tramp-read-passwd (user host prompt) +(defun tramp-read-passwd (proc &optional prompt) "Read a password from user (compat function). Invokes `password-read' if available, `read-passwd' else." - (if (functionp 'password-read) - (let* ((key (concat (or user (user-login-name)) "@" host)) - (password (apply #'password-read (list prompt key)))) - (apply #'password-cache-add (list key password)) - password) - (read-passwd prompt))) - -(defun tramp-clear-passwd (&optional user host) - "Clear password cache for connection related to current-buffer." + (let* ((key (tramp-make-tramp-file-name + tramp-current-method tramp-current-user + tramp-current-host "")) + (pw-prompt + (or prompt + (with-current-buffer (process-buffer proc) + (tramp-check-for-regexp proc tramp-password-prompt-regexp) + (format "%s for %s " (capitalize (match-string 1)) key))))) + (if (functionp 'password-read) + (let ((password (apply #'password-read (list pw-prompt key)))) + (apply #'password-cache-add (list key password)) + password) + (read-passwd pw-prompt)))) + +(defun tramp-clear-passwd () + "Clear password cache for connection related to current-buffer. +If METHOD, USER or HOST is given, take then for computing the key." (interactive) - (let ((filename (or buffer-file-name list-buffers-directory ""))) - (when (and (functionp 'password-cache-remove) - (or (and user host) (tramp-tramp-file-p filename))) - (let* ((v (when (tramp-tramp-file-p filename) - (tramp-dissect-file-name filename))) - (luser (or user (tramp-file-name-user v) (user-login-name))) - (lhost (or host (tramp-file-name-host v) (system-name))) - (key (concat luser "@" lhost))) - (apply #'password-cache-remove (list key)))))) + (when (functionp 'password-cache-remove) + (apply #'password-cache-remove + (list (tramp-make-tramp-file-name + tramp-current-method + tramp-current-user + tramp-current-host + ""))))) + +;; Snarfed code from time-date.el and parse-time.el + +(defconst tramp-half-a-year '(241 17024) +"Evaluated by \"(days-to-time 183)\".") + +(defconst tramp-parse-time-months + '(("jan" . 1) ("feb" . 2) ("mar" . 3) + ("apr" . 4) ("may" . 5) ("jun" . 6) + ("jul" . 7) ("aug" . 8) ("sep" . 9) + ("oct" . 10) ("nov" . 11) ("dec" . 12)) + "Alist mapping month names to integers.") + +(defun tramp-time-less-p (t1 t2) + "Say whether time value T1 is less than time value T2." + (unless t1 (setq t1 '(0 0))) + (unless t2 (setq t2 '(0 0))) + (or (< (car t1) (car t2)) + (and (= (car t1) (car t2)) + (< (nth 1 t1) (nth 1 t2))))) + +(defun tramp-time-subtract (t1 t2) + "Subtract two time values. +Return the difference in the format of a time value." + (unless t1 (setq t1 '(0 0))) + (unless t2 (setq t2 '(0 0))) + (let ((borrow (< (cadr t1) (cadr t2)))) + (list (- (car t1) (car t2) (if borrow 1 0)) + (- (+ (if borrow 65536 0) (cadr t1)) (cadr t2))))) (defun tramp-time-diff (t1 t2) "Return the difference between the two times, in seconds. @@ -7385,11 +6841,7 @@ (if (< (length t1) 3) (append t1 '(0)) t1) (if (< (length t2) 3) (append t2 '(0)) t2))) (t - ;; snarfed from Emacs 21 time-date.el; combining - ;; time-to-seconds and subtract-time - (let ((time (let ((borrow (< (cadr t1) (cadr t2)))) - (list (- (car t1) (car t2) (if borrow 1 0)) - (- (+ (if borrow 65536 0) (cadr t1)) (cadr t2)))))) + (let ((time (tramp-time-subtract t1 t2))) (+ (* (car time) 65536.0) (cadr time) (/ (or (nth 2 time) 0) 1000000.0)))))) @@ -7428,11 +6880,9 @@ "Specify if query is needed for process when Emacs is exited. If the second argument flag is non-nil, Emacs will query the user before exiting if process is running." - (funcall (if (fboundp 'set-process-query-on-exit-flag) - (symbol-function 'set-process-query-on-exit-flag) - (symbol-function 'process-kill-without-query)) - process flag)) + (funcall (symbol-function 'set-process-query-on-exit-flag) process flag) + (funcall (symbol-function 'process-kill-without-query) process flag))) ;; ------------------------------------------------------------ @@ -7479,29 +6929,6 @@ t t result))) result)))) -;; ;; EFS hooks itself into the file name handling stuff in more places -;; ;; than just `file-name-handler-alist'. The following tells EFS to stay -;; ;; away from tramp.el file names. -;; ;; -;; ;; This is needed because EFS installs (efs-dired-before-readin) into -;; ;; 'dired-before-readin-hook'. This prevents EFS from opening an FTP -;; ;; connection to help it's dired process. Not that I have any real -;; ;; idea *why* this is helpful to dired. -;; ;; -;; ;; Anyway, this advice fixes the problem (with a sledgehammer :) -;; ;; -;; ;; Daniel Pittman -;; ;; -;; ;; CCC: when the other defadvice calls have disappeared, make sure -;; ;; not to call defadvice unless it's necessary. How do we find out whether -;; ;; it is necessary? (featurep 'efs) is surely the wrong way -- -;; ;; EFS might nicht be loaded yet. -;; (defadvice efs-ftp-path (around dont-match-tramp-localname activate protect) -;; "Cause efs-ftp-path to fail when the path is a TRAMP localname." -;; (if (tramp-tramp-file-p (ad-get-arg 0)) -;; nil -;; ad-do-it)) - ;; We currently (sometimes) use "[" and "]" in the filename format. ;; This means that Emacs wants to expand wildcards if ;; `find-file-wildcards' is non-nil, and then barfs because no @@ -7552,10 +6979,6 @@ (format "tramp (%s)" tramp-version) ; package name and version (delq nil `(;; Current state - tramp-ls-command - tramp-test-groks-nt - tramp-file-exists-command - tramp-current-multi-method tramp-current-method tramp-current-user tramp-current-host @@ -7563,6 +6986,11 @@ ;; System defaults tramp-auto-save-directory ; vars to dump tramp-default-method + tramp-default-method-alist + tramp-default-host + tramp-default-proxies-alist + tramp-default-user + tramp-default-user-alist tramp-rsh-end-of-line tramp-default-password-end-of-line tramp-remote-path @@ -7576,24 +7004,21 @@ tramp-temp-name-prefix tramp-file-name-structure tramp-file-name-regexp - tramp-multi-file-name-structure - tramp-multi-file-name-hop-structure - tramp-multi-methods - tramp-multi-connection-function-alist tramp-methods tramp-end-of-output - tramp-coding-commands + tramp-local-coding-commands + tramp-remote-coding-commands tramp-actions-before-shell tramp-actions-copy-out-of-band - tramp-multi-actions tramp-terminal-type ;; Mask non-7bit characters (tramp-shell-prompt-pattern . tramp-reporter-dump-variable) - tramp-chunksize ,(when (boundp 'tramp-backup-directory-alist) 'tramp-backup-directory-alist) ,(when (boundp 'tramp-bkup-backup-directory-info) 'tramp-bkup-backup-directory-info) + ;; Dump cache. + (tramp-cache-data . tramp-reporter-dump-variable) ;; Non-tramp variables of interest ;; Mask non-7bit characters @@ -7616,18 +7041,21 @@ 'tramp-load-report-modules ; pre-hook 'tramp-append-tramp-buffers ; post-hook "\ -Enter your bug report in this message, including as much detail as you -possibly can about the problem, what you did to cause it and what the -local and remote machines are. - -If you can give a simple set of instructions to make this bug happen -reliably, please include those. Thank you for helping kill bugs in -TRAMP. - -Another useful thing to do is to put (setq tramp-debug-buffer t) in -the ~/.emacs file and to repeat the bug. Then, include the contents -of the *tramp/foo* buffer and the *debug tramp/foo* buffer in your bug -report. +Enter your bug report in this message, including as much detail +as you possibly can about the problem, what you did to cause it +and what the local and remote machines are. + +If you can give a simple set of instructions to make this bug +happen reliably, please include those. Thank you for helping +kill bugs in TRAMP. + +Another useful thing to do is to put + + (setq tramp-verbose 8) + +in the ~/.emacs file and to repeat the bug. Then, include the +contents of the *tramp/foo* buffer and the *debug tramp/foo* +buffer in your bug report. --bug report follows this line-- ")))) @@ -7639,29 +7067,32 @@ (val (with-current-buffer reporter-eval-buffer (symbol-value varsym)))) - ;; There are characters to be masked. - (when (and (boundp 'mm-7bit-chars) - (string-match - (concat "[^" (symbol-value 'mm-7bit-chars) "]") val)) - (with-current-buffer reporter-eval-buffer - (set varsym (concat "(base64-decode-string \"" - (base64-encode-string val) - "\")")))) + (if (hash-table-p val) + ;; Pretty print the cache. + (set varsym (read (format "(%s)" (tramp-cache-print val)))) + ;; There are characters to be masked. + (when (and (boundp 'mm-7bit-chars) + (string-match + (concat "[^" (symbol-value 'mm-7bit-chars) "]") val)) + (with-current-buffer reporter-eval-buffer + (set varsym (format "(base64-decode-string \"%s\"" + (base64-encode-string val)))))) ;; Dump variable. (funcall (symbol-function 'reporter-dump-variable) varsym mailbuf) - ;; Remove string quotation. - (forward-line -1) - (when (looking-at - (concat "\\(^.*\\)" "\"" ;; \1 " - "\\((base64-decode-string \\)" "\\\\" ;; \2 \ - "\\(\".*\\)" "\\\\" ;; \3 \ - "\\(\")\\)" "\"$")) ;; \4 " - (replace-match "\\1\\2\\3\\4") - (beginning-of-line) - (insert " ;; variable encoded due to non-printable characters\n")) - (forward-line 1) + (unless (hash-table-p val) + ;; Remove string quotation. + (forward-line -1) + (when (looking-at + (concat "\\(^.*\\)" "\"" ;; \1 " + "\\((base64-decode-string \\)" "\\\\" ;; \2 \ + "\\(\".*\\)" "\\\\" ;; \3 \ + "\\(\")\\)" "\"$")) ;; \4 " + (replace-match "\\1\\2\\3\\4") + (beginning-of-line) + (insert " ;; variable encoded due to non-printable characters\n")) + (forward-line 1)) ;; Reset VARSYM to old value. (with-current-buffer reporter-eval-buffer @@ -7683,8 +7114,39 @@ (funcall (symbol-function 'mml-mode) t))) (defun tramp-append-tramp-buffers () - "Append Tramp buffers into the bug report." - + "Append Tramp buffers and buffer local variables into the bug report." + + (goto-char (point-max)) + + ;; Dump buffer local variables. + (dolist (buffer + (delq nil + (mapcar + '(lambda (b) + (when (string-match "\\*tramp/" (buffer-name b)) b)) + (buffer-list)))) + (let ((reporter-eval-buffer buffer) + (buffer-name (buffer-name buffer)) + (elbuf (get-buffer-create " *tmp-reporter-buffer*"))) + (with-current-buffer elbuf + (emacs-lisp-mode) + (erase-buffer) + (insert "\n(setq\n") + (lisp-indent-line) + (funcall (symbol-function 'reporter-dump-variable) + 'buffer-name (current-buffer)) + (dolist (varsym-or-cons-cell (buffer-local-variables buffer)) + (let ((varsym (or (car-safe varsym-or-cons-cell) + varsym-or-cons-cell))) + (when (string-match "tramp" (symbol-name varsym)) + (funcall + (symbol-function 'reporter-dump-variable) + varsym (current-buffer))))) + (lisp-indent-line) + (insert ")\n")) + (insert-buffer-substring elbuf))) + + ;; Append buffers only when we are in message mode. (when (and (eq major-mode 'message-mode) (boundp 'mml-mode) @@ -7705,24 +7167,26 @@ (setq buffer-read-only nil) (goto-char (point-min)) (while (not (eobp)) - (if (re-search-forward tramp-buf-regexp (tramp-point-at-eol) t) + (if (re-search-forward tramp-buf-regexp (tramp-line-end-position) t) (forward-line 1) (forward-line 0) (let ((start (point))) (forward-line 1) (kill-region start (point))))) (insert " -The buffer(s) above will be appended to this message. If you don't want -to append a buffer because it contains sensible data, or because the buffer -is too large, you should delete the respective buffer. The buffer(s) will -contain user and host names. Passwords will never be included there.") - - (when (and tramp-debug-buffer (> tramp-verbose 9)) +The buffer(s) above will be appended to this message. If you +don't want to append a buffer because it contains sensitive data, +or because the buffer is too large, you should delete the +respective buffer. The buffer(s) will contain user and host +names. Passwords will never be included there.") + + (when (>= tramp-verbose 6) (insert "\n\n") (let ((start (point))) (insert "\ -Please note that you have set `tramp-verbose' to a value greater than 9. -Therefore, the contents of files might be included in the debug buffer(s).") +Please note that you have set `tramp-verbose' to a value of at +least 6. Therefore, the contents of files might be included in +the debug buffer(s).") (add-text-properties start (point) (list 'face 'italic)))) (set-buffer-modified-p nil) @@ -7735,7 +7199,10 @@ (kill-buffer nil) (switch-to-buffer curbuf) (goto-char (point-max)) - (insert "\n\n") + (insert "\n\ +This is a special notion of the `gnus/message' package. If you +use another mail agent (by copying the contents of this buffer) +please ensure that the buffers are attached to your email.\n\n") (dolist (buffer buffer-list) (funcall (symbol-function 'mml-insert-empty-tag) 'part 'type "text/plain" 'encoding "base64" @@ -7766,9 +7233,9 @@ ;; ange-ftp settings must be enabled. (when (functionp 'tramp-ftp-enable-ange-ftp) (funcall (symbol-function 'tramp-ftp-enable-ange-ftp))) - ;; `tramp-util' unloads also `tramp'. - (condition-case nil ;; maybe its not loaded yet. - (unload-feature (if (featurep 'tramp-util) 'tramp-util 'tramp) 'force) + ;; Maybe its not loaded yet. + (condition-case nil + (unload-feature 'tramp 'force) (error nil))) (provide 'tramp) @@ -7776,9 +7243,9 @@ ;; Make sure that we get integration with the VC package. ;; When it is loaded, we need to pull in the integration module. ;; This must come after (provide 'tramp) because tramp-vc.el -;; requires tramp. +;; requires tramp. Not necessary in Emacs 23. (eval-after-load "vc" - '(progn + '(unless (functionp 'start-file-process) (require 'tramp-vc) (add-hook 'tramp-unload-hook '(lambda () @@ -7795,6 +7262,12 @@ ;; Another approach is to read a netrc file like ~/.authinfo ;; from Gnus. ;; * Handle nonlocal exits such as C-g. +;; * But it would probably be better to use with-local-quit at the +;; place where it's actually needed: around any potentially +;; indefinitely blocking piece of code. In this case it would be +;; within Tramp around one of its calls to accept-process-output (or +;; around one of the loops that calls accept-process-output) +;; (Stefann Monnier). ;; * Autodetect if remote `ls' groks the "--dired" switch. ;; * Add fallback for inline encodings. This should be used ;; if the remote end doesn't support mimencode or a similar program. @@ -7808,9 +7281,6 @@ ;; two commands to write a null byte: ;; dd if=/dev/zero bs=1 count=1 ;; echo | tr '\n' '\000' -;; * Separate local `tramp-coding-commands' from remote ones. Connect -;; the two via a format which can be `uu' or `b64'. Then we can search -;; for the right local commands and the right remote commands separately. ;; * Cooperate with PCL-CVS. It uses start-process, which doesn't ;; work for remote files. ;; * Rewrite `tramp-shell-quote-argument' to abstain from using @@ -7830,43 +7300,27 @@ ;; * Don't use globbing for directories with many files, as this is ;; likely to produce long command lines, and some shells choke on ;; long command lines. -;; * Find out about the new auto-save mechanism in Emacs 21 and -;; do the right thing. ;; * `vc-directory' does not work. It never displays any files, even ;; if it does show files when run locally. ;; * Allow correction of passwords, if the remote end allows this. ;; (Mark Hershberger) ;; * How to deal with MULE in `insert-file-contents' and `write-region'? -;; * Do asynchronous `shell-command's. ;; * Grok `append' parameter for `write-region'. ;; * Test remote ksh or bash for tilde expansion in `tramp-find-shell'? ;; * abbreviate-file-name ;; * grok ~ in tramp-remote-path (Henrik Holm ) -;; * Also allow to omit user names when doing multi-hop. Not sure yet -;; what the user names should default to, though. ;; * better error checking. At least whenever we see something ;; strange when doing zerop, we should kill the process and start ;; again. (Greg Stark) -;; * Add caching for filename completion. (Greg Stark) -;; Of course, this has issues with usability (stale cache bites) -;; -- ;; * Provide a local cache of old versions of remote files for the rsync ;; transfer method to use. (Greg Stark) ;; * Remove unneeded parameters from methods. ;; * Invoke rsync once for copying a whole directory hierarchy. ;; (Francesco Potort,Al(B) -;; * Should we set PATH ourselves or should we rely on the remote end -;; to do it? -;; * Make it work for XEmacs 20, which is missing `with-timeout'. ;; * Make it work for different encodings, and for different file name ;; encodings, too. (Daniel Pittman) -;; * Change applicable functions to pass a struct tramp-file-name rather -;; than the individual items MULTI-METHOD, METHOD, USER, HOST, LOCALNAME. -;; * Implement asynchronous shell commands. ;; * Clean up unused *tramp/foo* buffers after a while. (Pete Forman) ;; * Progress reports while copying files. (Michael Kifer) -;; * `Smart' connection method that uses inline for small and out of -;; band for large files. (Michael Kifer) ;; * Don't search for perl5 and perl. Instead, only search for perl and ;; then look if it's the right version (with `perl -v'). ;; * When editing a remote CVS controlled file as a different user, VC @@ -7879,19 +7333,49 @@ ;; about Tramp, it does not do the right thing if the target file ;; name is a Tramp name. ;; * Username and hostname completion. -;; ** If `partial-completion-mode' isn't loaded, "/foo:bla" tries to -;; connect to host "blabla" already if that host is unique. No idea -;; how to suppress. Maybe not an essential problem. ;; ** Try to avoid usage of `last-input-event' in `tramp-completion-mode'. -;; ** Extend `tramp-get-completion-su' for NIS and shadow passwords. ;; ** Unify `tramp-parse-{rhosts,shosts,sconfig,hosts,passwd,netrc}'. ;; Code is nearly identical. -;; ** Decide whiche files to take for searching user/host names depending on -;; operating system (windows-nt) in `tramp-completion-function-alist'. -;; ** Enhance variables for debug. -;; ** Implement "/multi:" completion. -;; ** Add a learning mode for completion. Make results persistent. ;; * Allow out-of-band methods as _last_ multi-hop. +;; * WIBNI if we had a command "trampclient"? If I was editing in +;; some shell with root priviledges, it would be nice if I could +;; just call +;; trampclient filename.c +;; as an editor, and the _current_ shell would connect to an Emacs +;; server and would be used in an existing non-priviledged Emacs +;; session for doing the editing in question. +;; That way, I need not tell Emacs my password again and be afraid +;; that it makes it into core dumps or other ugly stuff (I had Emacs +;; once display a just typed password in the context of a keyboard +;; sequence prompt for a question immediately following in a shell +;; script run within Emacs -- nasty). +;; And if I have some ssh session running to a different computer, +;; having the possibility of passing a local file there to a local +;; Emacs session (in case I can arrange for a connection back) would +;; be nice. +;; Likely the corresponding tramp server should not allow the +;; equivalent of the emacsclient -eval option in order to make this +;; reasonably unproblematic. And maybe trampclient should have some +;; way of passing credentials, like by using an SSL socket or +;; something. (David Kastrup) +;; * Could Tramp reasonably look for a prompt after ^M rather than +;; only after ^J ? (Stefan Monnier) +;; * WIBNI there was an interactive command prompting for tramp +;; method, hostname, username and filename and translates the user +;; input into the correct filename syntax (depending on the Emacs +;; flavor) (Reiner Steib) +;; * Let the user edit the connection properties interactively. +;; Something like `gnus-server-edit-server' in Gnus' *Server* buffer. +;; * Reconnect directly to a compliant shell without first going +;; through the user's default shell. (Pete Forman) +;; * It's just that when I come to Customize `tramp-default-user-alist' +;; I'm presented with a mismatch and raw lisp for a value. It is my +;; understanding that a variable declared with defcustom is a User +;; Option and should not be modified by the code. add-to-list is +;; called in several places. One way to handle that is to have a new +;; ordinary variable that gets its initial value from +;; tramp-default-user-alist and then is added to. (Pete Forman) +;; * Make `tramp-default-user' obsolete. ;; Functions for file-name-handler-alist: ;; diff-latest-backup-file -- in diff.el diff -r a37d5bf6cbb7 -r a66921565bcb lisp/net/trampver.el --- a/lisp/net/trampver.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/net/trampver.el Sun Jul 15 04:47:46 2007 +0000 @@ -11,8 +11,8 @@ ;; GNU Emacs is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -20,22 +20,26 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs; see the file COPYING. If not, see +;; . ;;; Code: ;; In the Tramp CVS repository, the version numer and the bug report address ;; are auto-frobbed from configure.ac, so you should edit that file and run -;; "autoconf && ./configure" to change them. +;; "autoconf && ./configure" to change them. (X)Emacs version check is defined +;; in macro AC_EMACS_INFO of aclocal.m4; should be changed only there. -(defconst tramp-version "2.0.56" +(defconst tramp-version "2.1.10-pre" "This version of Tramp.") (defconst tramp-bug-report-address "tramp-devel@gnu.org" "Email address to send bug reports to.") +;; Check for (X)Emacs version. +(let ((x (if (or (< emacs-major-version 21) (and (featurep 'xemacs) (< emacs-minor-version 4))) (format "Tramp 2.1.10-pre is not fit for %s" (when (string-match "^.*$" (emacs-version)) (match-string 0 (emacs-version)))) "ok"))) + (unless (string-match "\\`ok\\'" x) (error x))) + (provide 'trampver) ;;; arch-tag: 443576ca-f8f1-4bb1-addc-5c70861e93b1 diff -r a37d5bf6cbb7 -r a66921565bcb lisp/pcomplete.el --- a/lisp/pcomplete.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/pcomplete.el Sun Jul 15 04:47:46 2007 +0000 @@ -711,6 +711,7 @@ If no directory information can be extracted from the completed component, `default-directory' is used as the basis for completion." (let* ((name (substitute-env-vars pcomplete-stub)) + (completion-ignore-case pcomplete-ignore-case) (default-directory (expand-file-name (or (file-name-directory name) default-directory))) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/pcvs-info.el --- a/lisp/pcvs-info.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/pcvs-info.el Sun Jul 15 04:47:46 2007 +0000 @@ -85,9 +85,9 @@ (defface cvs-unknown '((((class color) (background dark)) - (:foreground "red")) + (:foreground "red1")) (((class color) (background light)) - (:foreground "red")) + (:foreground "red1")) (t (:slant italic))) "PCL-CVS face used to highlight unknown file status." :group 'pcl-cvs) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/progmodes/compile.el --- a/lisp/progmodes/compile.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/progmodes/compile.el Sun Jul 15 04:47:46 2007 +0000 @@ -87,13 +87,13 @@ ;;;###autoload (defcustom compilation-mode-hook nil - "*List of hook functions run by `compilation-mode' (see `run-mode-hooks')." + "List of hook functions run by `compilation-mode' (see `run-mode-hooks')." :type 'hook :group 'compilation) ;;;###autoload (defcustom compilation-window-height nil - "*Number of lines in a compilation window. If nil, use Emacs default." + "Number of lines in a compilation window. If nil, use Emacs default." :type '(choice (const :tag "Default" nil) integer) :group 'compilation) @@ -442,7 +442,7 @@ "Overlay used to temporarily highlight compilation matches.") (defcustom compilation-error-screen-columns t - "*If non-nil, column numbers in error messages are screen columns. + "If non-nil, column numbers in error messages are screen columns. Otherwise they are interpreted as character positions, with each character occupying one column. The default is to use screen columns, which requires that the compilation @@ -453,21 +453,21 @@ :version "20.4") (defcustom compilation-read-command t - "*Non-nil means \\[compile] reads the compilation command to use. + "Non-nil means \\[compile] reads the compilation command to use. Otherwise, \\[compile] just uses the value of `compile-command'." :type 'boolean :group 'compilation) ;;;###autoload (defcustom compilation-ask-about-save t - "*Non-nil means \\[compile] asks which buffers to save before compiling. + "Non-nil means \\[compile] asks which buffers to save before compiling. Otherwise, it saves all modified buffers without asking." :type 'boolean :group 'compilation) ;;;###autoload (defcustom compilation-search-path '(nil) - "*List of directories to search for source files named in error messages. + "List of directories to search for source files named in error messages. Elements should be directory names, not file names of directories. The value nil as an element means to try the default directory." :type '(repeat (choice (const :tag "Default" nil) @@ -476,7 +476,7 @@ ;;;###autoload (defcustom compile-command "make -k " - "*Last shell command used to do a compilation; default for next compilation. + "Last shell command used to do a compilation; default for next compilation. Sometimes it is useful for files to supply local values for this variable. You might also use mode hooks to specify it in certain modes, like this: @@ -494,7 +494,7 @@ ;;;###autoload (defcustom compilation-disable-input nil - "*If non-nil, send end-of-file as compilation process input. + "If non-nil, send end-of-file as compilation process input. This only affects platforms that support asynchronous processes (see `start-process'); synchronous compilation processes never accept input." :type 'boolean @@ -605,6 +605,14 @@ (defvar compilation-error-list nil) (defvar compilation-old-error-list nil) +(defcustom compilation-auto-jump-to-first-error nil + "If non-nil, automatically jump to the first error after `compile'." + :type 'boolean) + +(defvar compilation-auto-jump-to-next nil + "If non-nil, automatically jump to the next error encountered.") +(make-variable-buffer-local 'compilation-auto-jump-to-next) + (defun compilation-face (type) (or (and (car type) (match-end (car type)) compilation-warning-face) (and (cdr type) (match-end (cdr type)) compilation-info-face) @@ -652,13 +660,18 @@ l2 (setcdr l1 (cons (list ,key) l2))))))) +(defun compilation-auto-jump (buffer pos) + (with-current-buffer buffer + (goto-char pos) + (compile-goto-error))) ;; This function is the central driver, called when font-locking to gather ;; all information needed to later jump to corresponding source code. ;; Return a property list with all meta information on this error location. (defun compilation-error-properties (file line end-line col end-col type fmt) - (unless (< (next-single-property-change (match-beginning 0) 'directory nil (point)) + (unless (< (next-single-property-change (match-beginning 0) + 'directory nil (point)) (point)) (if file (if (functionp file) @@ -710,6 +723,13 @@ (setq type (or (and (car type) (match-end (car type)) 1) (and (cdr type) (match-end (cdr type)) 0) 2))) + + (when (and compilation-auto-jump-to-next + (>= type compilation-skip-threshold)) + (kill-local-variable 'compilation-auto-jump-to-next) + (run-with-timer 0 nil 'compilation-auto-jump + (current-buffer) (match-beginning 0))) + (compilation-internal-error-properties file line end-line col end-col type fmt))) (defun compilation-move-to-column (col screen) @@ -932,7 +952,7 @@ `(,(eval compile-command)))))) (defcustom compilation-scroll-output nil - "*Non-nil to scroll the *compilation* buffer window as output appears. + "Non-nil to scroll the *compilation* buffer window as output appears. Setting it causes the Compilation mode commands to put point at the end of their output window so that the end of the output is always @@ -1026,8 +1046,9 @@ ;; Clear out the compilation buffer. (let ((inhibit-read-only t) (default-directory thisdir)) - ;; Then evaluate a cd command if any, but don't perform it yet, else start-command - ;; would do it again through the shell: (cd "..") AND sh -c "cd ..; make" + ;; Then evaluate a cd command if any, but don't perform it yet, else + ;; start-command would do it again through the shell: (cd "..") AND + ;; sh -c "cd ..; make" (cd (if (string-match "^\\s *cd\\(?:\\s +\\(\\S +?\\)\\)?\\s *[;&\n]" command) (if (match-end 1) (substitute-env-vars (match-string 1 command)) @@ -1043,6 +1064,8 @@ (if highlight-regexp (set (make-local-variable 'compilation-highlight-regexp) highlight-regexp)) + (if compilation-auto-jump-to-first-error + (set (make-local-variable 'compilation-auto-jump-to-next) t)) ;; Output a mode setter, for saving and later reloading this buffer. (insert "-*- mode: " name-of-mode "; default-directory: " (prin1-to-string default-directory) @@ -1075,7 +1098,8 @@ (unless (getenv "EMACS") (list "EMACS=t")) (list "INSIDE_EMACS=t") - (copy-sequence process-environment)))) + (copy-sequence process-environment))) + (start-process (symbol-function 'start-process))) (set (make-local-variable 'compilation-arguments) (list command mode name-function highlight-regexp)) (set (make-local-variable 'revert-buffer-function) @@ -1091,53 +1115,39 @@ (funcall compilation-process-setup-function)) (compilation-set-window-height outwin) ;; Start the compilation. - (if (fboundp 'start-process) - (let ((proc (if (eq mode t) - (get-buffer-process - (with-no-warnings - (comint-exec outbuf (downcase mode-name) - shell-file-name nil `("-c" ,command)))) - (start-process-shell-command (downcase mode-name) - outbuf command)))) - ;; Make the buffer's mode line show process state. - (setq mode-line-process '(":%s")) - (set-process-sentinel proc 'compilation-sentinel) - (set-process-filter proc 'compilation-filter) - (set-marker (process-mark proc) (point) outbuf) - (when compilation-disable-input - (condition-case nil - (process-send-eof proc) - ;; The process may have exited already. - (error nil))) - (setq compilation-in-progress - (cons proc compilation-in-progress))) - ;; No asynchronous processes available. - (message "Executing `%s'..." command) - ;; Fake modeline display as if `start-process' were run. - (setq mode-line-process ":run") - (force-mode-line-update) - (sit-for 0) ; Force redisplay - (let* ((buffer-read-only nil) ; call-process needs to modify outbuf - (status (call-process shell-file-name nil outbuf nil "-c" - command))) - (cond ((numberp status) - (compilation-handle-exit 'exit status - (if (zerop status) - "finished\n" - (format "\ -exited abnormally with code %d\n" - status)))) - ((stringp status) - (compilation-handle-exit 'signal status - (concat status "\n"))) - (t - (compilation-handle-exit 'bizarre status status)))) - ;; Without async subprocesses, the buffer is not yet - ;; fontified, so fontify it now. - (let ((font-lock-verbose nil)) ; shut up font-lock messages - (font-lock-fontify-buffer)) - (set-buffer-modified-p nil) - (message "Executing `%s'...done" command))) + (let ((proc + (if (eq mode t) + ;; comint uses `start-file-process'. + (get-buffer-process + (with-no-warnings + (comint-exec outbuf (downcase mode-name) + shell-file-name nil `("-c" ,command)))) + ;; Redefine temporarily `start-process' in order to + ;; handle remote compilation. + (fset 'start-process + (lambda (name buffer program &rest program-args) + (apply + (if (file-remote-p default-directory) + 'start-file-process + start-process) + name buffer program program-args))) + (unwind-protect + (start-process-shell-command (downcase mode-name) + outbuf command) + ;; Unwindform: Reset original definition of `start-process'. + (fset 'start-process start-process))))) + ;; Make the buffer's mode line show process state. + (setq mode-line-process '(":%s")) + (set-process-sentinel proc 'compilation-sentinel) + (set-process-filter proc 'compilation-filter) + (set-marker (process-mark proc) (point) outbuf) + (when compilation-disable-input + (condition-case nil + (process-send-eof proc) + ;; The process may have exited already. + (error nil))) + (setq compilation-in-progress + (cons proc compilation-in-progress)))) ;; Now finally cd to where the shell started make/grep/... (setq default-directory thisdir)) (if (buffer-local-value 'compilation-scroll-output outbuf) @@ -1258,7 +1268,7 @@ "*If non-nil, skip multiple error messages for the same source location.") (defcustom compilation-skip-threshold 1 - "*Compilation motion commands skip less important messages. + "Compilation motion commands skip less important messages. The value can be either 2 -- skip anything less than error, 1 -- skip anything less than warning or 0 -- don't skip any messages. Note that all messages not positively identified as warning or @@ -1270,7 +1280,7 @@ :version "22.1") (defcustom compilation-skip-visited nil - "*Compilation motion commands skip visited messages if this is t. + "Compilation motion commands skip visited messages if this is t. Visited messages are ones for which the file, line and column have been jumped to from the current content in the current compilation buffer, even if it was from a different message." @@ -1371,6 +1381,8 @@ ;; with the next-error function in simple.el, and it's only ;; coincidentally named similarly to compilation-next-error. (setq next-error-function 'compilation-next-error-function) + (set (make-local-variable 'comint-file-name-prefix) + (or (file-remote-p default-directory) "")) (set (make-local-variable 'font-lock-extra-managed-props) '(directory message help-echo mouse-face debug)) (set (make-local-variable 'compilation-locs) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/progmodes/gdb-ui.el --- a/lisp/progmodes/gdb-ui.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/progmodes/gdb-ui.el Sun Jul 15 04:47:46 2007 +0000 @@ -1765,7 +1765,7 @@ (defface breakpoint-enabled '((t - :foreground "red" + :foreground "red1" :weight bold)) "Face for enabled breakpoint icon in fringe." :group 'gud) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/progmodes/gud.el --- a/lisp/progmodes/gud.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/progmodes/gud.el Sun Jul 15 04:47:46 2007 +0000 @@ -237,7 +237,7 @@ ([menu-bar run] menu-item ,(propertize "run" 'face 'font-lock-doc-face) gud-run :visible (memq gud-minor-mode '(gdbmi gdb dbx jdb))) - ([menu-bar go] menu-item + ([menu-bar go] menu-item ,(propertize " go " 'face 'font-lock-doc-face) gud-go :visible (and (not gud-running) (eq gud-minor-mode 'gdba))) @@ -292,6 +292,11 @@ (defun gud-file-name (f) "Transform a relative file name to an absolute file name. Uses `gud--directories' to find the source files." + ;; When `default-directory' is a remote file name, prepend its + ;; remote part to f, which is the local file name. Fortunately, + ;; `file-remote-p' returns exactly this remote file name part (or + ;; nil otherwise). + (setq f (concat (or (file-remote-p default-directory) "") f)) (if (file-exists-p f) (expand-file-name f) (let ((directories (gud-val 'directories)) (result nil)) @@ -2510,7 +2515,10 @@ (while (and w (not (eq (car w) t))) (setq w (cdr w))) (if w - (setcar w file))) + (setcar w + (if (file-remote-p default-directory) + (setq file (file-name-nondirectory file)) + file)))) (apply 'make-comint (concat "gud" filepart) program nil (if massage-args (funcall massage-args file args) args)) ;; Since comint clobbered the mode, we don't set it until now. @@ -3114,7 +3122,7 @@ 'syntax-table (eval-when-compile (string-to-syntax "> b"))) ;; Make sure that rehighlighting the previous line won't erase our - ;; syntax-table property. + ;; syntax-table property. (put-text-property (1- (match-beginning 0)) (match-end 0) 'font-lock-multiline t) nil))))) @@ -3193,8 +3201,12 @@ (goto-char (point-max))) t) +;; Besides .gdbinit, gdb documents other names to be usable for init +;; files, cross-debuggers can use something like +;; .PROCESSORNAME-gdbinit so that the host and target gdbinit files +;; don't interfere with each other. ;;;###autoload -(add-to-list 'auto-mode-alist '("/\\.gdbinit" . gdb-script-mode)) +(add-to-list 'auto-mode-alist '("/\\.[a-z0-9-]*gdbinit" . gdb-script-mode)) ;;;###autoload (define-derived-mode gdb-script-mode nil "GDB-Script" diff -r a37d5bf6cbb7 -r a66921565bcb lisp/progmodes/python.el --- a/lisp/progmodes/python.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/progmodes/python.el Sun Jul 15 04:47:46 2007 +0000 @@ -996,7 +996,16 @@ (throw 'done t))))))) (setq arg (1- arg))) (zerop arg))) - + +(defvar python-which-func-length-limit 40 + "Non-strict length limit for `python-which-func' output.") + +(defun python-which-func () + (let ((function-name (python-current-defun python-which-func-length-limit))) + (set-text-properties 0 (length function-name) nil function-name) + function-name)) + + ;;;; Imenu. (defvar python-recursing) @@ -1814,22 +1823,30 @@ (1+ (/ (current-indentation) python-indent))) ;; Fixme: Consider top-level assignments, imports, &c. -(defun python-current-defun () +(defun python-current-defun (&optional length-limit) "`add-log-current-defun-function' for Python." (save-excursion ;; Move up the tree of nested `class' and `def' blocks until we ;; get to zero indentation, accumulating the defined names. (let ((start t) - accum) - (while (or start (> (current-indentation) 0)) + (accum) + (length -1)) + (while (and (or start (> (current-indentation) 0)) + (or (null length-limit) + (null (cdr accum)) + (< length length-limit))) (setq start nil) (python-beginning-of-block) (end-of-line) (beginning-of-defun) - (if (looking-at (rx (0+ space) (or "def" "class") (1+ space) - (group (1+ (or word (syntax symbol)))))) - (push (match-string 1) accum))) - (if accum (mapconcat 'identity accum "."))))) + (when (looking-at (rx (0+ space) (or "def" "class") (1+ space) + (group (1+ (or word (syntax symbol)))))) + (push (match-string 1) accum) + (setq length (+ length 1 (length (car accum)))))) + (when accum + (when (and length-limit (> length length-limit)) + (setcar accum "..")) + (mapconcat 'identity accum "."))))) (defun python-mark-block () "Mark the block around point. @@ -2248,6 +2265,7 @@ (set (make-local-variable 'beginning-of-defun-function) 'python-beginning-of-defun) (set (make-local-variable 'end-of-defun-function) 'python-end-of-defun) + (add-hook 'which-func-functions 'python-which-func nil t) (setq imenu-create-index-function #'python-imenu-create-index) (set (make-local-variable 'eldoc-documentation-function) #'python-eldoc-function) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/progmodes/which-func.el --- a/lisp/progmodes/which-func.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/progmodes/which-func.el Sun Jul 15 04:47:46 2007 +0000 @@ -76,8 +76,8 @@ :version "20.3") (defcustom which-func-modes - '(emacs-lisp-mode c-mode c++-mode perl-mode cperl-mode makefile-mode - sh-mode fortran-mode f90-mode ada-mode) + '(emacs-lisp-mode c-mode c++-mode perl-mode cperl-mode python-mode + makefile-mode sh-mode fortran-mode f90-mode ada-mode) "List of major modes for which Which Function mode should be used. For other modes it is disabled. If this is equal to t, then Which Function mode is enabled in any major mode that supports it." diff -r a37d5bf6cbb7 -r a66921565bcb lisp/replace.el --- a/lisp/replace.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/replace.el Sun Jul 15 04:47:46 2007 +0000 @@ -860,7 +860,7 @@ (defface match '((((class color) (min-colors 88) (background light)) - :background "yellow") + :background "yellow1") (((class color) (min-colors 88) (background dark)) :background "RoyalBlue3") (((class color) (min-colors 8) (background light)) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/subr.el --- a/lisp/subr.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/subr.el Sun Jul 15 04:47:46 2007 +0000 @@ -103,7 +103,7 @@ When COND yields non-nil, eval BODY forms sequentially and return value of last one, or nil if there are none. -\(fn COND BODY ...)" +\(fn COND BODY...)" (declare (indent 1) (debug t)) (list 'if cond (cons 'progn body))) @@ -112,7 +112,7 @@ When COND yields nil, eval BODY forms sequentially and return value of last one, or nil if there are none. -\(fn COND BODY ...)" +\(fn COND BODY...)" (declare (indent 1) (debug t)) (cons 'if (cons cond (cons nil body)))) @@ -510,6 +510,7 @@ (if (integerp b) (< a b) t) (if (integerp b) t + ;; string< also accepts symbols. (string< a b)))))) (dolist (p list) (funcall function (car p) (cdr p)))) @@ -1219,7 +1220,8 @@ Execution is delayed if `delay-mode-hooks' is non-nil. If `delay-mode-hooks' is nil, run `after-change-major-mode-hook' after running the mode hooks. -Major mode functions should use this." +Major mode functions should use this instead of `run-hooks' when running their +FOO-mode-hook." (if delay-mode-hooks ;; Delaying case. (dolist (hook hooks) @@ -2484,6 +2486,29 @@ (or (input-pending-p) ,@body)))))) +(defmacro condition-case-no-debug (var bodyform &rest handlers) + "Like `condition-case' except that it does not catch anything when debugging. +More specifically if `debug-on-error' is set, then it does not catch any signal." + (declare (debug condition-case) (indent 2)) + (let ((bodysym (make-symbol "body"))) + `(let ((,bodysym (lambda () ,bodyform))) + (if debug-on-error + (funcall ,bodysym) + (condition-case ,var + (funcall ,bodysym) + ,@handlers))))) + +(defmacro with-demoted-errors (&rest body) + "Run BODY and demote any errors to simple messages. +If `debug-on-error' is non-nil, run BODY without catching its errors. +This is to be used around code which is not expected to signal an error +but which should be robust in the unexpected case that an error is signalled." + (declare (debug t) (indent 0)) + (let ((err (make-symbol "err"))) + `(condition-case-no-debug ,err + (progn ,@body) + (error (message "Error: %s" ,err) nil)))) + (defmacro combine-after-change-calls (&rest body) "Execute BODY, but don't call the after-change functions till the end. If BODY makes changes in the buffer, they are recorded @@ -2518,6 +2543,20 @@ ;;;; Constructing completion tables. +(defun complete-with-action (action table string pred) + "Perform completion ACTION. +STRING is the string to complete. +TABLE is the completion table, which should not be a function. +PRED is a completion predicate. +ACTION can be one of nil, t or `lambda'." + ;; (assert (not (functionp table))) + (funcall + (cond + ((null action) 'try-completion) + ((eq action t) 'all-completions) + (t 'test-completion)) + string table pred)) + (defmacro dynamic-completion-table (fun) "Use function FUN as a dynamic completion table. FUN is called with one argument, the string for which completion is required, @@ -2539,10 +2578,7 @@ (with-current-buffer (let ((,win (minibuffer-selected-window))) (if (window-live-p ,win) (window-buffer ,win) (current-buffer))) - (cond - ((eq ,mode t) (all-completions ,string (,fun ,string) ,predicate)) - ((not ,mode) (try-completion ,string (,fun ,string) ,predicate)) - (t (test-completion ,string (,fun ,string) ,predicate))))))) + (complete-with-action ,mode (,fun ,string) ,string ,predicate))))) (defmacro lazy-completion-table (var fun) ;; We used to have `&rest args' where `args' were evaluated late (at the @@ -2667,6 +2703,18 @@ (looking-at (concat "\\(?:" regexp "\\)\\'"))))) (not (null pos)))) +(defsubst looking-at-p (regexp) + "\ +Same as `looking-at' except this function does not change the match data." + (let ((inhibit-changing-match-data t)) + (looking-at regexp))) + +(defsubst string-match-p (regexp string &optional start) + "\ +Same as `string-match' except this function does not change the match data." + (let ((inhibit-changing-match-data t)) + (string-match regexp string start))) + (defun subregexp-context-p (regexp pos &optional start) "Return non-nil if POS is in a normal subregexp context in REGEXP. A subregexp context is one where a sub-regexp can appear. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/textmodes/org.el --- a/lisp/textmodes/org.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/textmodes/org.el Sun Jul 15 04:47:46 2007 +0000 @@ -5,7 +5,7 @@ ;; Author: Carsten Dominik ;; Keywords: outlines, hypermedia, calendar, wp ;; Homepage: http://www.astro.uva.nl/~dominik/Tools/org/ -;; Version: 5.01 +;; Version: 5.03b ;; ;; This file is part of GNU Emacs. ;; @@ -83,7 +83,7 @@ ;;; Version -(defconst org-version "5.01" +(defconst org-version "5.03b" "The version number of the file org.el.") (defun org-version () (interactive) @@ -489,15 +489,22 @@ :tag "Org Edit Structure" :group 'org-structure) -(defcustom org-special-ctrl-a nil - "Non-nil means `C-a' behaves specially in headlines. + +(defcustom org-special-ctrl-a/e nil + "Non-nil means `C-a' and `C-e' behave specially in headlines. When set, `C-a' will bring back the cursor to the beginning of the headline text, i.e. after the stars and after a possible TODO keyword. When the cursor is already at that position, another `C-a' will bring -it to the beginning of the line." +it to the beginning of the line. +`C-e' will jump to the end of the headline, ignoring the presence of tags +in the headline. A second `C-e' will then jump to the true end of the +line, after any tags." :group 'org-edit-structure :type 'boolean) +(if (fboundp 'defvaralias) + (defvaralias 'org-special-ctrl-a 'org-special-ctrl-a/e)) + (defcustom org-odd-levels-only nil "Non-nil means, skip even levels and only use odd levels for the outline. This has the effect that two stars are being added/taken away in @@ -1763,7 +1770,7 @@ :group 'org-properties :type 'string) -(defcustom org-default-columns-format "%25ITEM %TODO %3PRIORITY %TAGS" +(defcustom org-columns-default-format "%25ITEM %TODO %3PRIORITY %TAGS" "The default column format, if no other format has been defined. This variable can be set on the per-file basis by inserting a line @@ -3244,6 +3251,12 @@ "Face for column display of entry properties." :group 'org-faces) +(when (fboundp 'set-face-attribute) + ;; Make sure that a fixed-width face is used when we have a column table. + (set-face-attribute 'org-column nil + :height (face-attribute 'default :height) + :family (face-attribute 'default :family))) + (defface org-warning ;; font-lock-warning-face (org-compatible-face '((((class color) (min-colors 16) (background light)) (:foreground "Red1" :bold t)) @@ -3402,8 +3415,13 @@ '(org-level-1 org-level-2 org-level-3 org-level-4 org-level-5 org-level-6 org-level-7 org-level-8 )) -(defconst org-n-levels (length org-level-faces)) - + +(defcustom org-n-level-faces (length org-level-faces) + "The number different faces to be used for headlines. +Org-mode defines 8 different headline faces, so this can be at most 8. +If it is less than 8, the level-1 face gets re-used for level N+1 etc." + :type 'number + :group 'org-faces) ;;; Variables for pre-computed regular expressions, all buffer local @@ -3573,7 +3591,7 @@ ((equal key "TAGS") (setq tags (append tags (org-split-string value splitre)))) ((equal key "COLUMNS") - (org-set-local 'org-default-columns-format value)) + (org-set-local 'org-columns-default-format value)) ((equal key "LINK") (when (string-match "^\\(\\S-+\\)[ \t]+\\(.+\\)" value) (push (cons (match-string 1 value) @@ -3678,15 +3696,15 @@ (mapconcat 'regexp-quote org-not-done-keywords "\\|") "\\)\\>") org-todo-line-regexp - (concat "^\\(\\*+\\)[ \t]*\\(?:\\(" + (concat "^\\(\\*+\\)[ \t]+\\(?:\\(" (mapconcat 'regexp-quote org-todo-keywords-1 "\\|") - "\\)\\>\\)? *\\(.*\\)") + "\\)\\>\\)?[ \t]*\\(.*\\)") org-nl-done-regexp - (concat "[\r\n]\\*+[ \t]+" + (concat "\n\\*+[ \t]+" "\\(?:" (mapconcat 'regexp-quote org-done-keywords "\\|") "\\)" "\\>") org-todo-line-tags-regexp - (concat "^\\(\\*+\\)[ \t]*\\(?:\\(" + (concat "^\\(\\*+\\)[ \t]+\\(?:\\(" (mapconcat 'regexp-quote org-todo-keywords-1 "\\|") (org-re "\\)\\>\\)? *\\(.*?\\([ \t]:[[:alnum:]:_@]+:[ \t]*\\)?$\\)")) @@ -3982,7 +4000,7 @@ (org-add-to-invisibility-spec '(org-cwidth)) (when (featurep 'xemacs) (org-set-local 'line-move-ignore-invisible t)) - (setq outline-regexp "\\*+") + (setq outline-regexp "\\*+ ") (setq outline-level 'org-outline-level) (when (and org-ellipsis (stringp org-ellipsis) (fboundp 'set-display-table-slot) (boundp 'buffer-display-table)) @@ -4412,17 +4430,20 @@ (looking-at outline-regexp) (if (match-beginning 1) (+ (org-get-string-indentation (match-string 1)) 1000) - (- (match-end 0) (match-beginning 0))))) + (1- (- (match-end 0) (match-beginning 0)))))) (defvar org-font-lock-keywords nil) +(defconst org-property-re "^[ \t]*\\(:\\([a-zA-Z_0-9]+\\):\\)[ \t]*\\(\\S-.*\\)" + "Regular expression matching a property line.") + (defun org-set-font-lock-defaults () (let* ((em org-fontify-emphasized-text) (lk org-activate-links) (org-font-lock-extra-keywords ;; Headlines (list - '("^\\(\\**\\)\\(\\*\\)\\(.*\\)" (1 (org-get-level-face 1)) + '("^\\(\\**\\)\\(\\* \\)\\(.*\\)" (1 (org-get-level-face 1)) (2 (org-get-level-face 2)) (3 (org-get-level-face 3))) '("^[ \t]*\\(\\(|\\|\\+-[-+]\\).*\\S-\\)" (1 'org-table)) @@ -4436,7 +4457,7 @@ '("^&?%%(.*\\|<%%([^>\n]*?>" (0 'org-sexp-date t)) '(org-hide-wide-columns (0 nil append)) ;; TODO lines - (list (concat "^\\*+[ \t]*" org-not-done-regexp) + (list (concat "^\\*+[ \t]+" org-not-done-regexp) '(1 'org-todo t)) ;; Priorities (list (concat "\\[#[A-Z0-9]\\]") '(0 'org-special-keyword t)) @@ -4452,13 +4473,13 @@ '(org-do-emphasis-faces (0 nil append)) '(org-do-emphasis-faces))) ;; Checkboxes, similar to Frank Ruell's org-checklet.el - '("^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +\\(\\[[ X]\\]\\)" + '("^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +\\(\\[[- X]\\]\\)" 2 'bold prepend) (if org-provide-checkbox-statistics '("\\[\\([0-9]*%\\)\\]\\|\\[\\([0-9]*\\)/\\([0-9]*\\)\\]" (0 (org-get-checkbox-statistics-face) t))) ;; COMMENT - (list (concat "^\\*+[ \t]*\\<\\(" org-comment-string + (list (concat "^\\*+[ \t]+\\<\\(" org-comment-string "\\|" org-quote-string "\\)\\>") '(1 'org-special-keyword t)) '("^#.*" (0 'font-lock-comment-face t)) @@ -4475,14 +4496,18 @@ ;; Table stuff '("^[ \t]*\\(:.*\\)" (1 'org-table t)) '("| *\\(:?=[^|\n]*\\)" (1 'org-formula t)) - '("^[ \t]*| *\\([#!$*_^]\\) *|" (1 'org-formula t)) +; '("^[ \t]*| *\\([#!$*_^/]\\) *|" (1 'org-formula t)) + '("^[ \t]*| *\\([#*]\\) *|" (1 'org-formula t)) + '("^[ \t]*|\\( *\\([$!_^/]\\) *|.*\\)|" (1 'org-formula t)) ;; Drawers - (list org-drawer-regexp '(0 'org-drawer t)) - (list "^[ \t]*:END:" '(0 'org-drawer t)) +; (list org-drawer-regexp '(0 'org-drawer t)) +; (list "^[ \t]*:END:" '(0 'org-drawer t)) + (list org-drawer-regexp '(0 'org-special-keyword t)) + (list "^[ \t]*:END:" '(0 'org-special-keyword t)) ;; Properties - '("^[ \t]*\\(:[a-zA-Z0-9]+:\\)[ \t]*\\(\\S-.*\\)" - (1 'org-special-keyword t) (2 'org-property-value t)) -;FIXME (1 'org-tag t) (2 'org-property-value t)) + (list org-property-re + '(1 'org-special-keyword t) + '(3 'org-property-value t)) (if org-format-transports-properties-p '("| *\\(<[0-9]+>\\) *" (1 'org-formula t))) '("^\\*+ \\(.*:ARCHIVE:.*\\)" (1 'org-archived prepend)) @@ -4499,9 +4524,9 @@ (defvar org-f nil) (defun org-get-level-face (n) "Get the right face for match N in font-lock matching of healdines." - (setq org-l (- (match-end 2) (match-beginning 1))) + (setq org-l (- (match-end 2) (match-beginning 1) 1)) (if org-odd-levels-only (setq org-l (1+ (/ org-l 2)))) - (setq org-f (nth (% (1- org-l) org-n-levels) org-level-faces)) + (setq org-f (nth (% (1- org-l) org-n-level-faces) org-level-faces)) (cond ((eq n 1) (if org-hide-leading-stars 'org-hide org-f)) ((eq n 2) org-f) @@ -4559,7 +4584,7 @@ (interactive "P") (let* ((outline-regexp (if (and (org-mode-p) org-cycle-include-plain-lists) - "\\(?:\\*+\\|\\([ \t]*\\)\\([-+*]\\|[0-9]+[.)]\\) \\)" + "\\(?:\\*+ \\|\\([ \t]*\\)\\([-+*]\\|[0-9]+[.)]\\) \\)" outline-regexp)) (bob-special (and org-cycle-global-at-bob (bobp) (not (looking-at outline-regexp)))) @@ -5175,8 +5200,8 @@ in the region." (org-back-to-heading t) (let* ((level (save-match-data (funcall outline-level))) - (up-head (make-string (org-get-legal-level level -1) ?*)) - (diff (abs (- level (length up-head))))) + (up-head (concat (make-string (org-get-legal-level level -1) ?*) " ")) + (diff (abs (- level (length up-head) -1)))) (if (= level 1) (error "Cannot promote to level 0. UNDO to recover if necessary")) (replace-match up-head nil t) ;; Fixup tag positioning @@ -5189,8 +5214,8 @@ in the region." (org-back-to-heading t) (let* ((level (save-match-data (funcall outline-level))) - (down-head (make-string (org-get-legal-level level 1) ?*)) - (diff (abs (- level (length down-head))))) + (down-head (concat (make-string (org-get-legal-level level 1) ?*) " ")) + (diff (abs (- level (length down-head) -1)))) (replace-match down-head nil t) ;; Fixup tag positioning (and org-auto-align-tags (org-set-tags nil t)) @@ -5251,8 +5276,8 @@ (let ((org-odd-levels-only nil) n) (save-excursion (goto-char (point-min)) - (while (re-search-forward "^\\*\\*+" nil t) - (setq n (1- (length (match-string 0)))) + (while (re-search-forward "^\\*\\*+ " nil t) + (setq n (- (length (match-string 0)) 2)) (while (>= (setq n (1- n)) 0) (org-demote)) (end-of-line 1)))))) @@ -5266,15 +5291,15 @@ (interactive) (goto-char (point-min)) ;; First check if there are no even levels - (when (re-search-forward "^\\(\\*\\*\\)+[^*]" nil t) + (when (re-search-forward "^\\(\\*\\*\\)+ " nil t) (org-show-context t) (error "Not all levels are odd in this file. Conversion not possible.")) (when (yes-or-no-p "Are you sure you want to globally change levels to odd-even? ") (let ((org-odd-levels-only nil) n) (save-excursion (goto-char (point-min)) - (while (re-search-forward "^\\*\\*+" nil t) - (setq n (/ (length (match-string 0)) 2)) + (while (re-search-forward "^\\*\\*+ " nil t) + (setq n (/ (length (1- (match-string 0))) 2)) (while (>= (setq n (1- n)) 0) (org-promote)) (end-of-line 1)))))) @@ -5399,7 +5424,7 @@ (^re_ (concat "\\(" outline-regexp "\\)[ \t]*")) (old-level (if (string-match ^re txt) - (- (match-end 0) (match-beginning 0)) + (- (match-end 0) (match-beginning 0) 1) -1)) (force-level (cond (level (prefix-numeric-value level)) ((string-match @@ -5693,7 +5718,7 @@ (save-excursion (goto-char (match-end 0)) (skip-chars-forward " \t") - (looking-at "\\[[ X]\\]")))) + (looking-at "\\[[- X]\\]")))) (defun org-toggle-checkbox (&optional arg) "Toggle the checkbox in the current line." @@ -5707,7 +5732,11 @@ (setq beg (point) end (save-excursion (outline-next-heading) (point)))) ((org-at-item-checkbox-p) (save-excursion - (replace-match (if (equal (match-string 0) "[ ]") "[X]" "[ ]") t t)) + (replace-match + (cond (arg "[-]") + ((member (match-string 0) '("[ ]" "[-]")) "[X]") + (t "[ ]")) + t t)) (throw 'exit t)) (t (error "Not at a checkbox or heading, and no active region"))) (save-excursion @@ -5741,7 +5770,7 @@ (end (move-marker (make-marker) (progn (outline-next-heading) (point)))) (re "\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*/[0-9]*\\]\\)") - (re-box "^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +\\(\\[[ X]\\]\\)") + (re-box "^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +\\(\\[[- X]\\]\\)") b1 e1 f1 c-on c-off lim (cstat 0)) (when all (goto-char (point-min)) @@ -5761,7 +5790,7 @@ (goto-char e1) (when lim (while (re-search-forward re-box lim t) - (if (equal (match-string 2) "[ ]") + (if (member (match-string 2) '("[ ]" "[-]")) (setq c-off (1+ c-off)) (setq c-on (1+ c-on)))) (delete-region b1 e1) @@ -6285,6 +6314,8 @@ '([(meta shift down)] org-shiftmetadown) '([(meta shift left)] org-shiftmetaleft) '([(meta shift right)] org-shiftmetaright) + '([(shift up)] org-shiftup) + '([(shift down)] org-shiftdown) '("\M-q" fill-paragraph) '("\C-c^" org-sort) '("\C-c-" org-cycle-list-bullet))) @@ -6466,8 +6497,7 @@ (if heading (progn (if (re-search-forward - (concat "\\(^\\|\r\\)" - (regexp-quote heading) + (concat "^" (regexp-quote heading) (org-re "[ \t]*\\(:[[:alnum:]_@:]+:\\)?[ \t]*\\($\\|\r\\)")) nil t) (goto-char (match-end 0)) @@ -7131,7 +7161,7 @@ (setq n (concat new "|") org-table-may-need-update t))) (or (equal n o) (let (org-table-may-need-update) - (replace-match n)))) + (replace-match n t t)))) (setq org-table-may-need-update t)) (goto-char pos)))))) @@ -7302,7 +7332,6 @@ val) (forward-char 1) "")) - (defun org-table-field-info (arg) "Show info about the current field, and highlight any reference at point." (interactive "P") @@ -7723,7 +7752,7 @@ (setq beg (point-at-bol 1))) (goto-char pos) (if (re-search-forward org-table-hline-regexp tend t) - (setq end (point-at-bol 0)) + (setq end (point-at-bol 1)) (goto-char tend) (setq end (point-at-bol)))) (setq beg (move-marker (make-marker) beg) @@ -8709,7 +8738,7 @@ (goto-line r1) (while (not (looking-at org-table-dataline-regexp)) (beginning-of-line 2)) - (prog1 (org-table-get-field c1) + (prog1 (org-trim (org-table-get-field c1)) (if highlight (org-table-highlight-rectangle (point) (point))))) ;; A range, return a vector ;; First sort the numbers to get a regular ractangle @@ -8729,7 +8758,8 @@ (org-table-highlight-rectangle beg (progn (skip-chars-forward "^|\n") (point)))) ;; return string representation of calc vector - (apply 'append (org-table-copy-region beg end)))))) + (mapcar 'org-trim + (apply 'append (org-table-copy-region beg end))))))) (defun org-table-get-descriptor-line (desc &optional cline bline table) "Analyze descriptor DESC and retrieve the corresponding line number. @@ -9313,10 +9343,10 @@ ((looking-at "[ \t]") (goto-char pos) (call-interactively 'lisp-indent-line)) - ((looking-at "[$@0-9a-zA-Z]+ *= *[^ \t\n']") (goto-char pos)) + ((looking-at "[$&@0-9a-zA-Z]+ *= *[^ \t\n']") (goto-char pos)) ((not (fboundp 'pp-buffer)) (error "Cannot pretty-print. Command `pp-buffer' is not available.")) - ((looking-at "[$@0-9a-zA-Z]+ *= *'(") + ((looking-at "[$&@0-9a-zA-Z]+ *= *'(") (goto-char (- (match-end 0) 2)) (setq beg (point)) (setq ind (make-string (current-column) ?\ )) @@ -10800,9 +10830,10 @@ (setq link (org-completing-read "Link: " (append - (mapcar (lambda (x) (concat (car x) ":")) + (mapcar (lambda (x) (list (concat (car x) ":"))) (append org-link-abbrev-alist-local org-link-abbrev-alist)) - (mapcar (lambda (x) (concat x ":")) org-link-types)) + (mapcar (lambda (x) (list (concat x ":"))) + org-link-types)) nil nil nil 'tmphist (or (car (car org-stored-links))))) @@ -11015,12 +11046,14 @@ (switch-to-buffer-other-window (org-get-buffer-for-internal-link (current-buffer))) (org-mark-ring-push)) - (org-link-search - path - (cond ((equal in-emacs '(4)) 'occur) - ((equal in-emacs '(16)) 'org-occur) - (t nil)) - pos)) + (let ((cmd `(org-link-search + ,path + ,(cond ((equal in-emacs '(4)) 'occur) + ((equal in-emacs '(16)) 'org-occur) + (t nil)) + ,pos))) + (condition-case nil (eval cmd) + (error (progn (widen) (eval cmd)))))) ((string= type "tree-match") (org-occur (concat "\\[" (regexp-quote path) "\\]"))) @@ -11170,7 +11203,7 @@ (let ((case-fold-search t) (s0 (mapconcat 'identity (org-split-string s "[ \t\r\n]+") " ")) (markers (concat "\\(?:" (mapconcat (lambda (x) (regexp-quote (car x))) - (append '((" ") ("\t") ("\n")) + (append '(("") (" ") ("\t") ("\n")) org-emphasis-alist) "\\|") "\\)")) (pos (point)) @@ -11197,10 +11230,10 @@ ;; (grep (concat "grep -n -e '" (match-string 1 s) "' *"))) (t (org-do-occur (match-string 1 s))))) (t - ;; A normal search string + ;; A normal search strings (when (equal (string-to-char s) ?*) ;; Anchor on headlines, post may include tags. - (setq pre "^\\*+[ \t]*\\(?:\\sw+\\)?[ \t]*" + (setq pre "^\\*+[ \t]+\\(?:\\sw+\\)?[ \t]*" post (org-re "[ \t]*\\(?:[ \t]+:[[:alnum:]_@:+]:[ \t]*\\)?$") s (substring s 1))) (remove-text-properties @@ -11707,6 +11740,7 @@ ((or (stringp cmd) (eq cmd 'emacs)) (funcall (cdr (assq 'file org-link-frame-setup)) file) + (widen) (if line (goto-line line) (if search (org-link-search search)))) ((consp cmd) @@ -11793,7 +11827,8 @@ (org-startup-folded nil) org-time-was-given org-end-time-was-given x prompt char time) (setq org-store-link-plist - (append (list :annotation v-a :initial v-i))) + (append (list :annotation v-a :initial v-i) + org-store-link-plist)) (unless tpl (setq tpl "") (message "No template") (ding)) (erase-buffer) (insert (substitute-command-keys @@ -11842,14 +11877,18 @@ (let* ((org-last-tags-completion-table (org-global-tags-completion-table (if (equal char "G") (org-agenda-files) (and file (list file))))) + (org-add-colon-after-tag-completion t) (ins (completing-read (if prompt (concat prompt ": ") "Tags: ") 'org-tags-completion-function nil nil nil 'org-tags-history))) - (insert (concat ":" (mapconcat 'identity - (org-split-string ins (org-re "[^[:alnum:]]+")) - ":") - ":")))) + (setq ins (mapconcat 'identity + (org-split-string ins (org-re "[^[:alnum:]]+")) + ":")) + (when (string-match "\\S-" ins) + (or (equal (char-before) ?:) (insert ":")) + (insert ins) + (or (equal (char-after) ?:) (insert ":"))))) (char (setq org-time-was-given (equal (upcase char) char)) (setq time (org-read-date (equal (upcase char) "U") t nil @@ -11939,7 +11978,7 @@ (let* ((lines (split-string txt "\n")) first) (setq first (car lines) lines (cdr lines)) - (if (string-match "^\\*+" first) + (if (string-match "^\\*+ " first) ;; Is already a headline (setq indent nil) ;; We need to add a headline: Use time and first buffer line @@ -11990,7 +12029,7 @@ (save-restriction (widen) (goto-char (point-min)) - (re-search-forward "^\\*" nil t) + (re-search-forward "^\\*+ " nil t) (beginning-of-line 1) (org-paste-subtree 1 txt))) ((and (org-on-heading-p t) (not current-prefix-arg)) @@ -12197,7 +12236,7 @@ (texp (setq type :tex) org-html-entities) - ((string-match "\\`\\*+[ \t]*\\'" + ((string-match "\\`\\*+[ \t]+\\'" (buffer-substring (point-at-bol) beg)) (setq type :todo) (mapcar 'list org-todo-keywords-1)) @@ -12258,12 +12297,12 @@ (save-excursion (org-back-to-heading) (if (looking-at (concat outline-regexp - "\\( +\\<" org-comment-string "\\>\\)")) + "\\( *\\<" org-comment-string "\\>\\)")) (replace-match "" t t nil 1) (if (looking-at outline-regexp) (progn (goto-char (match-end 0)) - (insert " " org-comment-string)))))) + (insert org-comment-string " ")))))) (defvar org-last-todo-state-is-todo nil "This is non-nil when the last TODO state change led to a TODO state. @@ -12297,7 +12336,7 @@ (interactive "P") (save-excursion (org-back-to-heading) - (if (looking-at outline-regexp) (goto-char (match-end 0))) + (if (looking-at outline-regexp) (goto-char (1- (match-end 0)))) (or (looking-at (concat " +" org-todo-regexp " *")) (looking-at " *")) (let* ((this (match-string 1)) @@ -12490,7 +12529,7 @@ org-todo-keywords-1))) (t (error "Invalid prefix argument: %s" arg))))) (message "%d TODO entries found" - (org-occur (concat "^" outline-regexp " +" kwd-re ))))) + (org-occur (concat "^" outline-regexp " *" kwd-re ))))) (defun org-deadline () "Insert the DEADLINE: string to make a deadline. @@ -13064,6 +13103,29 @@ (defvar org-tags-overlay (org-make-overlay 1 1)) (org-detach-overlay org-tags-overlay) +(defun org-align-tags-here (to-col) + ;; Assumes that this is a headline + (let ((pos (point)) (col (current-column)) tags) + (beginning-of-line 1) + (if (and (looking-at (org-re ".*?\\([ \t]+\\)\\(:[[:alnum:]_@:]+:\\)[ \t]*$")) + (< pos (match-beginning 2))) + (progn + (setq tags (match-string 2)) + (goto-char (match-beginning 1)) + (insert " ") + (delete-region (point) (1+ (match-end 0))) + (backward-char 1) + (move-to-column + (max (1+ (current-column)) + (1+ col) + (if (> to-col 0) + to-col + (- (abs to-col) (length tags)))) + t) + (insert tags) + (move-to-column (min (current-column) col) t)) + (goto-char pos)))) + (defun org-set-tags (&optional arg just-align) "Set the tags for the current headline. With prefix ARG, realign all tags in headings in the current buffer." @@ -13102,30 +13164,31 @@ (while (string-match "[-+&]+" tags) ;; No boolean logic, just a list (setq tags (replace-match ":" t t tags)))) - + (if (string-match "\\`[\t ]*\\'" tags) (setq tags "") (unless (string-match ":$" tags) (setq tags (concat tags ":"))) (unless (string-match "^:" tags) (setq tags (concat ":" tags)))) - + ;; Insert new tags at the correct column (beginning-of-line 1) - (if (re-search-forward - (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$") - (point-at-eol) t) - (progn - (if (equal tags "") - (setq rpl "") - (goto-char (match-beginning 0)) - (setq c0 (current-column) p0 (point) - c1 (max (1+ c0) (if (> org-tags-column 0) - org-tags-column - (- (- org-tags-column) (length tags)))) - rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags))) - (replace-match rpl t t) - (and (not (featurep 'xemacs)) c0 (tabify p0 (point))) - tags) - (error "Tags alignment failed"))))) + (cond + ((and (equal current "") (equal tags ""))) + ((re-search-forward + (concat "\\([ \t]*" (regexp-quote current) "\\)[ \t]*$") + (point-at-eol) t) + (if (equal tags "") + (setq rpl "") + (goto-char (match-beginning 0)) + (setq c0 (current-column) p0 (point) + c1 (max (1+ c0) (if (> org-tags-column 0) + org-tags-column + (- (- org-tags-column) (length tags)))) + rpl (concat (make-string (max 0 (- c1 c0)) ?\ ) tags))) + (replace-match rpl t t) + (and (not (featurep 'xemacs)) c0 (tabify p0 (point))) + tags) + (t (error "Tags alignment failed")))))) (defun org-tags-completion-function (string predicate &optional flag) (let (s1 s2 rtn (ctable org-last-tags-completion-table) @@ -13139,11 +13202,12 @@ ;; try completion (setq rtn (try-completion s2 ctable confirm)) (if (stringp rtn) - (concat s1 s2 (substring rtn (length s2)) - (if (and org-add-colon-after-tag-completion - (assoc rtn ctable)) - ":" ""))) - ) + (setq rtn + (concat s1 s2 (substring rtn (length s2)) + (if (and org-add-colon-after-tag-completion + (assoc rtn ctable)) + ":" "")))) + rtn) ((eq flag t) ;; all-completions (all-completions s2 ctable confirm) @@ -13202,7 +13266,7 @@ (save-excursion (beginning-of-line 1) (if (looking-at - (org-re ".*[ \t]\\(:[[:alnum:]_@:]+:\\)[ \t]*\\(\r\\|$\\)")) + (org-re ".*[ \t]\\(:[[:alnum:]_@:]+:\\)[ \t]*$")) (setq ov-start (match-beginning 1) ov-end (match-end 1) ov-prefix "") @@ -13358,7 +13422,7 @@ (error "Not on a heading")) (save-excursion (beginning-of-line 1) - (if (looking-at (org-re ".*[ \t]\\(:[[:alnum:]_@:]+:\\)[ \t]*\\(\r\\|$\\)")) + (if (looking-at (org-re ".*[ \t]\\(:[[:alnum:]_@:]+:\\)[ \t]*$")) (org-match-string-no-properties 1) ""))) @@ -13393,6 +13457,32 @@ (defconst org-property-end-re "^[ \t]*:END:[ \t]*$" "Regular expression matching the first line of a property drawer.") +(defun org-property-action () + "Do an action on properties." + (interactive) + (let (c prop) + (org-at-property-p) + (setq prop (match-string 2)) + (message "Property Action: [s]et [d]elete [D]delete globally") + (setq c (read-char-exclusive)) + (cond + ((equal c ?s) + (call-interactively 'org-set-property)) + ((equal c ?d) + (call-interactively 'org-delete-property)) + ((equal c ?D) + (call-interactively 'org-delete-property-globally)) + (t (error "No such property action %c" c))))) + +(defun org-at-property-p () + "Is the cursor in a property line?" + ;; FIXME: Does not check if we are actually in the drawer. + ;; FIXME: also returns true on any drawers..... + ;; This is used by C-c C-c for property action. + (save-excursion + (beginning-of-line 1) + (looking-at "^[ \t]*\\(:\\([a-zA-Z_0-9]+\\):\\)[ \t]*\\(.*\\)"))) + (defmacro org-with-point-at (pom &rest body) "Move to buffer and point of point-or-marker POM for the duration of BODY." (declare (indent 1) (debug t)) @@ -13406,7 +13496,7 @@ "Return the (beg . end) range of the body of the property drawer. BEG and END can be beginning and end of subtree, if not given they will be found. -If the drawer does not exist and FORCE is non-nil, greater the drawer." +If the drawer does not exist and FORCE is non-nil, create the drawer." (catch 'exit (save-excursion (let* ((beg (or beg (progn (org-back-to-heading t) (point)))) @@ -13414,18 +13504,14 @@ (goto-char beg) (if (re-search-forward org-property-start-re end t) (setq beg (1+ (match-end 0))) - (or force (throw 'exit nil)) - (beginning-of-line 2) - (while (and (looking-at (concat "[ \t]*" org-keyword-time-regexp)) - (not (equal (match-string 1) org-clock-string))) - (beginning-of-line 2)) - (insert ":PROPERTIES:\n:END:\n") - (beginning-of-line -1) - (org-indent-line-function) - (setq beg (1+ (point-at-eol)) end beg) - (beginning-of-line 2) - (org-indent-line-function) - (throw 'exit (cons beg end))) + (if force + (save-excursion + (org-insert-property-drawer) + (setq end (progn (outline-next-heading) (point)))) + (throw 'exit nil)) + (goto-char beg) + (if (re-search-forward org-property-start-re end t) + (setq beg (1+ (match-end 0))))) (if (re-search-forward org-property-end-re end t) (setq end (match-beginning 0)) (or force (throw 'exit nil)) @@ -13448,10 +13534,11 @@ (org-with-point-at pom (let ((clockstr (substring org-clock-string 0 -1)) (excluded '("TODO" "TAGS" "ALLTAGS" "PRIORITY")) - beg end range props key value) + beg end range props sum-props key value) (save-excursion (when (condition-case nil (org-back-to-heading t) (error nil)) (setq beg (point)) + (setq sum-props (get-text-property (point) 'org-summaries)) (outline-next-heading) (setq end (point)) (when (memq which '(all special)) @@ -13483,18 +13570,20 @@ (when range (goto-char (car range)) (while (re-search-forward - "^[ \t]*:\\([a-zA-Z][a-zA-Z0-9]*\\):[ \t]*\\(\\S-.*\\S-\\)" + "^[ \t]*:\\([a-zA-Z][a-zA-Z_0-9]*\\):[ \t]*\\(\\S-.*\\)?" (cdr range) t) (setq key (org-match-string-no-properties 1) - value (org-match-string-no-properties 2)) + value (org-trim (or (org-match-string-no-properties 2) ""))) (unless (member key excluded) - (push (cons key value) props))))) - (nreverse props)))))) + (push (cons key (or value "")) props))))) + (append sum-props (nreverse props))))))) (defun org-entry-get (pom property &optional inherit) "Get value of PROPERTY for entry at point-or-marker POM. If INHERIT is non-nil and the entry does not have the property, -then also check higher levels of the hierarchy." +then also check higher levels of the hierarchy. +If the property is present but empty, the return value is the empty string. +If the property is not present at all, nil is returned." (org-with-point-at pom (if inherit (org-entry-get-with-inheritance property) @@ -13505,10 +13594,12 @@ (if (and range (goto-char (car range)) (re-search-forward - (concat "^[ \t]*:" property ":[ \t]*\\(.*\\S-\\)") + (concat "^[ \t]*:" property ":[ \t]*\\(.*\\S-\\)?") (cdr range) t)) ;; Found the property, return it. - (org-match-string-no-properties 1))))))) + (if (match-end 1) + (org-match-string-no-properties 1) + ""))))))) (defun org-entry-delete (pom property) "Delete the property PROPERTY from entry at point-or-marker POM." @@ -13521,7 +13612,10 @@ (re-search-forward (concat "^[ \t]*:" property ":[ \t]*\\(.*\\S-\\)") (cdr range) t)) - (delete-region (match-beginning 0) (1+ (point-at-eol)))))))) + (progn + (delete-region (match-beginning 0) (1+ (point-at-eol))) + t) + nil))))) (defvar org-entry-property-inherited-from (make-marker)) @@ -13575,7 +13669,8 @@ (backward-char 1) (org-indent-line-function) (insert ":" property ":")) - (and value (insert " " value))))))) + (and value (insert " " value)) + (org-indent-line-function)))))) (defun org-buffer-property-keys (&optional include-specials) "Get all property keys in the current buffer." @@ -13594,56 +13689,197 @@ (setq rtn (append org-special-properties rtn))) (sort rtn (lambda (a b) (string< (upcase a) (upcase b)))))) -;; FIXME: This should automatically find the right place int he entry. -;; And then org-entry-put should use it. (defun org-insert-property-drawer () - "Insert a property drawer at point." - (interactive) - (beginning-of-line 1) - (insert ":PROPERTIES:\n:END:\n") - (beginning-of-line -1) - (org-indent-line-function) - (beginning-of-line 2) - (org-indent-line-function) - (end-of-line 0)) - -(defvar org-column-overlays nil + "Insert a property drawer into the current entry." + (interactive) + (org-back-to-heading t) + (let ((beg (point)) + (re (concat "^[ \t]*" org-keyword-time-regexp)) + end hiddenp) + (outline-next-heading) + (setq end (point)) + (goto-char beg) + (while (re-search-forward re end t)) + (setq hiddenp (org-invisible-p)) + (end-of-line 1) + (insert "\n:PROPERTIES:\n:END:") + (beginning-of-line 0) + (org-indent-line-function) + (beginning-of-line 2) + (org-indent-line-function) + (beginning-of-line 0) + (if hiddenp + (save-excursion + (org-back-to-heading t) + (hide-entry)) + (org-flag-drawer t)))) + +(defun org-set-property (property value) + "In the current entry, set PROPERTY to VALUE." + (interactive + (let* ((prop (completing-read "Property: " + (mapcar 'list (org-buffer-property-keys)))) + (cur (org-entry-get nil prop)) + (allowed (org-property-get-allowed-values nil prop 'table)) + (val (if allowed + (completing-read "Value: " allowed nil 'req-match) + (read-string + (concat "Value" (if (and cur (string-match "\\S-" cur)) + (concat "[" cur "]") "") + ": ") + "" cur)))) + (list prop (if (equal val "") cur val)))) + (unless (equal (org-entry-get nil property) value) + (org-entry-put nil property value))) + +(defun org-delete-property (property) + "In the current entry, delete PROPERTY." + (interactive + (let* ((prop (completing-read + "Property: " (org-entry-properties nil 'standard)))) + (list prop))) + (message (concat "Property " property + (if (org-entry-delete nil property) + " deleted" + " was not present in the entry")))) + +(defun org-delete-property-globally (property) + "Remove PROPERTY globally, from all entries." + (interactive + (let* ((prop (completing-read + "Globally remove property: " + (mapcar 'list (org-buffer-property-keys))))) + (list prop))) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (let ((cnt 0)) + (while (re-search-forward + (concat "^[ \t]*:" (regexp-quote property) ":.*\n?") + nil t) + (setq cnt (1+ cnt)) + (replace-match "")) + (message "Property \"%s\" removed from %d entries" property cnt))))) + +(defun org-property-get-allowed-values (pom property &optional table) + "Get allowed values for the property PROPERTY. +When TABLE is non-nil, return an alist that can directly be used for +completion." + (let (vals) + (cond + ((equal property "TODO") + (setq vals (org-with-point-at pom + (append org-todo-keywords-1 '(""))))) + ((equal property "PRIORITY") + (let ((n org-lowest-priority)) + (while (>= n org-highest-priority) + (push (char-to-string n) vals) + (setq n (1- n))))) + ((member property org-special-properties)) + (t + (setq vals (org-entry-get pom (concat property "_ALL") 'inherit)) + (when (and vals (string-match "\\S-" vals)) + (setq vals (car (read-from-string (concat "(" vals ")")))) + (setq vals (mapcar (lambda (x) + (cond ((stringp x) x) + ((numberp x) (number-to-string x)) + ((symbolp x) (symbol-name x)) + (t "???"))) + vals))))) + (if table (mapcar 'list vals) vals))) + +;;; Column View + +(defvar org-columns-overlays nil "Holds the list of current column overlays.") -(defvar org-current-columns-fmt nil - "Loval variable, holds the currently active column format.") -(defvar org-current-columns-maxwidths nil +(defvar org-columns-current-fmt nil + "Local variable, holds the currently active column format.") +(defvar org-columns-current-fmt-compiled nil + "Local variable, holds the currently active column format. +This is the compiled version of the format.") +(defvar org-columns-current-maxwidths nil "Loval variable, holds the currently active maximum column widths.") - -(defvar org-column-map (make-sparse-keymap) +(defvar org-columns-begin-marker (make-marker) + "Points to the position where last a column creation command was called.") +(defvar org-columns-top-level-marker (make-marker) + "Points to the position where current columns region starts.") + +(defvar org-columns-map (make-sparse-keymap) "The keymap valid in column display.") -(define-key org-column-map "e" 'org-column-edit) -(define-key org-column-map "v" 'org-column-show-value) -(define-key org-column-map "q" 'org-column-quit) -(define-key org-column-map [left] 'backward-char) -(define-key org-column-map [right] 'forward-char) - -(easy-menu-define org-column-menu org-column-map "Org Column Menu" +(defun org-columns-content () + "Switch to contents view while in columns view." + (interactive) + (org-overview) + (org-content)) + +(org-defkey org-columns-map "c" 'org-columns-content) +(org-defkey org-columns-map "o" 'org-overview) +(org-defkey org-columns-map "e" 'org-columns-edit-value) +(org-defkey org-columns-map "v" 'org-columns-show-value) +(org-defkey org-columns-map "q" 'org-columns-quit) +(org-defkey org-columns-map "r" 'org-columns-redo) +(org-defkey org-columns-map [left] 'backward-char) +(org-defkey org-columns-map "a" 'org-columns-edit-allowed) +(org-defkey org-columns-map "s" 'org-columns-edit-attributes) +(org-defkey org-columns-map [right] 'forward-char) +(org-defkey org-columns-map [(shift right)] 'org-columns-next-allowed-value) +(org-defkey org-columns-map "\C-c\C-c" 'org-columns-next-allowed-value) +(org-defkey org-columns-map "n" 'org-columns-next-allowed-value) +(org-defkey org-columns-map [(shift left)] 'org-columns-previous-allowed-value) +(org-defkey org-columns-map "p" 'org-columns-previous-allowed-value) +(org-defkey org-columns-map "<" 'org-columns-narrow) +(org-defkey org-columns-map ">" 'org-columns-widen) +(org-defkey org-columns-map [(meta right)] 'org-columns-move-right) +(org-defkey org-columns-map [(meta left)] 'org-columns-move-left) +(org-defkey org-columns-map [(shift meta right)] 'org-columns-new) +(org-defkey org-columns-map [(shift meta left)] 'org-columns-delete) + +(easy-menu-define org-columns-menu org-columns-map "Org Column Menu" '("Column" - ["Edit property" org-column-edit t] - ["Show full value" org-column-show-value t] - ["Quit" org-column-quit t])) - -(defun org-new-column-overlay (beg end &optional string face) - "Create a new column overlay an add it to the list." + ["Edit property" org-columns-edit-value t] + ["Next allowed value" org-columns-next-allowed-value t] + ["Previous allowed value" org-columns-previous-allowed-value t] + ["Show full value" org-columns-show-value t] + ["Edit allowed" org-columns-edit-allowed t] + "--" + ["Edit column attributes" org-columns-edit-attributes t] + ["Increase column width" org-columns-widen t] + ["Decrease column width" org-columns-narrow t] + "--" + ["Move column right" org-columns-move-right t] + ["Move column left" org-columns-move-left t] + ["Add column" org-columns-new t] + ["Delete column" org-columns-delete t] + "--" + ["CONTENTS" org-columns-content t] + ["OVERVIEW" org-overview t] + ["Refresh columns display" org-columns-redo t] + "--" + ["Quit" org-columns-quit t])) + +(defun org-columns-new-overlay (beg end &optional string face) + "Create a new column overlay and add it to the list." (let ((ov (org-make-overlay beg end))) (org-overlay-put ov 'face (or face 'secondary-selection)) (org-overlay-display ov string face) - (push ov org-column-overlays) + (push ov org-columns-overlays) ov)) -(defun org-overlay-columns (&optional props) +(defun org-columns-display-here (&optional props) "Overlay the current line with column display." (interactive) - (let ((fmt (copy-sequence org-current-columns-fmt)) - (beg (point-at-bol)) - (start 0) props pom property ass width f string ov) + (let* ((fmt org-columns-current-fmt-compiled) + (beg (point-at-bol)) + (level-face (save-excursion + (beginning-of-line 1) + (looking-at "\\(\\**\\)\\(\\* \\)") + (org-get-level-face 2))) + (color (list :foreground + (face-attribute (or level-face 'default) :foreground))) + props pom property ass width f string ov column) ;; Check if the entry is in another buffer. (unless props (if (eq major-mode 'org-agenda-mode) @@ -13651,11 +13887,9 @@ (get-text-property (point) 'org-marker)) props (if pom (org-entry-properties pom) nil)) (setq props (org-entry-properties nil)))) - ;; Parse the format - (while (string-match "%\\([0-9]+\\)?\\([a-zA-Z0-9]+\\)\\-*" - fmt start) - (setq start (match-end 0) - property (match-string 2 fmt) + ;; Walk the format + (while (setq column (pop fmt)) + (setq property (car column) ass (if (equal property "ITEM") (cons "ITEM" (save-match-data @@ -13664,17 +13898,21 @@ (buffer-substring-no-properties (point-at-bol) (point-at-eol)))))) (assoc property props)) - width (or (cdr (assoc property org-current-columns-maxwidths)) - (string-to-number (or (match-string 1 fmt) "10"))) + width (or (cdr (assoc property org-columns-current-maxwidths)) + (nth 2 column)) f (format "%%-%d.%ds | " width width) string (format f (or (cdr ass) ""))) ;; Create the overlay (org-unmodified - (setq ov (org-new-column-overlay - beg (setq beg (1+ beg)) string 'org-column)) - (org-overlay-put ov 'keymap org-column-map) - (org-overlay-put ov 'org-column-key property) - (org-overlay-put ov 'org-column-value (cdr ass))) + (setq ov (org-columns-new-overlay + beg (setq beg (1+ beg)) string + (list color 'org-column))) +;;; (list (get-text-property (point-at-bol) 'face) 'org-column))) + (org-overlay-put ov 'keymap org-columns-map) + (org-overlay-put ov 'org-columns-key property) + (org-overlay-put ov 'org-columns-value (cdr ass)) + (org-overlay-put ov 'org-columns-pom pom) + (org-overlay-put ov 'org-columns-format f)) (if (or (not (char-after beg)) (equal (char-after beg) ?\n)) (let ((inhibit-read-only t)) @@ -13682,64 +13920,72 @@ (goto-char beg) (insert " "))))) ;; Make the rest of the line disappear. - ;; FIXME: put the keymap also at the end of the line! (org-unmodified - (setq ov (org-new-column-overlay beg (point-at-eol))) + (setq ov (org-columns-new-overlay beg (point-at-eol))) (org-overlay-put ov 'invisible t) - (org-overlay-put ov 'keymap 'org-column-map) - (push ov org-column-overlays) + (org-overlay-put ov 'keymap org-columns-map) + (push ov org-columns-overlays) (setq ov (org-make-overlay (1- (point-at-eol)) (1+ (point-at-eol)))) - (org-overlay-put ov 'keymap 'org-column-map) - (push ov org-column-overlays) + (org-overlay-put ov 'keymap org-columns-map) + (push ov org-columns-overlays) (let ((inhibit-read-only t)) (put-text-property (1- (point-at-bol)) (min (point-max) (1+ (point-at-eol))) 'read-only "Type `e' to edit property"))))) -(defun org-overlay-columns-title () +(defvar org-previous-header-line-format nil + "The header line format before column view was turned on.") +(defvar org-columns-inhibit-recalculation nil + "Inhibit recomputing of columns on column view startup.") + +(defvar header-line-format) +(defun org-columns-display-here-title () "Overlay the newline before the current line with the table title." (interactive) - (let ((fmt (copy-sequence org-current-columns-fmt)) - (start 0) + (let ((fmt org-columns-current-fmt-compiled) string (title "") - property width f ov) - (while (string-match "%\\([0-9]+\\)?\\([a-zA-Z0-9]+\\)\\-*" - fmt start) - (setq start (match-end 0) - property (match-string 2 fmt) - width (or (cdr (assoc property org-current-columns-maxwidths)) - (string-to-number (or (match-string 1 fmt) "10"))) + property width f column str) + (while (setq column (pop fmt)) + (setq property (car column) + str (or (nth 1 column) property) + width (or (cdr (assoc property org-columns-current-maxwidths)) + (nth 2 column)) f (format "%%-%d.%ds | " width width) - string (format f property) + string (format f str) title (concat title string))) + (setq title (concat + (org-add-props " " nil 'display '(space :align-to 0)) + (org-add-props title nil 'face '(:weight bold :underline t)))) + (org-set-local 'org-previous-header-line-format header-line-format) + (setq header-line-format title))) + +(defun org-columns-remove-overlays () + "Remove all currently active column overlays." + (interactive) + (when (marker-buffer org-columns-begin-marker) + (with-current-buffer (marker-buffer org-columns-begin-marker) + (when (local-variable-p 'org-previous-header-line-format) + (setq header-line-format org-previous-header-line-format) + (kill-local-variable 'org-previous-header-line-format)) + (move-marker org-columns-begin-marker nil) + (move-marker org-columns-top-level-marker nil) (org-unmodified - (setq ov (org-new-column-overlay - (1- (point-at-bol)) (point-at-bol) - (concat "\n" (make-string (length title) ?-) "\n" - title "\n" (make-string (length title) ?-) "\n") - 'bold)) - (org-overlay-put ov 'keymap org-column-map)))) - -(defun org-remove-column-overlays () - "Remove all currently active column overlays." - (interactive) - (org-unmodified - (mapc 'org-delete-overlay org-column-overlays) - (setq org-column-overlays nil) - (let ((inhibit-read-only t)) - (remove-text-properties (point-min) (point-max) '(read-only t))))) - -(defun org-column-show-value () + (mapc 'org-delete-overlay org-columns-overlays) + (setq org-columns-overlays nil) + (let ((inhibit-read-only t)) + (remove-text-properties (point-min) (point-max) '(read-only t))))))) + +(defun org-columns-show-value () "Show the full value of the property." (interactive) - (let ((value (get-char-property (point) 'org-column-value))) + (let ((value (get-char-property (point) 'org-columns-value))) (message "Value is: %s" (or value "")))) -(defun org-column-quit () +(defun org-columns-quit () "Remove the column overlays and in this way exit column editing." (interactive) (org-unmodified - (org-remove-column-overlays) + (org-columns-remove-overlays) (let ((inhibit-read-only t)) ;; FIXME: is this safe??? ;; or are there other reasons why there may be a read-only property???? @@ -13747,13 +13993,13 @@ (when (eq major-mode 'org-agenda-mode) (message "Modification not yet reflected in Agenda buffer, use `r' to refresh"))) -(defun org-column-edit () +(defun org-columns-edit-value () "Edit the value of the property at point in column view. Where possible, use the standard interface for changing this line." (interactive) (let* ((col (current-column)) - (key (get-char-property (point) 'org-column-key)) - (value (get-char-property (point) 'org-column-value)) + (key (get-char-property (point) 'org-columns-key)) + (value (get-char-property (point) 'org-columns-value)) (bol (point-at-bol)) (eol (point-at-eol)) (pom (or (get-text-property bol 'org-hd-marker) (point))) ; keep despite of compiler waring @@ -13763,8 +14009,8 @@ (>= (overlay-start x) bol) (<= (overlay-start x) eol) x)) - org-column-overlays))) - nval eval) + org-columns-overlays))) + nval eval allowed) (when (equal key "ITEM") (error "Cannot edit item headline from here")) @@ -13788,7 +14034,10 @@ (setq eval '(org-with-point-at pom (call-interactively 'org-deadline)))) (t - (setq nval (read-string "Edit: " value)) + (setq allowed (org-property-get-allowed-values pom key 'table)) + (if allowed + (setq nval (completing-read "Value: " allowed nil t)) + (setq nval (read-string "Edit: " value))) (setq nval (org-trim nval)) (when (not (equal nval value)) (setq eval '(org-entry-put pom key nval))))) @@ -13797,67 +14046,272 @@ (remove-text-properties (1- bol) eol '(read-only t)) (unwind-protect (progn - (setq org-column-overlays - (org-delete-all line-overlays org-column-overlays)) + (setq org-columns-overlays + (org-delete-all line-overlays org-columns-overlays)) (mapc 'org-delete-overlay line-overlays) - (eval eval)) - (org-overlay-columns)))) - (move-to-column col))) + (org-columns-eval eval)) + (org-columns-display-here)))) + (move-to-column col) + (if (nth 3 (assoc key org-columns-current-fmt-compiled)) + (org-columns-update key)))) + +(defun org-columns-edit-allowed () + "Edit the list of allowed values for the current property." + (interactive) + (let* ((col (current-column)) + (key (get-char-property (point) 'org-columns-key)) + (key1 (concat key "_ALL")) + (value (get-char-property (point) 'org-columns-value)) + (allowed (org-entry-get (point) key1 t)) + nval) + (setq nval (read-string "Allowed: " allowed)) + (org-entry-put + (cond ((marker-position org-entry-property-inherited-from) + org-entry-property-inherited-from) + ((marker-position org-columns-top-level-marker) + org-columns-top-level-marker)) + key1 nval))) + +(defun org-columns-eval (form) + (let (hidep) + (save-excursion + (beginning-of-line 1) + (next-line 1) + (setq hidep (org-on-heading-p 1))) + (eval form) + (and hidep (hide-entry)))) + +(defun org-columns-previous-allowed-value () + "Switch to the previous allowed value for this column." + (interactive) + (org-columns-next-allowed-value t)) + +(defun org-columns-next-allowed-value (&optional previous) + "Switch to the next allowed value for this column." + (interactive) + (let* ((col (current-column)) + (key (get-char-property (point) 'org-columns-key)) + (value (get-char-property (point) 'org-columns-value)) + (bol (point-at-bol)) (eol (point-at-eol)) + (pom (or (get-text-property bol 'org-hd-marker) + (point))) ; keep despite of compiler waring + (line-overlays + (delq nil (mapcar (lambda (x) + (and (eq (overlay-buffer x) (current-buffer)) + (>= (overlay-start x) bol) + (<= (overlay-start x) eol) + x)) + org-columns-overlays))) + (allowed (or (org-property-get-allowed-values pom key) + (and (equal + (nth 4 (assoc key org-columns-current-fmt-compiled)) + 'checkbox) '("[ ]" "[X]")))) + nval) + (when (equal key "ITEM") + (error "Cannot edit item headline from here")) + (unless allowed + (error "Allowed values for this property have not been defined")) + (if previous (setq allowed (reverse allowed))) + (if (member value allowed) + (setq nval (car (cdr (member value allowed))))) + (setq nval (or nval (car allowed))) + (if (equal nval value) + (error "Only one allowed value for this property")) + (let ((inhibit-read-only t)) + (remove-text-properties (1- bol) eol '(read-only t)) + (unwind-protect + (progn + (setq org-columns-overlays + (org-delete-all line-overlays org-columns-overlays)) + (mapc 'org-delete-overlay line-overlays) + (org-columns-eval '(org-entry-put pom key nval))) + (org-columns-display-here))) + (move-to-column col) + (if (nth 3 (assoc key org-columns-current-fmt-compiled)) + (org-columns-update key)))) + +(defun org-verify-version (task) + (cond + ((eq task 'columns) + (if (or (featurep 'xemacs) + (< emacs-major-version 22)) + (error "Emacs 22 is required for the columns feature"))))) (defun org-columns () "Turn on column view on an org-mode file." (interactive) - (org-remove-column-overlays) + (org-verify-version 'columns) + (org-columns-remove-overlays) + (move-marker org-columns-begin-marker (point)) (let (beg end fmt cache maxwidths) - (move-marker org-entry-property-inherited-from nil) - (setq fmt (org-entry-get nil "COLUMNS" t)) - (unless fmt - (message "No local columns format defined, using default")) - (org-set-local 'org-current-columns-fmt (or fmt org-default-columns-format)) - (org-back-to-heading) + (when (condition-case nil (org-back-to-heading) (error nil)) + (move-marker org-entry-property-inherited-from nil) + (setq fmt (org-entry-get nil "COLUMNS" t))) + (setq fmt (or fmt org-columns-default-format)) + (org-set-local 'org-columns-current-fmt fmt) + (org-columns-compile-format fmt) (save-excursion (if (marker-position org-entry-property-inherited-from) (goto-char org-entry-property-inherited-from)) - (setq beg (point) - end (org-end-of-subtree t t)) + (setq beg (point)) + (move-marker org-columns-top-level-marker (point)) + (unless org-columns-inhibit-recalculation + (org-columns-compute-all)) + (setq end (or (condition-case nil (org-end-of-subtree t t) (error nil)) + (point-max))) (goto-char beg) ;; Get and cache the properties (while (re-search-forward (concat "^" outline-regexp) end t) (push (cons (org-current-line) (org-entry-properties)) cache)) (when cache - (setq maxwidths (org-get-columns-autowidth-alist fmt cache)) - (org-set-local 'org-current-columns-maxwidths maxwidths) + (setq maxwidths (org-columns-get-autowidth-alist fmt cache)) + (org-set-local 'org-columns-current-maxwidths maxwidths) (goto-line (car (org-last cache))) - (org-overlay-columns-title) + (org-columns-display-here-title) (mapc (lambda (x) (goto-line (car x)) - (org-overlay-columns (cdr x))) + (org-columns-display-here (cdr x))) cache))))) +(defun org-columns-new (&optional prop title width op fmt) + "Insert a new column, to the leeft o the current column." + (interactive) + (let ((editp (and prop (assoc prop org-columns-current-fmt-compiled))) + cell) + (setq prop (completing-read + "Property: " (mapcar 'list (org-buffer-property-keys t)) + nil nil prop)) + (setq title (read-string (concat "Column title [" prop "]: ") (or title prop))) + (setq width (read-string "Column width: " (if width (number-to-string width)))) + (if (string-match "\\S-" width) + (setq width (string-to-number width)) + (setq width nil)) + (setq fmt (completing-read "Summary [none]: " + '(("none") ("add_numbers") ("add_times") ("checkbox")) + nil t)) + (if (string-match "\\S-" fmt) + (setq fmt (intern fmt)) + (setq fmt nil)) + (if (eq fmt 'none) (setq fmt nil)) + (if editp + (progn + (setcar editp prop) + (setcdr editp (list title width nil fmt))) + (setq cell (nthcdr (1- (current-column)) + org-columns-current-fmt-compiled)) + (setcdr cell (cons (list prop title width nil fmt) + (cdr cell)))) + (org-columns-store-format) + (org-columns-redo))) + +(defun org-columns-delete () + "Delete the column at point from columns view." + (interactive) + (let* ((n (current-column)) + (title (nth 1 (nth n org-columns-current-fmt-compiled)))) + (when (y-or-n-p + (format "Are you sure you want to remove column \"%s\"? " title)) + (setq org-columns-current-fmt-compiled + (delq (nth n org-columns-current-fmt-compiled) + org-columns-current-fmt-compiled)) + (org-columns-store-format) + (org-columns-redo) + (if (>= (current-column) (length org-columns-current-fmt-compiled)) + (backward-char 1))))) + +(defun org-columns-edit-attributes () + "Edit the attributes of the current column." + (interactive) + (let* ((n (current-column)) + (info (nth n org-columns-current-fmt-compiled))) + (apply 'org-columns-new info))) + +(defun org-columns-widen (arg) + "Make the column wider by ARG characters." + (interactive "p") + (let* ((n (current-column)) + (entry (nth n org-columns-current-fmt-compiled)) + (width (or (nth 2 entry) + (cdr (assoc (car entry) org-columns-current-maxwidths))))) + (setq width (max 1 (+ width arg))) + (setcar (nthcdr 2 entry) width) + (org-columns-store-format) + (org-columns-redo))) + +(defun org-columns-narrow (arg) + "Make the column nrrower by ARG characters." + (interactive "p") + (org-columns-widen (- arg))) + +(defun org-columns-move-right () + "Swap this column with the one to the right." + (interactive) + (let* ((n (current-column)) + (cell (nthcdr n org-columns-current-fmt-compiled)) + e) + (when (>= n (1- (length org-columns-current-fmt-compiled))) + (error "Cannot shift this column further to the right")) + (setq e (car cell)) + (setcar cell (car (cdr cell))) + (setcdr cell (cons e (cdr (cdr cell)))) + (org-columns-store-format) + (org-columns-redo) + (forward-char 1))) + +(defun org-columns-move-left () + "Swap this column with the one to the left." + (interactive) + (let* ((n (current-column))) + (when (= n 0) + (error "Cannot shift this column further to the left")) + (backward-char 1) + (org-columns-move-right) + (backward-char 1))) + +(defun org-columns-store-format () + "Store the text version of the current columns format in appropriate place. +This is either in the COLUMNS property of the node starting the current column +display, or in the #+COLUMNS line of the current buffer." + (let (fmt) + (setq fmt (org-columns-uncompile-format org-columns-current-fmt-compiled)) + (if (marker-position org-columns-top-level-marker) + (save-excursion + (goto-char org-columns-top-level-marker) + (if (org-entry-get nil "COLUMNS") + (org-entry-put nil "COLUMNS" fmt) + (goto-char (point-min)) + (while (re-search-forward "^#\\+COLUMNS:.*" nil t) + (replace-match (concat "#+COLUMNS: " fmt t t))))) + (setq org-columns-current-fmt fmt)))) + (defvar org-overriding-columns-format nil - "FIXME:") + "When set, overrides any other definition.") (defvar org-agenda-view-columns-initially nil - "FIXME:") + "When set, switch to columns view immediately after creating the agenda.") (defun org-agenda-columns () "Turn on column view in the agenda." (interactive) - (let (fmt first-done cache maxwidths m) + (org-verify-version 'columns) + (org-columns-remove-overlays) + (move-marker org-columns-begin-marker (point)) + (let (fmt cache maxwidths m) (cond ((and (local-variable-p 'org-overriding-columns-format) org-overriding-columns-format) (setq fmt org-overriding-columns-format)) ((setq m (get-text-property (point-at-bol) 'org-hd-marker)) (setq fmt (org-entry-get m "COLUMNS" t))) - ((and (boundp 'org-current-columns-fmt) - (local-variable-p 'org-current-columns-fmt) - org-current-columns-fmt) - (setq fmt org-current-columns-fmt)) + ((and (boundp 'org-columns-current-fmt) + (local-variable-p 'org-columns-current-fmt) + org-columns-current-fmt) + (setq fmt org-columns-current-fmt)) ((setq m (next-single-property-change (point-min) 'org-hd-marker)) (setq m (get-text-property m 'org-hd-marker)) (setq fmt (org-entry-get m "COLUMNS" t)))) - (setq fmt (or fmt org-default-columns-format)) - (org-set-local 'org-current-columns-fmt fmt) + (setq fmt (or fmt org-columns-default-format)) + (org-set-local 'org-columns-current-fmt fmt) + (org-columns-compile-format fmt) (save-excursion ;; Get and cache the properties (goto-char (point-min)) @@ -13867,16 +14321,16 @@ (push (cons (org-current-line) (org-entry-properties m)) cache)) (beginning-of-line 2)) (when cache - (setq maxwidths (org-get-columns-autowidth-alist fmt cache)) - (org-set-local 'org-current-columns-maxwidths maxwidths) + (setq maxwidths (org-columns-get-autowidth-alist fmt cache)) + (org-set-local 'org-columns-current-maxwidths maxwidths) (goto-line (car (org-last cache))) - (org-overlay-columns-title) + (org-columns-display-here-title) (mapc (lambda (x) (goto-line (car x)) - (org-overlay-columns (cdr x))) + (org-columns-display-here (cdr x))) cache))))) -(defun org-get-columns-autowidth-alist (s cache) +(defun org-columns-get-autowidth-alist (s cache) "Derive the maximum column widths from the format and the cache." (let ((start 0) rtn) (while (string-match "%\\([a-zA-Z]\\S-*\\)" s start) @@ -13891,6 +14345,167 @@ rtn) rtn)) +(defun org-columns-compute-all () + "Compute all columns that have operators defined." + (remove-text-properties (point-min) (point-max) '(org-summaries t)) + (let ((columns org-columns-current-fmt-compiled) col) + (while (setq col (pop columns)) + (when (nth 3 col) + (save-excursion + (org-columns-compute (car col))))))) + +(defun org-columns-update (property) + "Recompute PROPERTY, and update the columns display for it." + (org-columns-compute property) + (let (fmt val pos) + (save-excursion + (mapc (lambda (ov) + (when (equal (org-overlay-get ov 'org-columns-key) property) + (setq pos (org-overlay-start ov)) + (goto-char pos) + (when (setq val (cdr (assoc property + (get-text-property (point-at-bol) 'org-summaries)))) + (setq fmt (org-overlay-get ov 'org-columns-format)) + (org-overlay-put ov 'display (format fmt val))))) + org-columns-overlays)))) + +(defun org-columns-compute (property) + "Sum the values of property PROPERTY hierarchically, for the entire buffer." + (interactive) + (let* ((re (concat "^" outline-regexp)) + (lmax 30) ; Does anyone use deeper levels??? + (lsum (make-vector lmax 0)) + (level 0) + (ass (assoc property org-columns-current-fmt-compiled)) + (format (nth 4 ass)) + (beg org-columns-top-level-marker) + last-level val end sumpos sum-alist sum str) + (save-excursion + ;; Find the region to compute + (goto-char beg) + (setq end (condition-case nil (org-end-of-subtree t) (error (point-max)))) + (goto-char end) + ;; Walk the tree from the back and do the computations + (while (re-search-backward re beg t) + (setq sumpos (match-beginning 0) + last-level level + level (org-outline-level) + val (org-entry-get nil property)) + (cond + ((< level last-level) + ;; put the sum of lower levels here as a property + (setq sum (aref lsum last-level) + str (org-column-number-to-string sum format) + sum-alist (get-text-property sumpos 'org-summaries)) + (if (assoc property sum-alist) + (setcdr (assoc property sum-alist) str) + (push (cons property str) sum-alist) + (add-text-properties sumpos (1+ sumpos) + (list 'org-summaries sum-alist))) + (when val + (org-entry-put nil property str)) + ;; add current to current level accumulator + (aset lsum level (+ (aref lsum level) sum)) + ;; clear accumulators for deeper levels + (loop for l from (1+ level) to (1- lmax) do (aset lsum l 0))) + ((>= level last-level) + ;; add what we have here to the accumulator for this level + (aset lsum level (+ (aref lsum level) + (org-column-string-to-number (or val "0") format)))) + (t (error "This should not happen"))))))) + +(defun org-columns-redo () + "Construct the column display again." + (interactive) + (message "Recomputing columns...") + (save-excursion + (if (marker-position org-columns-begin-marker) + (goto-char org-columns-begin-marker)) + (org-columns-remove-overlays) + (if (org-mode-p) + (call-interactively 'org-columns) + (call-interactively 'org-agenda-columns))) + (message "Recomputing columns...done")) + +(defun org-columns-not-in-agenda () + (if (eq major-mode 'org-agenda-mode) + (error "This command is only allowed in Org-mode buffers"))) + + +(defun org-string-to-number (s) + "Convert string to number, and interpret hh:mm:ss." + (if (not (string-match ":" s)) + (string-to-number s) + (let ((l (nreverse (org-split-string s ":"))) (sum 0.0)) + (while l + (setq sum (+ (string-to-number (pop l)) (/ sum 60)))) + sum))) + +(defun org-column-number-to-string (n fmt) + "Convert a computed column number to a string value, according to FMT." + (cond + ((eq fmt 'add_times) + (let* ((h (floor n)) (m (floor (+ 0.5 (* 60 (- n h)))))) + (format "%d:%02d" h m))) + ((eq fmt 'checkbox) + (cond ((= n (floor n)) "[X]") + ((> n 1.) "[-]") + (t "[ ]"))) + (t (number-to-string n)))) + +(defun org-column-string-to-number (s fmt) + "Convert a column value to a number that can be used for column computing." + (cond + ((string-match ":" s) + (let ((l (nreverse (org-split-string s ":"))) (sum 0.0)) + (while l + (setq sum (+ (string-to-number (pop l)) (/ sum 60)))) + sum)) + ((eq fmt 'checkbox) + (if (equal s "[X]") 1. 0.000001)) + (t (string-to-number s)))) + +(defun org-columns-uncompile-format (cfmt) + "Turn the compiled columns format back into a string representation." + (let ((rtn "") e s prop title op width fmt) + (while (setq e (pop cfmt)) + (setq prop (car e) + title (nth 1 e) + width (nth 2 e) + op (nth 3 e) + fmt (nth 4 e)) + (cond + ((eq fmt 'add_times) (setq op ":")) + ((eq fmt 'checkbox) (setq op "X")) + ((eq fmt 'add_numbers) (setq op "+"))) + (if (equal title prop) (setq title nil)) + (setq s (concat "%" (if width (number-to-string width)) + prop + (if title (concat "(" title ")")) + (if op (concat "{" op "}")))) + (setq rtn (concat rtn " " s))) + (org-trim rtn))) + +(defun org-columns-compile-format (fmt) + "FIXME" + (let ((start 0) width prop title op f) + (setq org-columns-current-fmt-compiled nil) + (while (string-match "%\\([0-9]+\\)?\\([a-zA-Z_0-9]+\\)\\(?:(\\([^)]+\\))\\)?\\(?:{\\([^}]+\\)}\\)?\\s-*" + fmt start) + (setq start (match-end 0) + width (match-string 1 fmt) + prop (match-string 2 fmt) + title (or (match-string 3 fmt) prop) + op (match-string 4 fmt) + f nil) + (if width (setq width (string-to-number width))) + (cond + ((equal op "+") (setq f 'add_numbers)) + ((equal op ":") (setq f 'add_times)) + ((equal op "X") (setq f 'checkbox))) + (push (list prop title width op f) org-columns-current-fmt-compiled)) + (setq org-columns-current-fmt-compiled + (nreverse org-columns-current-fmt-compiled)))) ;;;; Timestamps @@ -14084,7 +14699,7 @@ ;; Help matching am/pm times, because `parse-time-string' does not do that. ;; If there is a time with am/pm, and *no* time without it, we convert ;; so that matching will be successful. - ;; FIXME: make this replace twoce, so that we catch the end time. + ;; FIXME: make this replace twice, so that we catch the end time. (when (and (not (string-match "[012]?[0-9]:[0-9][0-9]\\([ \t\n]\\|$\\)" ans)) (string-match "\\([012]?[0-9]\\)\\(:\\([0-5][0-9]\\)\\)?\\(am\\|AM\\|pm\\|PM\\)\\>" ans)) (setq hour (string-to-number (match-string 1 ans)) @@ -15308,8 +15923,7 @@ (org-defkey org-agenda-mode-map [(right)] 'org-agenda-later) (org-defkey org-agenda-mode-map [(left)] 'org-agenda-earlier) (org-defkey org-agenda-mode-map "\C-c\C-x\C-c" 'org-agenda-columns) -; FIXME: other key? wtah about the menu???/ -;(org-defkey org-agenda-mode-map "\C-c\C-x\C-c" 'org-export-icalendar-combine-agenda-files) + (defvar org-agenda-keymap (copy-keymap org-agenda-mode-map) "Local keymap for agenda entries from Org-mode.") @@ -16555,7 +17169,6 @@ (mapcar 'list kwds) nil nil))) (and (equal 0 arg) (setq org-select-this-todo-keyword nil)) (org-set-local 'org-last-arg arg) -;FIXME (org-set-local 'org-todo-keywords-for-agenda kwds) (setq org-agenda-redo-command '(org-todo-list (or current-prefix-arg org-last-arg))) (setq files (org-agenda-files) @@ -16581,7 +17194,7 @@ (mapc (lambda (x) (setq s (format "(%d)%s" (setq n (1+ n)) x)) (if (> (+ (current-column) (string-width s) 1) (frame-width)) - (insert "\n ")) + (insert "\n ")) (insert " " s)) kwds)) (insert "\n")) @@ -16705,8 +17318,8 @@ "\\)\\>")) (tags (nth 2 org-stuck-projects)) (tags-re (if (member "*" tags) - (org-re "^\\*+.*:[[:alnum:]_@]+:[ \t]*$") - (concat "^\\*+.*:\\(" + (org-re "^\\*+ .*:[[:alnum:]_@]+:[ \t]*$") + (concat "^\\*+ .*:\\(" (mapconcat 'identity tags "\\|") (org-re "\\):[[:alnum:]_@:]*[ \t]*$")))) (gen-re (nth 3 org-stuck-projects)) @@ -16951,7 +17564,7 @@ (defun org-entry-is-done-p () "Is the current entry marked DONE?" (save-excursion - (and (re-search-backward "[\r\n]\\*" nil t) + (and (re-search-backward "[\r\n]\\* " nil t) (looking-at org-nl-done-regexp)))) (defun org-at-date-range-p (&optional inactive-ok) @@ -16984,7 +17597,7 @@ (format "mouse-2 or RET jump to org file %s" (abbreviate-file-name buffer-file-name)))) ;; FIXME: get rid of the \n at some point but watch out - (regexp (concat "[\n\r]\\*+ *\\(" + (regexp (concat "\n\\*+[ \t]+\\(" (if org-select-this-todo-keyword (if (equal org-select-this-todo-keyword "*") org-todo-regexp @@ -17093,12 +17706,12 @@ ;; substring should only run to end of time stamp (setq timestr (substring timestr 0 (match-end 0)))) (save-excursion - (if (re-search-backward "\\(^\\|\r\\)\\*+" nil t) + (if (re-search-backward "^\\*+ " nil t) (progn - (goto-char (match-end 1)) + (goto-char (match-beginning 0)) (setq hdmarker (org-agenda-new-marker) tags (org-get-tags-at)) - (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") + (looking-at "\\*+[ \t]+\\([^\r\n]+\\)") (setq txt (org-format-agenda-item (format "%s%s" (if deadlinep "Deadline: " "") @@ -17202,12 +17815,12 @@ ;; substring should only run to end of time stamp (setq timestr (substring timestr 0 (match-end 0)))) (save-excursion - (if (re-search-backward "\\(^\\|\r\\)\\*+" nil t) + (if (re-search-backward "^\\*+ " nil t) (progn - (goto-char (match-end 1)) + (goto-char (match-beginning 0)) (setq hdmarker (org-agenda-new-marker) tags (org-get-tags-at)) - (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") + (looking-at "\\*+[ \t]+\\([^\r\n]+\\)") (setq txt (org-format-agenda-item (if closedp "Closed: " "Clocked: ") (match-string 1) category tags timestr))) @@ -17252,10 +17865,10 @@ (if (and (< diff wdays) todayp (not (= diff 0))) (save-excursion (setq category (org-get-category)) - (if (re-search-backward "\\(^\\|\r\\)\\*+[ \t]*" nil t) + (if (re-search-backward "^\\*+[ \t]+" nil t) (progn (goto-char (match-end 0)) - (setq pos1 (match-end 1)) + (setq pos1 (match-beginning 0)) (setq tags (org-get-tags-at pos1)) (setq head (buffer-substring-no-properties (point) @@ -17311,10 +17924,10 @@ (if (and (< diff 0) todayp) (save-excursion (setq category (org-get-category)) - (if (re-search-backward "\\(^\\|\r\\)\\*+[ \t]*" nil t) + (if (re-search-backward "^\\*+[ \t]+" nil t) (progn (goto-char (match-end 0)) - (setq pos1 (match-end 1)) + (setq pos1 (match-beginning 0)) (setq tags (org-get-tags-at)) (setq head (buffer-substring-no-properties (point) @@ -17364,12 +17977,12 @@ (save-excursion (setq marker (org-agenda-new-marker (point))) (setq category (org-get-category)) - (if (re-search-backward "\\(^\\|\r\\)\\*+" nil t) + (if (re-search-backward "^\\*+ " nil t) (progn - (setq hdmarker (org-agenda-new-marker (match-end 1))) - (goto-char (match-end 1)) + (goto-char (match-beginning 0)) + (setq hdmarker (org-agenda-new-marker (point))) (setq tags (org-get-tags-at)) - (looking-at "\\*+[ \t]*\\([^\r\n]+\\)") + (looking-at "\\*+[ \t]+\\([^\r\n]+\\)") (setq txt (org-format-agenda-item (format (if (= d1 d2) "" "(%d/%d): ") (1+ (- d0 d1)) (1+ (- d2 d1))) @@ -17655,8 +18268,8 @@ (defsubst org-cmp-category (a b) "Compare the string values of categories of strings A and B." - (let ((ca (or (get-text-property 1 'category a) "")) - (cb (or (get-text-property 1 'category b) ""))) + (let ((ca (or (get-text-property 1 'org-category a) "")) + (cb (or (get-text-property 1 'org-category b) ""))) (cond ((string-lessp ca cb) -1) ((string-lessp cb ca) +1) (t nil)))) @@ -17715,7 +18328,7 @@ (if (not (one-window-p)) (delete-window)) (kill-buffer buf) (org-agenda-maybe-reset-markers 'force) - (org-remove-column-overlays)) + (org-columns-remove-overlays)) ;; Maybe restore the pre-agenda window configuration. (and org-agenda-restore-windows-after-quit (not (eq org-agenda-window-setup 'other-frame)) @@ -17814,10 +18427,12 @@ (defun org-agenda-day-view () "Switch to daily view for agenda." (interactive) + (setq org-agenda-ndays 1) (org-agenda-change-time-span 'day)) (defun org-agenda-week-view () "Switch to daily view for agenda." (interactive) + (setq org-agenda-ndays 7) (org-agenda-change-time-span 'week)) (defun org-agenda-month-view () "Switch to daily view for agenda." @@ -17860,8 +18475,9 @@ ((eq span 'week) (let* ((nt (calendar-day-of-week (calendar-gregorian-from-absolute sd))) - (n1 org-agenda-start-on-weekday) - (d (- nt n1))) + (d (if org-agenda-start-on-weekday + (- nt org-agenda-start-on-weekday) + 0))) (setq sd (- sd (+ (if (< d 0) 7 0) d))) (setq nd 7))) ((eq span 'month) @@ -18329,7 +18945,7 @@ (org-back-to-heading t) (condition-case nil (while t - (if (looking-at (org-re "[^\r\n]+?:\\([[:alnum:]_@:]+\\):[ \t]*\\([\n\r]\\|\\'\\)")) + (if (looking-at (org-re "[^\r\n]+?:\\([[:alnum:]_@:]+\\):[ \t]*$")) (setq tags (append (org-split-string (org-match-string-no-properties 1) ":") tags))) @@ -19463,7 +20079,8 @@ (re-archive (concat ":" org-archive-tag ":")) (re-quote (concat "^\\*+[ \t]+" org-quote-string "\\>")) (htmlp (plist-get parameters :for-html)) - (outline-regexp "\\*+") + (inhibit-read-only t) + (outline-regexp "\\*+ ") a b rtn p) (save-excursion @@ -19739,7 +20356,7 @@ :skip-before-1st-heading (plist-get opt-plist :skip-before-1st-heading) :add-text (plist-get opt-plist :text)) - "[\r\n]"))) + "[\r\n]"))) ;; FIXME: why \r here???/ thetoc have-headings first-heading-pos table-open table-buffer) @@ -19846,7 +20463,7 @@ (when custom-times (setq line (org-translate-time line))) (cond - ((string-match "^\\(\\*+\\)[ \t]*\\(.*\\)" line) + ((string-match "^\\(\\*+\\)[ \t]+\\(.*\\)" line) ;; a Headline (setq first-heading-pos (or first-heading-pos (point))) (setq level (org-tr-level (- (match-end 1) (match-beginning 1))) @@ -19953,7 +20570,7 @@ ;; find the indentation of the next non-empty line (catch 'stop (while lines - (if (string-match "^\\*" (car lines)) (throw 'stop nil)) + (if (string-match "^\\* " (car lines)) (throw 'stop nil)) (if (string-match "^\\([ \t]*\\)\\S-" (car lines)) (throw 'stop (setq ind (org-get-indentation (car lines))))) (pop lines))) @@ -20145,12 +20762,12 @@ (save-excursion (org-back-to-heading) (if (looking-at (concat outline-regexp - "\\( +\\<" org-quote-string "\\>\\)")) + "\\( *\\<" org-quote-string "\\>\\)")) (replace-match "" t t nil 1) (if (looking-at outline-regexp) (progn (goto-char (match-end 0)) - (insert " " org-quote-string)))))))) + (insert org-quote-string " ")))))))) (defun org-export-as-html-and-open (arg) "Export the outline as HTML and immediately open it with a browser. @@ -20303,7 +20920,7 @@ (file-name-nondirectory buffer-file-name))) "UNTITLED")) (quote-re0 (concat "^[ \t]*" org-quote-string "\\>")) - (quote-re (concat "^\\(\\*+\\)\\([ \t]*" org-quote-string "\\>\\)")) + (quote-re (concat "^\\(\\*+\\)\\([ \t]+" org-quote-string "\\>\\)")) (inquote nil) (infixed nil) (in-local-list nil) @@ -20495,7 +21112,7 @@ (catch 'nextline ;; end of quote section? - (when (and inquote (string-match "^\\*+" line)) + (when (and inquote (string-match "^\\*+ " line)) (insert "\n") (setq inquote nil)) ;; inside a quote section? @@ -20672,7 +21289,7 @@ t t line))))) (cond - ((string-match "^\\(\\*+\\)[ \t]*\\(.*\\)" line) + ((string-match "^\\(\\*+\\)[ \t]+\\(.*\\)" line) ;; This is a headline (setq level (org-tr-level (- (match-end 1) (match-beginning 1))) txt (match-string 2 line)) @@ -21595,7 +22212,7 @@ (with-current-buffer out (erase-buffer)) ;; Kick off the output (org-export-as-xoxo-insert-into out "
    \n") - (while (re-search-forward "^\\(\\*+\\) \\(.+\\)" (point-max) 't) + (while (re-search-forward "^\\(\\*+\\)[ \t]+\\(.+\\)" (point-max) 't) (let* ((hd (match-string-no-properties 1)) (level (length hd)) (text (concat @@ -21827,7 +22444,13 @@ (goto-char (match-beginning 0)) (self-insert-command N)) (setq org-table-may-need-update t) - (self-insert-command N))) + (self-insert-command N) + (org-fix-tags-on-the-fly))) + +(defun org-fix-tags-on-the-fly () + (when (and (equal (char-after (point-at-bol)) ?*) + (org-on-heading-p)) + (org-align-tags-here org-tags-column))) (defun org-delete-backward-char (N) "Like `delete-backward-char', insert whitespace at field end in tables. @@ -21850,7 +22473,8 @@ ;; noalign: if there were two spaces at the end, this field ;; does not determine the width of the column. (if noalign (setq org-table-may-need-update c))) - (backward-delete-char N))) + (backward-delete-char N) + (org-fix-tags-on-the-fly))) (defun org-delete-char (N) "Like `delete-char', but insert whitespace at field end in tables. @@ -21875,7 +22499,8 @@ ;; does not determine the width of the column. (if noalign (setq org-table-may-need-update c))) (delete-char N)) - (delete-char N))) + (delete-char N) + (org-fix-tags-on-the-fly))) ;; Make `delete-selection-mode' work with org-mode and orgtbl-mode (put 'org-self-insert-command 'delete-selection t) @@ -22052,6 +22677,7 @@ (cond ((org-at-timestamp-p t) (call-interactively 'org-timestamp-up-day)) ((org-on-heading-p) (org-call-with-arg 'org-todo 'right)) + ((org-at-property-p) (call-interactively 'org-property-next-allowed-value)) (t (org-shiftcursor-error)))) (defun org-shiftleft () @@ -22060,6 +22686,8 @@ (cond ((org-at-timestamp-p t) (call-interactively 'org-timestamp-down-day)) ((org-on-heading-p) (org-call-with-arg 'org-todo 'left)) + ((org-at-property-p) + (call-interactively 'org-property-previous-allowed-value)) (t (org-shiftcursor-error)))) (defun org-shiftcontrolright () @@ -22152,6 +22780,8 @@ ((and (local-variable-p 'org-finish-function (current-buffer)) (fboundp org-finish-function)) (funcall org-finish-function)) + ((org-at-property-p) + (call-interactively 'org-property-action)) ((org-on-target-p) (call-interactively 'org-update-radio-target-regexp)) ((org-on-heading-p) (call-interactively 'org-set-tags)) ((org-at-table.el-p) @@ -22306,9 +22936,9 @@ "--" ["Jump" org-goto t] "--" - ["C-a finds headline start" - (setq org-special-ctrl-a (not org-special-ctrl-a)) - :style toggle :selected org-special-ctrl-a]) + ["C-a/e find headline start/end" + (setq org-special-ctrl-a/e (not org-special-ctrl-a/e)) + :style toggle :selected org-special-ctrl-a/e]) ("Edit Structure" ["Move Subtree Up" org-shiftmetaup (not (org-at-table-p))] ["Move Subtree Down" org-shiftmetadown (not (org-at-table-p))] @@ -22361,17 +22991,7 @@ "--" ["Set Priority" org-priority t] ["Priority Up" org-shiftup t] - ["Priority Down" org-shiftdown t] - "--" - ;; FIXME: why is this still here???? -; ["Insert Checkbox" org-insert-todo-heading (org-in-item-p)] -; ["Toggle Checkbox" org-ctrl-c-ctrl-c (org-at-item-checkbox-p)] -; ["Insert [n/m] cookie" (progn (insert "[/]") (org-update-checkbox-count)) -; (or (org-on-heading-p) (org-at-item-p))] -; ["Insert [%] cookie" (progn (insert "[%]") (org-update-checkbox-count)) -; (or (org-on-heading-p) (org-at-item-p))] -; ["Update Statistics" org-update-checkbox-count t] - ) + ["Priority Down" org-shiftdown t]) ("TAGS and Properties" ["Set Tags" 'org-ctrl-c-ctrl-c (org-at-heading-p)] ["Column view of properties" org-columns t]) @@ -22811,16 +23431,16 @@ ;; text in a line directly attached to a headline would otherwise ;; fill the headline as well. (org-set-local 'comment-start-skip "^#+[ \t]*") - (org-set-local 'paragraph-separate "\f\\|\\*\\|[ ]*$\\|[ \t]*[:|]") + (org-set-local 'paragraph-separate "\f\\|\\*+ \\|[ ]*$\\|[ \t]*[:|]") ;; FIXME!!!!!!! (org-set-local 'paragraph-separate "\f\\|[ ]*$") ;; The paragraph starter includes hand-formatted lists. (org-set-local 'paragraph-start - "\f\\|[ ]*$\\|\\([*\f]+\\)\\|[ \t]*\\([-+*][ \t]+\\|[0-9]+[.)][ \t]+\\)\\|[ \t]*[:|]") + "\f\\|[ ]*$\\|\\*+ \\|\f\\|[ \t]*\\([-+*][ \t]+\\|[0-9]+[.)][ \t]+\\)\\|[ \t]*[:|]") ;; Inhibit auto-fill for headers, tables and fixed-width lines. ;; But only if the user has not turned off tables or fixed-width regions (org-set-local 'auto-fill-inhibit-regexp - (concat "\\*\\|#\\+" + (concat "\\*+ \\|#\\+" "\\|[ \t]*" org-keyword-time-regexp (if (or org-enable-table-editor org-enable-fixed-width-editor) (concat @@ -22866,10 +23486,13 @@ ;; C-a should go to the beginning of a *visible* line, also in the ;; new outline.el. I guess this should be patched into Emacs? -(defun org-beginning-of-line () +(defun org-beginning-of-line (&optional arg) "Go to the beginning of the current line. If that is invisible, continue -to a visible line beginning. This makes the function of C-a more intuitive." - (interactive) +to a visible line beginning. This makes the function of C-a more intuitive. +If this is a headline, and `org-special-ctrl-a/e' is set, ignore tags on the +first attempt, and only move to after the tags when the cursor is already +beyond the end of the headline." + (interactive "P") (let ((pos (point))) (beginning-of-line 1) (if (bobp) @@ -22880,14 +23503,33 @@ (backward-char 1) (beginning-of-line 1)) (forward-char 1))) - (when (and org-special-ctrl-a (looking-at org-todo-line-regexp) + (when (and org-special-ctrl-a/e (looking-at org-todo-line-regexp) (= (char-after (match-end 1)) ?\ )) (goto-char (cond ((> pos (match-beginning 3)) (match-beginning 3)) ((= pos (point)) (match-beginning 3)) (t (point))))))) +(defun org-end-of-line (&optional arg) + "Go to the end of the line. +If this is a headline, and `org-special-ctrl-a/e' is set, ignore tags on the +first attempt, and only move to after the tags when the cursor is already +beyond the end of the headline." + (interactive "P") + (if (or (not org-special-ctrl-a/e) + (not (org-on-heading-p))) + (end-of-line arg) + (let ((pos (point))) + (beginning-of-line 1) + (if (looking-at (org-re ".*?\\([ \t]*\\)\\(:[[:alnum:]_@:]+:\\)[ \t]*$")) + (if (or (< pos (match-beginning 1)) + (= pos (match-end 0))) + (goto-char (match-beginning 1)) + (goto-char (match-end 0))) + (end-of-line arg))))) + (define-key org-mode-map "\C-a" 'org-beginning-of-line) +(define-key org-mode-map "\C-e" 'org-end-of-line) (defun org-invisible-p () "Check if point is at a character currently not visible." @@ -23099,7 +23741,53 @@ ;; make tree, check each match with the callback (org-occur "CLOSED: +\\[\\(.*?\\)\\]" nil callback))) - +(defun org-fill-paragraph-experimental (&optional justify) + "Re-align a table, pass through to fill-paragraph if no table." + (let ((table-p (org-at-table-p)) + (table.el-p (org-at-table.el-p))) + (cond ((equal (char-after (point-at-bol)) ?*) t) ; skip headlines + (table.el-p t) ; skip table.el tables + (table-p (org-table-align) t) ; align org-mode tables + ((save-excursion + (let ((pos (1+ (point-at-eol)))) + (backward-paragraph 1) + (re-search-forward "\\\\\\\\[ \t]*$" pos t))) + (save-excursion + (save-restriction + (narrow-to-region (1+ (match-end 0)) (point-max)) + (fill-paragraph nil) + t))) + (t nil)))) ; call paragraph-fill + +(defun org-property-previous-allowed-value (&optional previous) + "Switch to the next allowed value for this property." + (interactive) + (org-property-next-allowed-value t)) + +(defun org-property-next-allowed-value (&optional previous) + "Switch to the next allowed value for this property." + (interactive) + (unless (org-at-property-p) + (error "Not at a property")) + (let* ((key (match-string 2)) + (value (match-string 3)) + (allowed (or (org-property-get-allowed-values (point) key) + (and (member value '("[ ]" "[-]" "[X]")) + '("[ ]" "[X]")))) + nval) + (unless allowed + (error "Allowed values for this property have not been defined")) + (if previous (setq allowed (reverse allowed))) + (if (member value allowed) + (setq nval (car (cdr (member value allowed))))) + (setq nval (or nval (car allowed))) + (if (equal nval value) + (error "Only one allowed value for this property")) + (org-at-property-p) + (replace-match (concat " :" key ": " nval)) + (org-indent-line-function) + (beginning-of-line 1) + (skip-chars-forward " \t"))) ;;;; Finish up @@ -23109,3 +23797,4 @@ ;; arch-tag: e77da1a7-acc7-4336-b19e-efa25af3f9fd ;;; org.el ends here + diff -r a37d5bf6cbb7 -r a66921565bcb lisp/textmodes/tex-mode.el --- a/lisp/textmodes/tex-mode.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/textmodes/tex-mode.el Sun Jul 15 04:47:46 2007 +0000 @@ -243,6 +243,21 @@ :options '("''" "\">" "\"'" ">>" "»") :group 'tex) +(defcustom tex-fontify-script t + "If non-nil, fontify subscript and superscript strings." + :type 'boolean + :group 'tex) +(put 'tex-fontify-script 'safe-local-variable 'booleanp) + +(defcustom tex-font-script-display '(-0.3 . 0.3) + "Display specification for subscript and superscript content. +The car is used for subscript, the cdr is used for superscripts." + :group 'tex + :type '(cons (choice (float :tag "Subscript") + (const :tag "No lowering" nil)) + (choice (float :tag "Superscript") + (const :tag "No raising" nil)))) + (defvar tex-last-temp-file nil "Latest temporary file generated by \\[tex-region] and \\[tex-buffer]. Deleted when the \\[tex-region] or \\[tex-buffer] is next run, or when the @@ -593,13 +608,14 @@ (setq pos (1- pos) odd (not odd))) odd)) (if (eq (char-after pos) ?_) - '(face subscript display (raise -0.3)) - '(face superscript display (raise +0.3))))) + `(face subscript display (raise ,(car tex-font-script-display))) + `(face superscript display (raise ,(cdr tex-font-script-display)))))) (defun tex-font-lock-match-suscript (limit) "Match subscript and superscript patterns up to LIMIT." - (when (re-search-forward "[_^] *\\([^\n\\{}]\\|\ -\\\\\\([a-zA-Z@]+\\|[^ \t\n]\\)\\|\\({\\)\\)" limit t) + (when (and tex-fontify-script + (re-search-forward "[_^] *\\([^\n\\{}]\\|\ +\\\\\\([a-zA-Z@]+\\|[^ \t\n]\\)\\|\\({\\)\\)" limit t)) (when (match-end 3) (let ((beg (match-beginning 3)) (end (save-restriction diff -r a37d5bf6cbb7 -r a66921565bcb lisp/vc-arch.el --- a/lisp/vc-arch.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/vc-arch.el Sun Jul 15 04:47:46 2007 +0000 @@ -424,13 +424,6 @@ ;;; Completion of versions and revisions. -(defun vc-arch-complete (table string pred action) - (assert (not (functionp table))) - (cond - ((null action) (try-completion string table pred)) - ((eq action t) (all-completions string table pred)) - (t (test-completion string table pred)))) - (defun vc-arch--version-completion-table (root string) (delq nil (mapcar @@ -450,10 +443,9 @@ (lexical-let ((file file)) (lambda (string pred action) ;; FIXME: complete revision patches as well. - (let ((root (expand-file-name "{arch}" (vc-arch-root file)))) - (vc-arch-complete - (vc-arch--version-completion-table root string) - string pred action))))) + (let* ((root (expand-file-name "{arch}" (vc-arch-root file))) + (table (vc-arch--version-completion-table root string))) + (complete-with-action action table string pred))))) ;;; Trimming revision libraries. diff -r a37d5bf6cbb7 -r a66921565bcb lisp/vc-cvs.el --- a/lisp/vc-cvs.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/vc-cvs.el Sun Jul 15 04:47:46 2007 +0000 @@ -29,8 +29,11 @@ ;;; Code: -(eval-when-compile - (require 'vc)) +(eval-when-compile (require 'cl) (require 'vc)) + +;; Clear up the cache to force vc-call to check again and discover +;; new functions when we reload this file. +(put 'CVS 'vc-functions nil) ;;; ;;; Customization options @@ -368,99 +371,45 @@ "-p" (vc-switches 'CVS 'checkout))) -(defun vc-cvs-checkout (file &optional editable rev workfile) - "Retrieve a revision of FILE into a WORKFILE. +(defun vc-cvs-checkout (file &optional editable rev) + "Checkout a revision of FILE into the working area. EDITABLE non-nil means that the file should be writable. -REV is the revision to check out into WORKFILE." - (let ((filename (or workfile file)) - (file-buffer (get-file-buffer file)) - switches) - (message "Checking out %s..." filename) - (save-excursion - ;; Change buffers to get local value of vc-checkout-switches. - (if file-buffer (set-buffer file-buffer)) - (setq switches (vc-switches 'CVS 'checkout)) - ;; Save this buffer's default-directory - ;; and use save-excursion to make sure it is restored - ;; in the same buffer it was saved in. - (let ((default-directory default-directory)) - (save-excursion - ;; Adjust the default-directory so that the check-out creates - ;; the file in the right place. - (setq default-directory (file-name-directory filename)) - (if workfile - (let ((failed t) - (backup-name (if (string= file workfile) - (car (find-backup-file-name filename))))) - (when backup-name - (copy-file filename backup-name - 'ok-if-already-exists 'keep-date) - (unless (file-writable-p filename) - (set-file-modes filename - (logior (file-modes filename) 128)))) - (unwind-protect - (progn - (let ((coding-system-for-read 'no-conversion) - (coding-system-for-write 'no-conversion)) - (with-temp-file filename - (apply 'vc-cvs-command - (current-buffer) 0 file - "-Q" ; suppress diagnostic output - "update" - (and (stringp rev) - (not (string= rev "")) - (concat "-r" rev)) - "-p" - switches))) - (setq failed nil)) - (if failed - (if backup-name - (rename-file backup-name filename - 'ok-if-already-exists) - (if (file-exists-p filename) - (delete-file filename))) - (and backup-name - (not vc-make-backup-files) - (delete-file backup-name))))) - (if (and (file-exists-p file) (not rev)) - ;; If no revision was specified, just make the file writable - ;; if necessary (using `cvs-edit' if requested). - (and editable (not (eq (vc-cvs-checkout-model file) 'implicit)) - (if vc-cvs-use-edit - (vc-cvs-command nil 0 file "edit") - (set-file-modes file (logior (file-modes file) 128)) - (if file-buffer (toggle-read-only -1)))) - ;; Check out a particular version (or recreate the file). - (vc-file-setprop file 'vc-workfile-version nil) - (apply 'vc-cvs-command nil 0 file - (and editable - (or (not (file-exists-p file)) - (not (eq (vc-cvs-checkout-model file) - 'implicit))) - "-w") - "update" - (when rev - (unless (eq rev t) - ;; default for verbose checkout: clear the - ;; sticky tag so that the actual update will - ;; get the head of the trunk - (if (string= rev "") - "-A" - (concat "-r" rev)))) - switches)))) - (vc-mode-line file) - (message "Checking out %s...done" filename))))) +REV is the revision to check out." + (message "Checking out %s..." file) + ;; Change buffers to get local value of vc-checkout-switches. + (with-current-buffer (or (get-file-buffer file) (current-buffer)) + (if (and (file-exists-p file) (not rev)) + ;; If no revision was specified, just make the file writable + ;; if necessary (using `cvs-edit' if requested). + (and editable (not (eq (vc-cvs-checkout-model file) 'implicit)) + (if vc-cvs-use-edit + (vc-cvs-command nil 0 file "edit") + (set-file-modes file (logior (file-modes file) 128)) + (if (equal file buffer-file-name) (toggle-read-only -1)))) + ;; Check out a particular version (or recreate the file). + (vc-file-setprop file 'vc-workfile-version nil) + (apply 'vc-cvs-command nil 0 file + (and editable "-w") + "update" + (when rev + (unless (eq rev t) + ;; default for verbose checkout: clear the + ;; sticky tag so that the actual update will + ;; get the head of the trunk + (if (string= rev "") + "-A" + (concat "-r" rev)))) + (vc-switches 'CVS 'checkout))) + (vc-mode-line file)) + (message "Checking out %s...done" file)) (defun vc-cvs-delete-file (file) (vc-cvs-command nil 0 file "remove" "-f") (vc-cvs-command nil 0 file "commit" "-mRemoved.")) (defun vc-cvs-revert (file &optional contents-done) - "Revert FILE to the version it was based on." - (unless contents-done - ;; Check out via standard output (caused by the final argument - ;; FILE below), so that no sticky tag is set. - (vc-cvs-checkout file nil (vc-workfile-version file) file)) + "Revert FILE to the version on which it was based." + (vc-default-revert 'CVS file contents-done) (unless (eq (vc-checkout-model file) 'implicit) (if vc-cvs-use-edit (vc-cvs-command nil 0 file "unedit") @@ -588,14 +537,36 @@ (and rev2 (concat "-r" rev2)) (vc-switches 'CVS 'diff)))))) +(defconst vc-cvs-annotate-first-line-re "^[0-9]") + +(defun vc-cvs-annotate-process-filter (process string) + (setq string (concat (process-get process 'output) string)) + (if (not (string-match vc-cvs-annotate-first-line-re string)) + ;; Still waiting for the first real line. + (process-put process 'output string) + (let ((vc-filter (process-get process 'vc-filter))) + (set-process-filter process vc-filter) + (funcall vc-filter process (substring string (match-beginning 0)))))) + (defun vc-cvs-annotate-command (file buffer &optional version) "Execute \"cvs annotate\" on FILE, inserting the contents in BUFFER. Optional arg VERSION is a version to annotate from." - (vc-cvs-command buffer 0 file "annotate" (if version (concat "-r" version))) - (with-current-buffer buffer - (goto-char (point-min)) - (re-search-forward "^[0-9]") - (delete-region (point-min) (1- (point))))) + (vc-cvs-command buffer + (if (and (vc-stay-local-p file) (fboundp 'start-process)) + 'async 0) + file "annotate" + (if version (concat "-r" version))) + ;; Strip the leading few lines. + (let ((proc (get-buffer-process buffer))) + (if proc + ;; If running asynchronously, use a process filter. + (progn + (process-put proc 'vc-filter (process-filter proc)) + (set-process-filter proc 'vc-cvs-annotate-process-filter)) + (with-current-buffer buffer + (goto-char (point-min)) + (re-search-forward vc-cvs-annotate-first-line-re) + (delete-region (point-min) (1- (point))))))) (defun vc-cvs-annotate-current-time () "Return the current time, based at midnight of the current day, and @@ -960,7 +931,34 @@ (vc-file-setprop file 'vc-checkout-time 0) (if set-state (vc-file-setprop file 'vc-state 'edited))))))))) +;; Completion of revision names. +;; Just so I don't feel like I'm duplicating code from pcl-cvs, I'll use +;; `cvs log' so I can list all the revision numbers rather than only +;; tag names. + +(defun vc-cvs-revision-table (file) + (let ((default-directory (file-name-directory file)) + (res nil)) + (with-temp-buffer + (vc-cvs-command t nil file "log") + (goto-char (point-min)) + (when (re-search-forward "^symbolic names:\n" nil t) + (while (looking-at "^ \\(.*\\): \\(.*\\)") + (push (cons (match-string 1) (match-string 2)) res) + (forward-line 1))) + (while (re-search-forward "^revision \\([0-9.]+\\)" nil t) + (push (match-string 1) res)) + res))) + +(defun vc-cvs-revision-completion-table (file) + (lexical-let ((file file) + table) + (setq table (lazy-completion-table + table (lambda () (vc-cvs-revision-table file)))) + table)) + + (provide 'vc-cvs) -;;; arch-tag: 60e1402a-aa53-4607-927a-cf74f144b432 +;; arch-tag: 60e1402a-aa53-4607-927a-cf74f144b432 ;;; vc-cvs.el ends here diff -r a37d5bf6cbb7 -r a66921565bcb lisp/vc-hooks.el --- a/lisp/vc-hooks.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/vc-hooks.el Sun Jul 15 04:47:46 2007 +0000 @@ -62,9 +62,9 @@ :type 'regexp :group 'vc) -(defcustom vc-handled-backends '(RCS CVS SVN SCCS HG Arch MCVS) +(defcustom vc-handled-backends '(RCS CVS BZR SVN SCCS HG Arch MCVS) ;; Arch and MCVS come last because they are per-tree rather than per-dir. - "*List of version control backends for which VC will be used. + "List of version control backends for which VC will be used. Entries in this list will be tried in order to determine whether a file is under that sort of version control. Removing an entry from the list prevents VC from being activated @@ -78,19 +78,19 @@ (if (file-directory-p "/usr/sccs") '("/usr/sccs") nil) - "*List of extra directories to search for version control commands." + "List of extra directories to search for version control commands." :type '(repeat directory) :group 'vc) (defcustom vc-make-backup-files nil - "*If non-nil, backups of registered files are made as with other files. + "If non-nil, backups of registered files are made as with other files. If nil (the default), files covered by version control don't get backups." :type 'boolean :group 'vc :group 'backup) (defcustom vc-follow-symlinks 'ask - "*What to do if visiting a symbolic link to a file under version control. + "What to do if visiting a symbolic link to a file under version control. Editing such a file through the link bypasses the version control system, which is dangerous and probably not what you want. @@ -104,26 +104,26 @@ :group 'vc) (defcustom vc-display-status t - "*If non-nil, display revision number and lock status in modeline. + "If non-nil, display revision number and lock status in modeline. Otherwise, not displayed." :type 'boolean :group 'vc) (defcustom vc-consult-headers t - "*If non-nil, identify work files by searching for version headers." + "If non-nil, identify work files by searching for version headers." :type 'boolean :group 'vc) (defcustom vc-keep-workfiles t - "*If non-nil, don't delete working files after registering changes. + "If non-nil, don't delete working files after registering changes. If the back-end is CVS, workfiles are always kept, regardless of the value of this flag." :type 'boolean :group 'vc) (defcustom vc-mistrust-permissions nil - "*If non-nil, don't assume permissions/ownership track version-control status. + "If non-nil, don't assume permissions/ownership track version-control status. If nil, do rely on the permissions. See also variable `vc-consult-headers'." :type 'boolean @@ -137,7 +137,7 @@ (vc-backend-subdirectory-name file))))) (defcustom vc-stay-local t - "*Non-nil means use local operations when possible for remote repositories. + "Non-nil means use local operations when possible for remote repositories. This avoids slow queries over the network and instead uses heuristics and past information to determine the current status of a file. @@ -742,17 +742,27 @@ This function assumes that the file is registered." (setq backend (symbol-name backend)) (let ((state (vc-state file)) + (state-echo nil) (rev (vc-workfile-version file))) - (cond ((or (eq state 'up-to-date) - (eq state 'needs-patch)) - (concat backend "-" rev)) - ((stringp state) - (concat backend ":" state ":" rev)) - (t - ;; Not just for the 'edited state, but also a fallback - ;; for all other states. Think about different symbols - ;; for 'needs-patch and 'needs-merge. - (concat backend ":" rev))))) + (propertize + (cond ((or (eq state 'up-to-date) + (eq state 'needs-patch)) + (setq state-echo "Up to date file") + (concat backend "-" rev)) + ((stringp state) + (setq state-echo (concat "File locked by" state)) + (concat backend ":" state ":" rev)) + (t + ;; Not just for the 'edited state, but also a fallback + ;; for all other states. Think about different symbols + ;; for 'needs-patch and 'needs-merge. + (setq state-echo "Edited file") + (concat backend ":" rev))) + 'mouse-face 'mode-line-highlight + 'local-map (let ((map (make-sparse-keymap))) + (define-key map [mode-line down-mouse-1] 'vc-menu-map) map) + 'help-echo (concat state-echo " under the " backend + " version control system\nmouse-1: VC Menu")))) (defun vc-follow-link () "If current buffer visits a symbolic link, visit the real file. @@ -783,7 +793,7 @@ (when buffer-file-name (vc-file-clearprops buffer-file-name) (cond - ((vc-backend buffer-file-name) + ((with-demoted-errors (vc-backend buffer-file-name)) ;; Compute the state and put it in the modeline. (vc-mode-line buffer-file-name) (unless vc-make-backup-files diff -r a37d5bf6cbb7 -r a66921565bcb lisp/w32-fns.el --- a/lisp/w32-fns.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/w32-fns.el Sun Jul 15 04:47:46 2007 +0000 @@ -149,14 +149,19 @@ (if default-enable-multibyte-characters '(undecided-dos . undecided-unix) '(raw-text-dos . raw-text-unix))) - (or (w32-using-nt) - ;; On Windows 9x, make cmdproxy default to using DOS line endings - ;; for input, because command.com requires this. - (setq process-coding-system-alist - `(("[cC][mM][dD][pP][rR][oO][xX][yY]" - . ,(if default-enable-multibyte-characters - '(undecided-dos . undecided-dos) - '(raw-text-dos . raw-text-dos))))))) + ;; Make cmdproxy default to using DOS line endings for input, + ;; because some Windows programs (including command.com) require it. + (add-to-list 'process-coding-system-alist + `("[cC][mM][dD][pP][rR][oO][xX][yY]" + . ,(if default-enable-multibyte-characters + '(undecided-dos . undecided-dos) + '(raw-text-dos . raw-text-dos)))) + ;; plink needs DOS input when entering the password. + (add-to-list 'process-coding-system-alist + `("[pP][lL][iI][nN][kK]" + . ,(if default-enable-multibyte-characters + '(undecided-dos . undecided-dos) + '(raw-text-dos . raw-text-dos))))) (add-hook 'before-init-hook 'set-default-process-coding-system) diff -r a37d5bf6cbb7 -r a66921565bcb lisp/window.el --- a/lisp/window.el Tue Jul 10 02:07:45 2007 +0000 +++ b/lisp/window.el Sun Jul 15 04:47:46 2007 +0000 @@ -645,10 +645,7 @@ ;; desired-height lines, constrained by MIN-HEIGHT and MAX-HEIGHT. (- (max (min desired-height max-height) (or min-height window-min-height)) - window-height)) - ;; We do our own height checking, so avoid any restrictions due to - ;; window-min-height. - (window-min-height 1)) + window-height))) ;; Don't try to redisplay with the cursor at the end ;; on its own line--that would force a scroll and spoil things. diff -r a37d5bf6cbb7 -r a66921565bcb lispref/ChangeLog --- a/lispref/ChangeLog Tue Jul 10 02:07:45 2007 +0000 +++ b/lispref/ChangeLog Sun Jul 15 04:47:46 2007 +0000 @@ -1,6 +1,23 @@ +2007-07-14 Richard Stallman + + * control.texi (Handling Errors): Document `debug' in handler list. + +2007-07-10 Richard Stallman + + * display.texi (Defining Faces): Explain C-M-x feature for defface. + +2007-07-09 Richard Stallman + + * files.texi (Magic File Names): Rewrite previous change. + +2007-07-08 Michael Albinus + + * files.texi (Magic File Names): Introduce optional parameter + CONNECTED for `file-remote-p'. + 2007-07-07 Michael Albinus - * process.texi (Asynchronous Processes): + * processes.texi (Asynchronous Processes): * files.texi (Magic File Names): Add `start-file-process'. 2007-06-27 Richard Stallman diff -r a37d5bf6cbb7 -r a66921565bcb lispref/control.texi --- a/lispref/control.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/lispref/control.texi Sun Jul 15 04:47:46 2007 +0000 @@ -893,6 +893,12 @@ This deletes the file named @var{filename}, catching any error and returning @code{nil} if an error occurs. + The @code{condition-case} construct is often used to trap errors that +are predictable, such as failure to open a file in a call to +@code{insert-file-contents}. It is also used to trap errors that are +totally unpredictable, such as when the program evaluates an expression +read from the user. + The second argument of @code{condition-case} is called the @dfn{protected form}. (In the example above, the protected form is a call to @code{delete-file}.) The error handlers go into effect when @@ -920,15 +926,33 @@ If an error is handled by some @code{condition-case} form, this ordinarily prevents the debugger from being run, even if @code{debug-on-error} says this error should invoke the debugger. -@xref{Error Debugging}. If you want to be able to debug errors that are -caught by a @code{condition-case}, set the variable -@code{debug-on-signal} to a non-@code{nil} value. + + If you want to be able to debug errors that are caught by a +@code{condition-case}, set the variable @code{debug-on-signal} to a +non-@code{nil} value. You can also specify that a particular handler +should let the debugger run first, by writing @code{debug} among the +conditions, like this: - When an error is handled, control returns to the handler. Before this -happens, Emacs unbinds all variable bindings made by binding constructs -that are being exited and executes the cleanups of all -@code{unwind-protect} forms that are exited. Once control arrives at -the handler, the body of the handler is executed. +@example +@group +(condition-case nil + (delete-file filename) + ((debug error) nil)) +@end group +@end example + +@noindent +The effect of @code{debug} here is only to prevent +@code{condition-case} from suppressing the call to the debugger. Any +given error will invoke the debugger only if @code{debug-on-error} and +the other usual filtering mechanisms say it should. @xref{Error Debugging}. + + Once Emacs decides that a certain handler handles the error, it +returns control to that handler. To do so, Emacs unbinds all variable +bindings made by binding constructs that are being exited, and +executes the cleanups of all @code{unwind-protect} forms that are +being exited. Once control arrives at the handler, the body of the +handler executes normally. After execution of the handler body, execution returns from the @code{condition-case} form. Because the protected form is exited @@ -937,12 +961,6 @@ bindings that were made within the protected form. All it can do is clean up and proceed. - The @code{condition-case} construct is often used to trap errors that -are predictable, such as failure to open a file in a call to -@code{insert-file-contents}. It is also used to trap errors that are -totally unpredictable, such as when the program evaluates an expression -read from the user. - Error signaling and handling have some resemblance to @code{throw} and @code{catch} (@pxref{Catch and Throw}), but they are entirely separate facilities. An error cannot be caught by a @code{catch}, and a @@ -960,7 +978,8 @@ Each of the @var{handlers} is a list of the form @code{(@var{conditions} @var{body}@dots{})}. Here @var{conditions} is an error condition name -to be handled, or a list of condition names; @var{body} is one or more +to be handled, or a list of condition names (which can include @code{debug} +to allow the debugger to run before the handler); @var{body} is one or more Lisp expressions to be executed when this handler handles an error. Here are examples of handlers: diff -r a37d5bf6cbb7 -r a66921565bcb lispref/display.texi --- a/lispref/display.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/lispref/display.texi Sun Jul 15 04:47:46 2007 +0000 @@ -1760,6 +1760,11 @@ @var{spec}, then uses any customizations that were read from the init file (@pxref{Init File}) to override that specification. +When you evaluate a @code{defcustom} form with @kbd{C-M-x} in Emacs +Lisp mode (@code{eval-defun}), a special feature of @code{eval-defun} +overrides any customizations of the face. This way, the face reflects +exactly what the @code{defcustom} says. + The purpose of @var{spec} is to specify how the face should appear on different kinds of terminals. It should be an alist whose elements have the form @code{(@var{display} @var{atts})}. Each element's diff -r a37d5bf6cbb7 -r a66921565bcb lispref/files.texi --- a/lispref/files.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/lispref/files.texi Sun Jul 15 04:47:46 2007 +0000 @@ -2768,7 +2768,7 @@ of the local copy file. @end defun -@defun file-remote-p filename +@defun file-remote-p filename &optional connected This function tests whether @var{filename} is a remote file. If @var{filename} is local (not remote), the return value is @code{nil}. If @var{filename} is indeed remote, the return value is a string that @@ -2777,7 +2777,7 @@ This identifier string can include a host name and a user name, as well as characters designating the method used to access the remote system. For example, the remote identifier string for the filename -@code{/ssh:user@@host:/some/file} is @code{/ssh:user@@host:}. +@code{/sudo::/some/file} is @code{/sudo:root@@localhost:}. If @code{file-remote-p} returns the same identifier for two different filenames, that means they are stored on the same file system and can @@ -2785,6 +2785,11 @@ example, that it is possible to start a remote process accessing both files at the same time. Implementors of file handlers need to ensure this principle is valid. + +If @var{connected} is non-@code{nil}, this function returns @code{nil} +even if @var{filename} is remote, if Emacs has no network connection +to its host. This is useful when you want to avoid the delay of +making connections when they don't exist. @end defun @defun unhandled-file-name-directory filename diff -r a37d5bf6cbb7 -r a66921565bcb man/ChangeLog --- a/man/ChangeLog Tue Jul 10 02:07:45 2007 +0000 +++ b/man/ChangeLog Sun Jul 15 04:47:46 2007 +0000 @@ -1,7 +1,52 @@ +2007-07-13 Eli Zaretskii + + * Makefile.in (../info/emacs-mime): Use --enable-encoding. + + * makefile.w32-in ($(infodir)/emacs-mime): Ditto. + + * emacs-mime.texi: Add @documentencoding directive. + +2007-07-12 Nick Roberts + + * tramp.texi (Remote processes): Add an anchor to the subsection + "Running a debugger on a remote host". + + * building.texi (Starting GUD): Add xref to this anchor. + +2007-07-12 Michael Albinus + + * tramp.texi (Remote processes): Don't call it "experimental" any + longer. Add subsection about running a debugger on a remote host. + +2007-07-10 Carsten Dominik + + * org.texi (Properties and columns): Chapter rewritten. + +2007-07-08 Michael Albinus + + * tramp.texi: + * trampver.texi: Migrate to Tramp 2.1. + 2007-07-02 Carsten Dominik * org.texi (Properties): New chapter. +2007-07-02 Reiner Steib + + * gnus-faq.texi ([3.2]): Fix locating of environment variables in the + Control Panel. + + * gnus.texi (Misc Article): Add index entry for + gnus-single-article-buffer. + +2007-06-27 Andreas Seltenreich + + * gnus.texi (Starting Up): Fix typo. + +2007-06-25 Katsumi Yamaoka + + * gnus.texi (Asynchronous Fetching): Fix typo. + 2007-06-24 Karl Berry * emacs.texi: new Back-Cover Text. diff -r a37d5bf6cbb7 -r a66921565bcb man/Makefile.in --- a/man/Makefile.in Tue Jul 10 02:07:45 2007 +0000 +++ b/man/Makefile.in Sun Jul 15 04:47:46 2007 +0000 @@ -217,7 +217,7 @@ $(ENVADD) $(TEXI2DVI) ${srcdir}/sieve.texi ../info/emacs-mime: emacs-mime.texi - cd $(srcdir); $(MAKEINFO) emacs-mime.texi + cd $(srcdir); $(MAKEINFO) --enable-encoding emacs-mime.texi emacs-mime.dvi: emacs-mime.texi $(ENVADD) $(TEXI2DVI) ${srcdir}/emacs-mime.texi diff -r a37d5bf6cbb7 -r a66921565bcb man/building.texi --- a/man/building.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/man/building.texi Sun Jul 15 04:47:46 2007 +0000 @@ -527,6 +527,10 @@ allowed. GUD assumes that the first argument not starting with a @samp{-} is the executable file name. +Tramp provides a facility to debug programs on remote hosts. +@xref{Running a debugger on a remote host, Running a debugger on a remote host,, tramp, The Tramp Manual}. +@c Running a debugger on a remote host + @node Debugger Operation @subsection Debugger Operation diff -r a37d5bf6cbb7 -r a66921565bcb man/emacs-mime.texi --- a/man/emacs-mime.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/man/emacs-mime.texi Sun Jul 15 04:47:46 2007 +0000 @@ -32,6 +32,9 @@ @end quotation @end copying +@c Node ``Interface Functions'' uses Latin-1 characters +@documentencoding ISO-8859-1 + @dircategory Emacs @direntry * Emacs MIME: (emacs-mime). Emacs MIME de/composition library. diff -r a37d5bf6cbb7 -r a66921565bcb man/gnus-faq.texi --- a/man/gnus-faq.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/man/gnus-faq.texi Sun Jul 15 04:47:46 2007 +0000 @@ -427,12 +427,11 @@ @end example @noindent -in your autoexec.bat and reboot. Under NT, 2000 and XP, -hit Winkey+Pause/Break to enter system options (if it -doesn't work, go to Control Panel -> System). There you'll -find the possibility to set environment variables, create -a new one with name HOME and value C:\myhome, a reboot is -not necessary. +in your autoexec.bat and reboot. Under NT, 2000 and XP, hit +Winkey+Pause/Break to enter system options (if it doesn't work, go to +Control Panel -> System -> Advanced). There you'll find the possibility +to set environment variables. Create a new one with name HOME and value +C:\myhome. Rebooting is not necessary. Now to create ~/.gnus.el, say @samp{C-x C-f ~/.gnus.el RET C-x C-s}. diff -r a37d5bf6cbb7 -r a66921565bcb man/gnus.texi --- a/man/gnus.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/man/gnus.texi Sun Jul 15 04:47:46 2007 +0000 @@ -947,8 +947,8 @@ @chapter Starting Gnus @cindex starting up -If you are haven't used Emacs much before using Gnus, read @ref{Emacs -for Heathens} first. +If you haven't used Emacs much before using Gnus, read @ref{Emacs for +Heathens} first. @kindex M-x gnus @findex gnus @@ -7173,12 +7173,12 @@ @code{nil}, no pre-fetching will be done. @vindex gnus-async-prefetch-article-p -@findex gnus-async-read-p +@findex gnus-async-unread-p There are probably some articles that you don't want to pre-fetch---read articles, for instance. The @code{gnus-async-prefetch-article-p} variable controls whether an article is to be pre-fetched. This function should return non-@code{nil} when the article in question is -to be pre-fetched. The default is @code{gnus-async-read-p}, which +to be pre-fetched. The default is @code{gnus-async-unread-p}, which returns @code{nil} on read articles. The function is called with an article data structure as the only parameter. @@ -11504,6 +11504,7 @@ @item gnus-single-article-buffer @vindex gnus-single-article-buffer +@cindex article buffers, several If non-@code{nil}, use the same article buffer for all the groups. (This is the default.) If @code{nil}, each group will have its own article buffer. @@ -13509,14 +13510,18 @@ @code{nnmail-split-header-length-limit} are excluded from the split function. +@vindex nnmail-mail-splitting-decodes @vindex nnmail-mail-splitting-charset -@vindex nnmail-mail-splitting-decodes -By default, splitting @acronym{MIME}-decodes headers so you -can match on non-@acronym{ASCII} strings. The -@code{nnmail-mail-splitting-charset} variable specifies the default -charset for decoding. The behavior can be turned off completely by -binding @code{nnmail-mail-splitting-decodes} to @code{nil}, which is -useful if you want to match articles based on the raw header data. +By default, splitting does not decode headers, so you can not match on +non-@acronym{ASCII} strings. But it is useful if you want to match +articles based on the raw header data. To enable it, set the +@code{nnmail-mail-splitting-decodes} variable to a non-@code{nil} value. +In addition, the value of the @code{nnmail-mail-splitting-charset} +variable is used for decoding non-@acronym{MIME} encoded string when +@code{nnmail-mail-splitting-decodes} is non-@code{nil}. The default +value is @code{nil} which means not to decode non-@acronym{MIME} encoded +string. A suitable value for you will be @code{undecided} or be the +charset used normally in mails you are interested in. @vindex nnmail-resplit-incoming By default, splitting is performed on all incoming messages. If you diff -r a37d5bf6cbb7 -r a66921565bcb man/makefile.w32-in --- a/man/makefile.w32-in Tue Jul 10 02:07:45 2007 +0000 +++ b/man/makefile.w32-in Sun Jul 15 04:47:46 2007 +0000 @@ -217,7 +217,7 @@ $(ENVADD) $(TEXI2DVI) $(srcdir)/message.texi # $(infodir)/emacs-mime: emacs-mime.texi - $(MAKEINFO) emacs-mime.texi + $(MAKEINFO) --enable-encoding emacs-mime.texi emacs-mime.dvi: emacs-mime.texi $(ENVADD) $(TEXI2DVI) $(srcdir)/emacs-mime.texi # diff -r a37d5bf6cbb7 -r a66921565bcb man/org.texi --- a/man/org.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/man/org.texi Sun Jul 15 04:47:46 2007 +0000 @@ -3,7 +3,7 @@ @setfilename ../info/org @settitle Org Mode Manual -@set VERSION 5.01 +@set VERSION 5.03 @set DATE July 2007 @dircategory Emacs @@ -81,7 +81,7 @@ * Hyperlinks:: Notes in context * TODO items:: Every tree branch can be a TODO item * Tags:: Tagging headlines and matching sets of tags -* Properties:: +* Properties and columns:: * Timestamps:: Assign date and time to items * Agenda views:: Collecting information into views * Embedded LaTeX:: LaTeX fragments and formulas @@ -113,7 +113,8 @@ * Archiving:: Move done task trees to a different place * Sparse trees:: Matches embedded in context * Plain lists:: Additional structure within an entry -* Drawers:: +* Drawers:: Tucking stuff away +* orgstruct-mode:: Structure editing outside Org-mode Archiving @@ -181,7 +182,7 @@ * Setting tags:: How to assign tags to a headline * Tag searches:: Searching for combinations of tags -Properties +Properties and Columns * Property syntax:: How properties are spelled out * Special properties:: Access to other Org-mode features @@ -194,6 +195,11 @@ * Defining columns:: The COLUMNS format property * Using column view:: How to create and use column view +Defining Columns + +* Scope of column definitions:: +* Column attributes:: + Timestamps * Time stamps:: Assigning a time to a tree entry @@ -379,7 +385,7 @@ Org-mode keeps simple things simple. When first fired up, it should feel like a straightforward, easy to use outliner. Complexity is not imposed, but a large amount of functionality is available when you need -it. Org-mode can be used on different levels and in different ways, for +it. Org-mode is a toolbox and can be used in different ways, for example as: @example @@ -389,6 +395,7 @@ @r{@bullet{} TODO list editor} @r{@bullet{} full agenda and planner with deadlines and work scheduling} @r{@bullet{} environment to implement David Allen's GTD system} +@r{@bullet{} a basic database application} @r{@bullet{} simple hypertext system, with HTML export} @r{@bullet{} publishing tool to create a set of interlinked webpages} @end example @@ -396,7 +403,9 @@ Org-mode's automatic, context sensitive table editor with spreadsheet capabilities can be integrated into any major mode by activating the minor Orgtbl-mode. Using a translation step, it can be used to maintain -tables in arbitrary file types, for example in LaTeX. +tables in arbitrary file types, for example in LaTeX. The structure +editing and list creation capabilities can be used outside Org-mode with +the minor Orgstruct-mode. @cindex FAQ There is a website for Org-mode which provides links to the newest @@ -468,9 +477,10 @@ @iftex @b{Important:} @i{If you use copy-and-paste to copy lisp code from the -PDF documentation to your .emacs file, the single quote character comes -out incorrectly and the code will not work. You need to fix the single -quotes by hand, or copy from Info documentation.} +PDF documentation as viewed by Acrobat reader to your .emacs file, the +single quote character comes out incorrectly and the code will not work. +You need to fix the single quotes by hand, or copy from Info +documentation.} @end iftex Add the following lines to your @file{.emacs} file. The last two lines @@ -580,7 +590,8 @@ * Archiving:: Move done task trees to a different place * Sparse trees:: Matches embedded in context * Plain lists:: Additional structure within an entry -* Drawers:: +* Drawers:: Tucking stuff away +* orgstruct-mode:: Structure editing outside Org-mode @end menu @node Outlines, Headlines, Document structure, Document structure @@ -605,8 +616,8 @@ Headlines define the structure of an outline tree. The headlines in Org-mode start with one or more stars, on the left margin@footnote{See -the variable @code{org-special-ctrl-a} to configure special behavior of -@kbd{C-a} in headlines.}. For example: +the variable @code{org-special-ctrl-a/e} to configure special behavior +of @kbd{C-a} and @kbd{C-e} in headlines.}. For example: @example * Top level headline @@ -1121,14 +1132,15 @@ With prefix arg, select the nth bullet from this list. @end table -@node Drawers, , Plain lists, Document structure +@node Drawers, orgstruct-mode, Plain lists, Document structure @section Drawers @cindex drawers +@cindex visibility cycling, drawers Sometimes you want to keep information associated with an entry, but you -normally don't want to see it, except when explicitly asking for it. -For this, Org-mode has @emph{drawers}. Drawers need to be configured -with the variable @code{org-drawers}, and look like this: +normally don't want to see it. For this, Org-mode has @emph{drawers}. +Drawers need to be configured with the variable @code{org-drawers}, and +look like this: @example ** This is a headline @@ -1143,7 +1155,30 @@ hide and show the entry, but keep the drawer collapsed to a single line. In order to look inside the drawer, you need to move the cursor to the drawer line and press @key{TAB} there. Org-mode uses a drawer for -storing properties (@pxref{Properties}). +storing properties (@pxref{Properties and columns}). + +@node orgstruct-mode, , Drawers, Document structure +@section The Orgstruct minor mode +@cindex orgstruct-mode +@cindex minor mode for structure editing + +If you like the intuitive way the Org-mode structure editing and list +formatting works, you might want to use these commands in other modes +like text-mode or mail-mode as well. The minor mode Orgstruct-mode +makes this possible. You can always toggle the mode with @kbd{M-x +orgstruct-mode}. To turn it on by default, for example in mail mode, +use + +@lisp +(add-hook 'mail-mode-hook 'turn-on-orgstruct) +@end lisp + +When this mode is active and the cursor is on a line that looks to +Org-mode like a headline of the first line of a list item, most +structure editing commands will work, even if the same keys normally +have different functionality in the major mode you are using. If the +cursor is not in one of those special lines, Orgstruct-mode lurks +silently in the shadow. @node Tables, Hyperlinks, Document structure, Top @chapter Tables @@ -1611,15 +1646,15 @@ @end example @noindent -Also properties (@pxref{Properties}) can be used as constants in table -formulas: For a property @samp{:XYZ:} use the name @samp{$PROP_XYZ}, and -the property will be searched in the current outline entry and in the -hierarchy above it. If you have the @file{constants.el} package, it -will also be used to resolve constants, including natural constants like -@samp{$h} for Planck's constant, and units like @samp{$km} for -kilometers@footnote{@file{Constant.el} can supply the values of -constants in two different unit systems, @code{SI} and @code{cgs}. -Which one is used depends on the value of the variable +Also properties (@pxref{Properties and columns}) can be used as +constants in table formulas: For a property @samp{:XYZ:} use the name +@samp{$PROP_XYZ}, and the property will be searched in the current +outline entry and in the hierarchy above it. If you have the +@file{constants.el} package, it will also be used to resolve constants, +including natural constants like @samp{$h} for Planck's constant, and +units like @samp{$km} for kilometers@footnote{@file{Constant.el} can +supply the values of constants in two different unit systems, @code{SI} +and @code{cgs}. Which one is used depends on the value of the variable @code{constants-unit-system}. You can use the @code{#+STARTUP} options @code{constSI} and @code{constcgs} to set this value for the current buffer.}. Column names and parameters can be specified in special table @@ -2998,7 +3033,8 @@ @table @kbd @kindex C-c C-c @item C-c C-c -Toggle checkbox at point. +Toggle checkbox at point. With prefix argument, set it to @samp{[-]}, +which is considered to be an intermediate state. @kindex C-c C-x C-b @item C-c C-x C-b Toggle checkbox at point. @@ -3030,7 +3066,7 @@ @end table -@node Tags, Properties, TODO items, Top +@node Tags, Properties and columns, TODO items, Top @chapter Tags @cindex tags @cindex headline tagging @@ -3286,8 +3322,8 @@ search @samp{+LEVEL=3+BOSS/-DONE} lists all level three headlines that have the tag BOSS and are @emph{not} marked with the todo keyword DONE. -@node Properties, Timestamps, Tags, Top -@chapter Properties +@node Properties and columns, Timestamps, Tags, Top +@chapter Properties and Columns @cindex properties Properties are a set of key-value pairs associated with an entry. There @@ -3298,7 +3334,8 @@ efficient to use a property @code{RELEASE} with a value @code{1.0} or @code{2.0}. Second, you can use properties to implement (very basic) database capabilities in an Org-mode buffer, for example to create a -list of Music CD's you own. +list of Music CD's you own. You can edit and view properties +conveniently in column view (@pxref{Column view}). @menu * Property syntax:: How properties are spelled out @@ -3308,8 +3345,10 @@ * Property API:: Properties for Lisp programmers @end menu -@node Property syntax, Special properties, Properties, Properties +@node Property syntax, Special properties, Properties and columns, Properties and columns @section Property Syntax +@cindex property syntax +@cindex drawer, for properties Properties are key-value pairs. They need to be inserted into a special drawer (@pxref{Drawers}) with the name @code{PROPERTIES}. Each property @@ -3324,26 +3363,65 @@ :Title: Goldberg Variations :Composer: J.S. Bach :Artist: Glen Gould - :END: + :Publisher: Deutsche Grammphon + :NDisks: 1 + :END: +@end example + +You may define the allowed values for a particular property @samp{XYZ} +by setting a property @samp{XYZ_ALL}. This special property is +@emph{inherited}, so if you set it in a level 1 entry, it will apply to +the entire tree. When allowed values are defined, setting the +corresponding property becomes easier and is less prone to typing +errors. For the example with the CD collection, we can predefine +publishers and the number of disks in a box like this: + +@example +* CD collection + :PROPERTIES: + :NDisks_ALL: 1 2 3 4 + :Publisher_ALL: "Deutsche Grammophon" Phillips EMI + :END: @end example @noindent -The following commands help to insert properties: +The following commands help to work with properties: @table @kbd @kindex M-@key{TAB} @item M-@key{TAB} After an initial colon in a line, complete property keys. All keys used in the current file will be offered as possible completions. +@item M-x org-insert-property-drawer +Insert a property drawer into the current entry. The drawer will be +inserted early in the entry, but after the lines with planning +information like deadlines. +@kindex C-c C-c +@item C-c C-c +With the cursor in a property drawer, this executes property commands. +@item C-c C-c s +Set a property in the current entry. Both the property and the value +can be inserted using completion. +@kindex S-@key{right} +@kindex S-@key{left} +@item S-@key{left}/@key{right} +Switch property at point to the next/previous allowed value. +@item C-c C-c d +Remove a property from the current entry. +@item C-c C-c D +Globally remove a property, from all entries in the current file. @end table - - -@node Special properties, Property searches, Property syntax, Properties +@node Special properties, Property searches, Property syntax, Properties and columns @section Special Properties - -Several properties are special, because they can be used to access other -features of Org-mode like the TODO status: +@cindex properties, special + +Special properties provide alternative access method to Org-mode +features discussed in the previous chapters, like the TODO state or the +priority of an entry. This interface exists so that you can include +these states into columns view (@pxref{Column view}). The following +property names are special and should not be used as keys in the +properties drawer: @example TODO @r{The TODO keyword of the entry.} @@ -3354,8 +3432,9 @@ SCHEDULED @r{The scheduling time stamp, without the angular brackets.} @end example -@node Property searches, Column view, Special properties, Properties +@node Property searches, Column view, Special properties, Properties and columns @section Property searches +@cindex properties, searching To create sparse trees and special lists with selection based on properties, the same commands are used as for tag searches (@pxref{Tag @@ -3371,23 +3450,22 @@ value @samp{unlimited}, and a @samp{:with:} property that is matched by the regular expression @samp{Sarah\|Denny}. -@node Column view, Property API, Property searches, Properties +@node Column view, Property API, Property searches, Properties and columns @section Column View -If different items in a document have similar properties, it can be nice -to view and edit those properties in a table-like format, in -@emph{column view}. Org-mode implements columns by overlaying a tabular -structure over the headline of an item. So the column view does not use -a special buffer, it happens in exactly the same buffer where the -outline is, and only temporarily changes the look of this buffer - not -the content. This has the advantage that you can still change the -visibility of the outline tree. For example, you get a compact table by -switching to CONTENTS view, but you can still open, read, and edit the -entry below each headline. Or, you can switch to column view after -executing a sparse tree command and in this way get a table only for the -selected items. Column view also works in agenda buffers (@pxref{Agenda -views}) where queries have collected selected items, possibly from a -number of files. +A great way to view and edit properties in an outline tree is +@emph{column view}. In column view, each outline item is turned into a +table row. Columns in this table provide access to properties of the +entries. Org-mode implements columns by overlaying a tabular structure +over the headline of each item. While the headlines have been turned +into a table row, you can still change the visibility of the outline +tree. For example, you get a compact table by switching to CONTENTS +view (@kbd{S-@key{TAB} S-@key{TAB}}, or simply @kbd{c} while column view +is active), but you can still open, read, and edit the entry below each +headline. Or, you can switch to column view after executing a sparse +tree command and in this way get a table only for the selected items. +Column view also works in agenda buffers (@pxref{Agenda views}) where +queries have collected selected items, possibly from a number of files. @menu * Defining columns:: The COLUMNS format property @@ -3396,81 +3474,122 @@ @node Defining columns, Using column view, Column view, Column view @subsection Defining Columns - -Setting up a column view first requires defining the columns. A column -definition is a property itself and looks like this: - -@example -:COLUMNS: %25ITEM %TAGS %PRIORITY %TODO -@end example - -This definition means that column 1 should be the first 25 characters of -the item itself, i.e. of the headline. You probably always should start -the column definition with the ITEM specifier - just select a useful -width for it. The other specifiers create columns for the local tags, -for the priority and for the TODO state. When no width is given after -the @samp{%} character, the column will be exactly as wide as it need to -be in order to fully display all values. - -If a @code{COLUMNS} property is present in an entry, it defines -columns for the entry itself, and for the entire subtree below it. -Since the column definition is part of the hierarchical structure of the -document, you can define columns on level 1 that are general enough for -all sublevels, and more specific columns further down, when you edit a deeper -part of the tree. Here is an example: - -@example -* People - :PROPERTIES: - :COLUMNS: %25ITEM %Name - :END: -** Family +@cindex column view, for properties +@cindex properties, column view + +Setting up a column view first requires defining the columns. This is +done by defining a column format line. + +@menu +* Scope of column definitions:: Where defined, where valid? +* Column attributes:: Appearance and content of a column +@end menu + +@node Scope of column definitions, Column attributes, Defining columns, Defining columns +@subsubsection Scope of column definitions + +To define a column format for an entire file, use a line like + +@example +#+COLUMNS: %25ITEM %TAGS %PRIORITY %TODO +@end example + +To specify a format that only applies to a specific tree, add a COLUMNS +property to the top node of that tree, for example +@example +** Top node for columns view :PROPERTIES: - :COLUMNS: %25ITEM %Name %3Age - :END: -*** Sam - Info about Sam, including a property list with Name and Age. -*** Sarah - Info about Sarah, including a property list with Name and Age. -** Office - :PROPERTIES: - :COLUMNS: %25ITEM %Name %Function %Salary + :COLUMNS: %25ITEM %TAGS %PRIORITY %TODO :END: -*** Boss - Info about the Boss, including a property list with Name, - Function and Salary (if only we knew....). -@end example - -Now we have defined three different sets of columns. If you switch to -column view in the @emph{Family} section, you will get a different table -than if you do it in the @emph{Office} section. However, if you switch -to column view with the cursor on the @emph{People} section, the table -will cover all entries, but contain only the @emph{Name} column. - -If no COLUMNS property applies to a given location, Org-mode uses a -default format specified in the variable -@code{org-default-columns-format}. This format in particular also -applies when column view is invoked with the cursor before the first -headline. You can set the default format on a per-file basis with a -line (don't forget to press @kbd{C-c C-c} to activate any changes to -this line). - -@example -#+COLUMNS: %25ITEM ....." -@end example +@end example + +If a @code{COLUMNS} property is present in an entry, it defines columns +for the entry itself, and for the entire subtree below it. Since the +column definition is part of the hierarchical structure of the document, +you can define columns on level 1 that are general enough for all +sublevels, and more specific columns further down, when you edit a +deeper part of the tree. + +@node Column attributes, , Scope of column definitions, Defining columns +@subsubsection Column attributes +A column definition sets the attributes of a column. The general +definition looks like this: + +@example + %[width]property[(title)][@{summary-type@}] +@end example + +@noindent +Except for the percent sign and the property name, all items are +optional. The individual parts have the following meaning: + +@example +width @r{An integer specifying the width of the column in characters.} + @r{If omitted, the width will be determined automatically.} +property @r{The property that should be edited in this column.} +(title) @r{The header text for the column. If omitted, the} + @r{property name is used.} +@{summary-type@} @r{The summary type. If specified, the column values for} + @r{parent nodes are computed from the children.} + @r{Supported summary types are:} + @{+@} @r{Sum numbers in this column.} + @{:@} @r{Sum times, HH:MM:SS, plain numbers are hours.} + @{X@} @r{Checkbox status, [X] if all children are [X].} +@end example + +@noindent +Here is an example for a complete columns definition, along with allowed +values. + +@example +:COLUMNS: %20ITEM %9Approved(Approved?)@{X@} %Owner %11Status %10Time_Spent@{:@} +:Owner_ALL: Tammy Mark Karl Lisa Don +:Status_ALL: "In progress" "Not started yet" "Finished" "" +:Approved_ALL: "[ ]" "[X]" +@end example + +The first column, @samp{%25ITEM}, means the first 25 characters of the +item itself, i.e. of the headline. You probably always should start the +column definition with the ITEM specifier. The other specifiers create +columns @samp{Owner} with a list of names as allowed values, for +@samp{Status} with four different possible values, and for a checkbox +field @samp{Approved}. When no width is given after the @samp{%} +character, the column will be exactly as wide as it needs to be in order +to fully display all values. The @samp{Approved} column does have a +modified title (@samp{Approved?}, with a question mark). Summaries will +be created for the @samp{Time_Spent} column by adding time duration +expressions like HH:MM, and for the @samp{Approved} column, by providing +an @samp{[X]} status if all children have been checked. @node Using column view, , Defining columns, Column view @subsection Using Column View @table @kbd +@tsubheading{Turning column view on and off} @kindex C-c C-x C-c @item C-c C-x C-c Create the column view for the local environment. This command searches the hierarchy, up from point, for a @code{COLUMNS} property that defines a format. When one is found, the column view table is established for -the entire subtree. +the entire tree, starting from the entry that contains the @code{COLUMNS} +property. If none is found, the format is taken from the @code{#+COLUMNS} +line or from the variable @code{org-columns-default-format}, and column +view is established for the current entry and its subtree. +@kindex q +@item q +Exit column view. +@tsubheading{Editing values} @item @key{left} @key{right} @key{up} @key{down} Move through the column view from field to field. +@kindex S-@key{left} +@kindex S-@key{right} +@item S-@key{left}/@key{right} +Switch to the next/previous allowed value of the field. For this, you +have to have specified allowed values for a property. +@kindex n +@kindex p +@itemx n / p +Same as @kbd{S-@key{left}/@key{right}} @kindex e @item e Edit the property at point. For the special properties, this will @@ -3481,20 +3600,36 @@ @item v View the full value of this property. This is useful if the width of the column is smaller than that of the value. -@kindex q -@item q -Exit column view. +@kindex a +@item a +Edit the list of allowed values for this property. If the list is found +in the hierarchy, the modified values is stored there. If no list is +found, the new value is stored in the first entry that is part of the +current column view. +@tsubheading{Modifying the table structure} +@kindex < +@kindex > +@item < / > +Make the column narrower/wider by one character. +@kindex S-M-@key{right} +@item S-M-@key{right} +Insert a new column, to the right of the current column. +@kindex S-M-@key{left} +@item S-M-@key{left} +Delete the current column. @end table -@node Property API, , Column view, Properties +@node Property API, , Column view, Properties and columns @section The Property API +@cindex properties, API +@cindex API, for properties There is a full API for accessing and changing properties. This API can be used by Emacs Lisp programs to work with properties and to implement features based on them. For more information see @ref{Using the property API}. -@node Timestamps, Agenda views, Properties, Top +@node Timestamps, Agenda views, Properties and columns, Top @chapter Timestamps @cindex time stamps @cindex date stamps @@ -4355,7 +4490,7 @@ to give an overview over events in a project. @table @kbd -@kindex C-a a L +@kindex C-c a L @item C-c a L Show a time-sorted view of the org file, with all time-stamped items. When called with a @kbd{C-u} prefix, all unfinished TODO entries @@ -4604,7 +4739,9 @@ @kindex m @kindex y @item d w m y -Switch to day/week/month/year view. +Switch to day/week/month/year view. When switching to day or week view, +this setting becomes the default for subseqent agenda commands. Since +month and year views are slow to create, the do not become the default. @c @kindex D @item D @@ -5947,16 +6084,15 @@ @chapter Publishing @cindex publishing -Org-mode includes@footnote{@file{org-publish.el} is not yet part of -Emacs, so if you are using @file{org.el} as it comes with Emacs, you -need to download this file separately. Also make sure org.el is at -least version 4.27.} a publishing management system -that allows you to configure automatic HTML conversion of -@emph{projects} composed of interlinked org files. This system is -called @emph{org-publish}. You can also configure org-publish to -automatically upload your exported HTML pages and related attachments, -such as images and source code files, to a web server. Org-publish turns -org-mode into a web-site authoring tool. +Org-mode includes@footnote{@file{org-publish.el} is not distributed with +Emacs 21, if you are still using Emacs 21, you need you need to download +this file separately.} a publishing management system that allows you to +configure automatic HTML conversion of @emph{projects} composed of +interlinked org files. This system is called @emph{org-publish}. You +can also configure org-publish to automatically upload your exported +HTML pages and related attachments, such as images and source code +files, to a web server. Org-publish turns org-mode into a web-site +authoring tool. Org-publish has been contributed to Org-mode by David O'Toole. @@ -6118,7 +6254,7 @@ When a property is given a value in org-publish-project-alist, its setting overrides the value of the corresponding user variable (if any) -during publishing. options set within a file (@pxref{Export +during publishing. Options set within a file (@pxref{Export options}), however, override everything. @node Publishing links, Project page index, Publishing options, Configuration @@ -6445,8 +6581,8 @@ logging @r{record a timestamp when an item is marked DONE} nologging @r{don't record when items are marked DONE} lognotedone @r{record timestamp and a note when DONE} -lognotestate @r{record timestamp, note when TODO state changes} -logrepeat @r{record a not when re-instating a repeating item} +lognotestate @r{record timestamp and a note when TODO state changes} +logrepeat @r{record a note when re-instating a repeating item} nologrepeat @r{do not record when re-instating repeating item} lognoteclock-out @r{record timestamp and a note when clocking out} @end example @@ -6531,6 +6667,9 @@ If the cursor is on a @code{<<>>}, update radio targets and corresponding links in this buffer. @item +If the cursor is in a property line or at the start or end of a property +drawer, offer property commands. +@item If the cursor is in a plain list item with a checkbox, toggle the status of the checkbox. @item @@ -7289,6 +7428,7 @@ @node Using the property API, , Special agenda views, Extensions and Hacking @section Using the property API @cindex API, for properties +@cindex properties, API Here is a description of the functions that can be used to work with properties. diff -r a37d5bf6cbb7 -r a66921565bcb man/texinfo.tex --- a/man/texinfo.tex Tue Jul 10 02:07:45 2007 +0000 +++ b/man/texinfo.tex Sun Jul 15 04:47:46 2007 +0000 @@ -3,7 +3,7 @@ % Load plain if necessary, i.e., if running under initex. \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi % -\def\texinfoversion{2007-06-16.10} +\def\texinfoversion{2007-07-09.21} % % Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, % 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, @@ -7434,22 +7434,41 @@ % @documentlanguage is usually given very early, just after % @setfilename. If done too late, it may not override everything -% properly. Single argument is the language abbreviation. -% It would be nice if we could set up a hyphenation file here. -% -\parseargdef\documentlanguage{% +% properly. Single argument is the language (de) or locale (de_DE) +% abbreviation. It would be nice if we could set up a hyphenation file. +% +{ + \catcode`\_ = \active + \globaldefs=1 +\parseargdef\documentlanguage{\begingroup + \let_=\normalunderscore % normal _ character for filenames \tex % read txi-??.tex file in plain TeX. - % Read the file if it exists. + % Read the file by the name they passed if it exists. \openin 1 txi-#1.tex \ifeof 1 - \errhelp = \nolanghelp - \errmessage{Cannot read language file txi-#1.tex}% + \documentlanguagetrywithoutunderscore{#1_\finish}% \else \input txi-#1.tex \fi \closein 1 \endgroup -} +\endgroup} +} +% +% If they passed de_DE, and txi-de_DE.tex doesn't exist, +% try txi-de.tex. +% +\def\documentlanguagetrywithoutunderscore#1_#2\finish{% + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \input txi-#1.tex + \fi + \closein 1 +} +% \newhelp\nolanghelp{The given language definition file cannot be found or is empty. Maybe you need to install it? In the current directory should work if nowhere else does.} @@ -8316,6 +8335,8 @@ \ifpdf \pdfpageheight #7\relax \pdfpagewidth #8\relax + \pdfhorigin = 1 true in + \pdfvorigin = 1 true in \fi % \setleading{\textleading} diff -r a37d5bf6cbb7 -r a66921565bcb man/tramp.texi --- a/man/tramp.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/man/tramp.texi Sun Jul 15 04:47:46 2007 +0000 @@ -18,15 +18,27 @@ @include trampver.texi @c Macros for formatting a filename. -@c trampfn is for a full filename, trampfnmhp means method, host, localname +@c trampfn is for a full filename, trampfnmhl means method, host, localname @c were given, and so on. -@macro trampfn(method, user, host, localname) -@value{prefix}@value{method}@value{user}@@@value{host}@value{postfix}@value{localname} +@macro trampfn {method, user, host, localname} +@value{prefix}\method\@value{postfixhop}\user\@@\host\@value{postfix}\localname\ +@end macro + +@macro trampfnmhl {method, host, localname} +@value{prefix}\method\@value{postfixhop}\host\@value{postfix}\localname\ +@end macro + +@macro trampfnuhl {user, host, localname} +@value{prefix}\user\@@\host\@value{postfix}\localname\ +@end macro + +@macro trampfnhl {host, localname} +@value{prefix}\host\@value{postfix}\localname\ @end macro @copying -Copyright @copyright{} 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 -Free Software Foundation, Inc. +Copyright @copyright{} 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +2007 Free Software Foundation, Inc. @quotation Permission is granted to copy, distribute and/or modify this document @@ -157,6 +169,7 @@ * Version Control:: The inner workings of remote version control. * Files directories and localnames:: How file names, directories and localnames are mangled and managed. +* Traces and Profiles:: How to Customize Traces. * Issues:: Debatable Issues and What Was Decided. * GNU Free Documentation License:: The license for this documentation. @@ -178,11 +191,17 @@ * Connection types:: Types of connections made to remote machines. * Inline methods:: Inline methods. * External transfer methods:: External transfer methods. -* Multi-hop Methods:: Connecting to a remote host using multiple hops. +@ifset emacsgw +* Gateway methods:: Gateway methods. +@end ifset * Default Method:: Selecting a default method. +* Default User:: Selecting a default user. +* Default Host:: Selecting a default host. +* Multi-hops:: Connecting to a remote host using multiple hops. * Customizing Methods:: Using Non-Standard Methods. * Customizing Completion:: Selecting config files for user/host name completion. * Password caching:: Reusing passwords for several connections. +* Connection caching:: Reusing connection related information. * Remote Programs:: How @value{tramp} finds and uses programs on the remote machine. * Remote shell setup:: Remote shell setup hints. * Windows setup hints:: Issues with Cygwin ssh. @@ -191,10 +210,9 @@ Using @value{tramp} * Filename Syntax:: @value{tramp} filename conventions. -* Multi-hop filename syntax:: Multi-hop filename conventions. +* Alternative Syntax:: URL-like filename syntax. * Filename completion:: Filename completion. -* Dired:: Dired. -* Compilation:: Compile remote files. +* Remote processes:: Integration with other @value{emacsname} packages. The inner workings of remote version control @@ -220,10 +238,10 @@ @chapter An overview of @value{tramp} @cindex overview -After the installation of @value{tramp} into your @value{emacsname}, -you will be able to access files on remote machines as though they -were local. Access to the remote file system for editing files, -version control, and @code{dired} are transparently enabled. +After the installation of @value{tramp} into your @value{emacsname}, you +will be able to access files on remote machines as though they were +local. Access to the remote file system for editing files, version +control, and @code{dired} are transparently enabled. Your access to the remote machine can be with the @command{rsh}, @command{rlogin}, @command{telnet} programs or with any similar @@ -380,7 +398,7 @@ @value{tramp} is freely available on the Internet and the latest release may be downloaded from -@uref{ftp://ftp.gnu.org/gnu/tramp/}. This release includes the full +@uref{ftp://ftp.gnu.org/gnu/tramp/}. This release includes the full documentation and code for @value{tramp}, suitable for installation. But GNU Emacs (22 or later) includes @value{tramp} already, and there is a @value{tramp} package for XEmacs, as well. So maybe it is easier @@ -389,7 +407,7 @@ For the especially brave, @value{tramp} is available from CVS. The CVS version is the latest version of the code and may contain incomplete -features or new issues. Use these versions at your own risk. +features or new issues. Use these versions at your own risk. Instructions for obtaining the latest development version of @value{tramp} from CVS can be found by going to the Savannah project page at the @@ -410,7 +428,7 @@ @noindent You should now have a directory @file{~/@value{emacsdir}/tramp} -containing the latest version of @value{tramp}. You can fetch the latest +containing the latest version of @value{tramp}. You can fetch the latest updates from the repository by issuing the command: @example @@ -429,6 +447,11 @@ ] @strong{autoconf} @end example +People who have no direct CVS access (maybe because sitting behind a +blocking firewall), can try the +@uref{http://savannah.gnu.org/cvs-backup/tramp-sources.tar.gz, Nightly +CVS Tree Tarball} instead of. + @node History @chapter History of @value{tramp} @@ -445,7 +468,19 @@ The most recent addition of major features were the multi-hop methods added in April 2000 and the unification of @value{tramp} and Ange-FTP -filenames in July 2002. +filenames in July 2002. In July 2004, multi-hop methods have been +replaced by proxy hosts. Running commands on remote hosts was +introduced in December 2005. +@ifset emacsgw +Support of gateways exists since April 2007. +@end ifset + +In December 2001, @value{tramp} has been added to the XEmacs package +repository. Being part of the GNU Emacs repository happened in June +2002, the first release including @value{tramp} was GNU Emacs 22.1. + +@value{tramp} is also a GNU/Linux Debian package since February 2001. + @c Installation chapter is necessary only in case of standalone @c installation. Text taken from trampinst.texi. @@ -462,7 +497,7 @@ installed. It is initially configured to use the @command{scp} program to connect to the remote host. So in the easiest case, you just type @kbd{C-x C-f} and then enter the filename -@file{@value{prefix}@var{user}@@@var{machine}@value{postfix}@var{/path/to.file}}. +@file{@trampfnuhl{user, machine, /path/to.file}}. On some hosts, there are problems with opening a connection. These are related to the behavior of the remote shell. See @xref{Remote shell @@ -482,14 +517,20 @@ * Connection types:: Types of connections made to remote machines. * Inline methods:: Inline methods. * External transfer methods:: External transfer methods. -* Multi-hop Methods:: Connecting to a remote host using multiple hops. +@ifset emacsgw +* Gateway methods:: Gateway methods. +@end ifset * Default Method:: Selecting a default method. Here we also try to help those who don't have the foggiest which method is right for them. +* Default User:: Selecting a default user. +* Default Host:: Selecting a default host. +* Multi-hops:: Connecting to a remote host using multiple hops. * Customizing Methods:: Using Non-Standard Methods. * Customizing Completion:: Selecting config files for user/host name completion. * Password caching:: Reusing passwords for several connections. +* Connection caching:: Reusing connection related information. * Remote Programs:: How @value{tramp} finds and uses programs on the remote machine. * Remote shell setup:: Remote shell setup hints. * Windows setup hints:: Issues with Cygwin ssh. @@ -508,7 +549,7 @@ This connection is used to perform many of the operations that @value{tramp} requires to make the remote file system transparently accessible from -the local machine. It is only when visiting files that the methods +the local machine. It is only when visiting files that the methods differ. @cindex inline methods @@ -519,7 +560,7 @@ @cindex methods, external transfer @cindex methods, out-of-band Loading or saving a remote file requires that the content of the file -be transfered between the two machines. The content of the file can be +be transfered between the two machines. The content of the file can be transfered over the same connection used to log in to the remote machine or the file can be transfered through another connection using a remote copy program such as @command{rcp}, @command{scp} or @@ -539,16 +580,10 @@ External transfer methods should be configured such a way that they don't require a password (with @command{ssh-agent}, or such alike). -If it isn't possible, you should consider @ref{Password caching}, -otherwise you will be prompted for a password every copy action. - -@cindex multi-hop methods -@cindex methods, multi-hop -A variant of the inline methods are the @dfn{multi-hop methods}. -These methods allow you to connect a remote host using a number `hops', -each of which connects to a different host. This is useful if you are -in a secured network where you need to go through a bastion host to -connect to the outside world. +Modern @command{scp} implementations offer options to reuse existing +@command{ssh} connections, see method @command{scpc}. If it isn't +possible, you should consider @ref{Password caching}, otherwise you +will be prompted for a password every copy action. @node Inline methods @@ -635,6 +670,8 @@ This method does not connect to a remote host at all, rather it uses the @command{su} program to allow you to edit files as another user. +With other words, a specified host name in the file name is silently +ignored. @item @option{sudo} @@ -682,7 +719,7 @@ @item @option{krlogin} @cindex method krlogin -@cindex km krlogin +@cindex krlogin method @cindex Kerberos (with krlogin method) This method is also similar to @option{ssh}. It only uses the @@ -697,20 +734,45 @@ implementation of SSH. It uses @samp{plink -ssh} to log in to the remote host. -Additionally, the method @option{plink1} is provided, which calls -@samp{plink -1 -ssh} in order to use SSH protocol version 1 -explicitly. +This supports the @samp{-P} kludge. + +Additionally, the methods @option{plink1} and @option{plink2} are +provided, which call @samp{plink -1 -ssh} or @samp{plink -2 -ssh} in +order to use SSH protocol version 1 or 2 explicitly. CCC: Do we have to connect to the remote host once from the command line to accept the SSH key? Maybe this can be made automatic? -CCC: Does @command{plink} support the @samp{-p} option? @value{tramp} will -support that, anyway. +CCC: Say something about the first shell command failing. This might +be due to a wrong setting of @code{tramp-rsh-end-of-line}. + + +@item @option{plinkx} +@cindex method plinkx +@cindex plinkx method + +Another method using PuTTY on Windows. Instead of host names, it +expects PuTTY session names, calling @samp{plink -load @var{session} +-t"}. User names are relevant only in case the corresponding session +hasn't defined a user name. Different port numbers must be defined in +the session. + + +@item @option{fish} +@cindex method fish +@cindex fish method + +This is an experimental implementation of the fish protocol, known from +the GNU Midnight Commander or the KDE Konqueror. @value{tramp} expects +the fish server implementation from the KDE kioslave. That means, the +file @file{~/.fishsrv.pl} is expected to reside on the remote host. + +The implementation lacks good performance. The code is offered anyway, +maybe somebody can improve the performance. @end table - @node External transfer methods @section External transfer methods @cindex methods, external transfer @@ -725,21 +787,10 @@ This saves the overhead of encoding and decoding that multiplexing the transfer through the one connection has with the inline methods. -If you want to use an external transfer method you should be able to -execute the transfer utility to copy files to and from the remote -machine without any interaction. - -@cindex ssh-agent -This means that you will need to use @command{ssh-agent} if you use the -@command{scp} program for transfers, or maybe your version of -@command{scp} accepts a password on the command line.@footnote{PuTTY's -@command{pscp} allows you to specify the password on the command line.} -If you use @command{rsync} via @command{ssh} then the same rule must -apply to that connection. - -If you cannot get an external method to run without asking for a -password you should consider @ref{Password caching}. - +Since external transfer methods need their own overhead opening a new +channel, all files which are smaller than @var{tramp-copy-size-limit} +are still transferred with the corresponding inline method. It should +provide a fair trade-off between both approaches. @table @asis @item @option{rcp} --- @command{rsh} and @command{rcp} @@ -767,7 +818,7 @@ transfer files between the machines is the best method for securely connecting to a remote machine and accessing files. -The performance of this option is also quite good. It may be slower than +The performance of this option is also quite good. It may be slower than the inline methods when you often open and close small files however. The cost of the cryptographic handshake at the start of an @command{scp} session can begin to absorb the advantage that the lack of encoding and @@ -787,7 +838,24 @@ All the @command{ssh} based methods support the kludgy @samp{-p} feature where you can specify a port number to connect to in the host name. For example, the host name @file{host#42} tells @value{tramp} to -specify @samp{-p 42} in the argument list for @command{ssh}. +specify @samp{-p 42} in the argument list for @command{ssh}, and to +specify @samp{-P 42} in the argument list for @command{scp}. + + +@item @option{sftp} --- @command{ssh} and @command{sftp} +@cindex method sftp +@cindex sftp method +@cindex sftp (with sftp method) +@cindex ssh (with sftp method) + +That is mostly the same method as @option{scp}, but using +@command{sftp} as transfer command. So the same remarks are valid. + +This command does not work like @value{ftppackagename}, where +@command{ftp} is called interactively, and all commands are send from +within this session. Instead of, @command{ssh} is used for login. + +This method supports the @samp{-p} hack. @item @option{rsync} --- @command{ssh} and @command{rsync} @@ -805,7 +873,7 @@ the file exists only on one side of the connection. The @command{rsync} based method may be considerably faster than the -@command{rcp} based methods when writing to the remote system. Reading +@command{rcp} based methods when writing to the remote system. Reading files to the local machine is no faster than with a direct copy. This method supports the @samp{-p} hack. @@ -866,7 +934,22 @@ @command{pscp} for transferring the files. These programs are part of PuTTY, an SSH implementation for Windows. -CCC: Does @command{plink} support the @samp{-p} hack? +This method supports the @samp{-P} hack. + + +@item @option{psftp} --- @command{plink} and @command{psftp} +@cindex method psftp +@cindex psftp method +@cindex psftp (with psftp method) +@cindex plink (with psftp method) +@cindex PuTTY (with psftp method) + +As you would expect, this method is similar to @option{sftp}, but it +uses the @command{plink} command to connect to the remote host, and it +uses @command{psftp} for transferring the files. These programs are +part of PuTTY, an SSH implementation for Windows. + +This method supports the @samp{-P} hack. @item @option{fcp} --- @command{fsh} and @command{fcp} @@ -901,7 +984,7 @@ @cindex method ftp @cindex ftp method -This is not a native @value{tramp} method. Instead of, it forwards all +This is not a native @value{tramp} method. Instead of, it forwards all requests to @value{ftppackagename}. @ifset xemacs This works only for unified filenames, see @ref{Issues}. @@ -935,8 +1018,15 @@ name, then a percent sign, then the domain name). So, to connect to the machine @code{melancholia} as user @code{daniel} of the domain @code{BIZARRE}, and edit @file{.emacs} in the home directory (share -@code{daniel$}) I would specify the filename -@file{@value{prefix}smb@value{postfixsinglehop}daniel%BIZARRE@@melancholia@value{postfix}/daniel$$/.emacs}. +@code{daniel$}) I would specify the filename @file{@trampfn{smb, +daniel%BIZARRE, melancholia, /daniel$$/.emacs}}. + +Depending on the Windows domain configuration, a Windows user might be +considered as domain user per default. In order to connect as local +user, the WINS name of that machine must be given as domain name. +Usually, it is the machine name in capital letters. In the example +above, the local user @code{daniel} would be specified as +@file{@trampfn{smb, daniel%MELANCHOLIA, melancholia, /daniel$$/.emacs}}. The domain name as well as the user name are optional. If no user name is specified at all, the anonymous user (without password @@ -953,97 +1043,56 @@ @end table -@node Multi-hop Methods -@section Connecting to a remote host using multiple hops -@cindex multi-hop methods -@cindex methods, multi-hop - -Sometimes, the methods described before are not sufficient. Sometimes, -it is not possible to connect to a remote host using a simple command. -For example, if you are in a secured network, you might have to log in -to a `bastion host' first before you can connect to the outside world. -Of course, the target host may also require a bastion host. The format -of multi-hop filenames is slightly different than the format of normal -@value{tramp} methods. - -@cindex method multi -@cindex multi method -A multi-hop file name specifies a method, a number of hops, and a -localname (path name on the remote system). The method name is always -@option{multi}. - -Each hop consists of a @dfn{hop method} specification, a user name and -a host name. The hop method can be an inline method only. The -following hop methods are (currently) available: - -@table @option -@item telnet -@cindex hop method telnet -@cindex telnet hop method - -Uses the well-known @command{telnet} program to connect to the host. -Whereas user name and host name are supplied in the file name, the -user is queried for the password. - -@item rsh -@cindex hop method rsh -@cindex rsh hop method - -This uses @command{rsh} to connect to the host. You do not need to -enter a password unless @command{rsh} explicitly asks for it. - -The variant @option{remsh} uses the @command{remsh} command. It -should be applied on machines where @command{remsh} is used instead of -@command{rsh}. - -@item ssh -@cindex hop method ssh -@cindex ssh hop method - -This uses @command{ssh} to connect to the host. You might have to enter -a password or a pass phrase. - -@item su -@cindex hop method su -@cindex su hop method - -This method does not actually contact a different host, but it allows -you to become a different user on the host you're currently on. This -might be useful if you want to edit files as root, but the remote host -does not allow remote root logins. In this case you can use -@option{telnet}, @option{rsh} or @option{ssh} to connect to the -remote host as a non-root user, then use an @option{su} hop to become -root. But @option{su} need not be the last hop in a sequence, you could -also use it somewhere in the middle, if the need arises. - -Even though you @emph{must} specify both user and host with an -@option{su} hop, the host name is ignored and only the user name is -used. - -@item sudo -@cindex hop method sudo -@cindex sudo hop method - -This is similar to the @option{su} hop, except that it uses -@command{sudo} rather than @command{su} to become a different user. + +@ifset emacsgw +@node Gateway methods +@section Gateway methods +@cindex methods, gateway +@cindex gateway methods + +Gateway methods are not methods to access a remote host directly. +These methods are intended to pass firewalls or proxy servers. +Therefore, they can be used for proxy host declarations +(@pxref{Multi-hops}) only. + +A gateway method must come always along with a method who supports +port setting (referred to as @samp{-p} kludge). This is because +@value{tramp} targets the accompanied method to +@file{localhost#random_port}, from where the firewall or proxy server +is accessed to. + +Gateway methods support user name and password declarations. These +are used to authenticate towards the corresponding firewall or proxy +server. They can be passed only if your friendly administrator has +granted your access. + +@table @asis +@item @option{tunnel} +@cindex method tunnel +@cindex tunnel method + +This method implements an HTTP tunnel via the @command{CONNECT} +command (see RFC 2616, 2817). Any HTTP 1.1 compliant (proxy) server +shall support this command. + +As authentication method, only @option{Basic Authentication} (see RFC +2617) is implemented so far. If no port number is given in the +declaration, port @option{8080} is used for the proxy server. + + +@item @option{socks} +@cindex method socks +@cindex socks method + +The @command{socks} method provides access to SOCKSv5 servers (see +RFC 1928). @option{Username/Password Authentication} according to RFC +1929 is supported. + +The default port number of the socks server is @option{1080}, if not +specified otherwise. @end table - -Some people might wish to use port forwarding with @command{ssh} or -maybe they have to use a nonstandard port. This can be accomplished -by putting a stanza in @file{~/.ssh/config} for the account which -specifies a different port number for a certain host name. But it can -also be accomplished within @value{tramp}, by adding a multi-hop method. -For example: - -@lisp -(add-to-list - 'tramp-multi-connection-function-alist - '("sshf" tramp-multi-connect-rlogin "ssh %h -l %u -p 4400%n")) -@end lisp - -Now you can use an @option{sshf} hop which connects to port 4400 instead of -the standard port. +@end ifset @node Default Method @@ -1085,7 +1134,6 @@ @xref{Inline methods}. @xref{External transfer methods}. -@xref{Multi-hop Methods}. Another consideration with the selection of transfer methods is the environment you will use them in and, especially when used over the @@ -1098,7 +1146,7 @@ If you need to connect to remote systems that are accessible from the Internet, you should give serious thought to using @option{ssh} based -methods to connect. These provide a much higher level of security, +methods to connect. These provide a much higher level of security, making it a non-trivial exercise for someone to obtain your password or read the content of the files you are editing. @@ -1119,9 +1167,9 @@ I guess that these days, most people can access a remote machine by using @command{ssh}. So I suggest that you use the @option{ssh} -method. So, type @kbd{C-x C-f -@value{prefix}ssh@value{postfixsinglehop}root@@otherhost@value{postfix}/etc/motd -@key{RET}} to edit the @file{/etc/motd} file on the other host. +method. So, type @kbd{C-x C-f @trampfn{ssh, root, otherhost, +/etc/motd} @key{RET}} to edit the @file{/etc/motd} file on the other +host. If you can't use @option{ssh} to log in to the remote host, then select a method that uses a program that works. For instance, Windows @@ -1132,9 +1180,9 @@ For the special case of editing files on the local host as another user, see the @option{su} or @option{sudo} methods. They offer shortened syntax for the @samp{root} account, like -@file{@value{prefix}su@value{postfixsinglehop}@value{postfix}/etc/motd}. - -People who edit large files may want to consider @option{scp} instead +@file{@trampfnmhl{su, , /etc/motd}}. + +People who edit large files may want to consider @option{scpc} instead of @option{ssh}, or @option{pscp} instead of @option{plink}. These out-of-band methods are faster than inline methods for large files. Note, however, that out-of-band methods suffer from some limitations. @@ -1143,6 +1191,205 @@ methods are fast enough. +@node Default User +@section Selecting a default user +@cindex default user + +The user part of a @value{tramp} file name can be omitted. Usually, +it is replaced by the user name you are logged in. Often, this is not +what you want. A typical use of @value{tramp} might be to edit some +files with root permissions on the local host. This case, you should +set the variable @code{tramp-default-user} to reflect that choice. +For example: + +@lisp +(setq tramp-default-user "root") +@end lisp + +@code{tramp-default-user} is regarded as obsolete, and will be removed +soon. + +@vindex tramp-default-user-alist +You can also specify different users for certain method/host +combinations, via the variable @code{tramp-default-user-alist}. For +example, if you always have to use the user @samp{john} in the domain +@samp{somewhere.else}, you can specify the following: + +@lisp +(add-to-list 'tramp-default-user-alist + '("ssh" ".*\\.somewhere\\.else\\'" "john")) +@end lisp + +@noindent +See the documentation for the variable +@code{tramp-default-user-alist} for more details. + +One trap to fall in must be known. If @value{tramp} finds a default +user, this user will be passed always to the connection command as +parameter (for example @samp{ssh here.somewhere.else -l john}. If you +have specified another user for your command in its configuration +files, @value{tramp} cannot know it, and the remote access will fail. +If you have specified in the given example in @file{~/.ssh/config} the +lines + +@example +Host here.somewhere.else + User lily +@end example + +@noindent +than you must discard selecting a default user by @value{tramp}. This +will be done by setting it to @code{nil} (or @samp{lily}, likewise): + +@lisp +(add-to-list 'tramp-default-user-alist + '("ssh" "\\`here\\.somewhere\\.else\\'" nil)) +@end lisp + +The last entry in @code{tramp-default-user-alist} could be your +default user you'll apply predominantly. You shall @emph{append} it +to that list at the end: + +@lisp +(add-to-list 'tramp-default-user-alist '(nil nil "jonas") t) +@end lisp + + +@node Default Host +@section Selecting a default host +@cindex default host + +@vindex tramp-default-host +Finally, it is even possible to omit the host name part of a +@value{tramp} file name. This case, the value of the variable +@code{tramp-default-host} is used. Per default, it is initialized +with the host name your local @value{emacsname} is running. + +If you, for example, use @value{tramp} mainly to contact the host +@samp{target} as user @samp{john}, you can specify: + +@lisp +(setq tramp-default-user "john" + tramp-default-host "target") +@end lisp + +Then the simple file name @samp{@trampfnmhl{ssh,,}} will connect you +to John's home directory on target. +@ifset emacs +Note, however, that the most simplification @samp{@trampfnmhl{,,}} +won't work, because @samp{/:} is the prefix for quoted file names. +@end ifset + + +@node Multi-hops +@section Connecting to a remote host using multiple hops +@cindex multi-hop +@cindex proxy hosts + +Sometimes, the methods described before are not sufficient. Sometimes, +it is not possible to connect to a remote host using a simple command. +For example, if you are in a secured network, you might have to log in +to a `bastion host' first before you can connect to the outside world. +Of course, the target host may also require a bastion host. + +@vindex tramp-default-proxies-alist +In order to specify such multiple hops, it is possible to define a proxy +host to pass through, via the variable +@code{tramp-default-proxies-alist}. This variable keeps a list of +triples (@var{host} @var{user} @var{proxy}). + + The first matching item specifies the proxy host to be passed for a +file name located on a remote target matching @var{user}@@@var{host}. +@var{host} and @var{user} are regular expressions or @code{nil}, which +is interpreted as a regular expression which always matches. + +@var{proxy} must be a Tramp filename which localname part is ignored. +Method and user name on @var{proxy} are optional, which is interpreted +with the default values. +@ifset emacsgw +The method must be an inline or gateway method (@pxref{Inline +methods}, @pxref{Gateway methods}). +@end ifset +@ifclear emacsgw +The method must be an inline method (@pxref{Inline methods}). +@end ifclear +If @var{proxy} is @code{nil}, no additional hop is required reaching +@var{user}@@@var{host}. + +If you, for example, must pass the host @samp{bastion.your.domain} as +user @samp{bird} for any remote host which is not located in your local +domain, you can set + +@lisp +(add-to-list 'tramp-default-proxies-alist + '("\\." nil "@trampfn{ssh, bird, bastion.your.domain,}")) +(add-to-list 'tramp-default-proxies-alist + '("\\.your\\.domain\\'" nil nil)) +@end lisp + +Please note the order of the code. @code{add-to-list} adds elements at the +beginning of a list. Therefore, most relevant rules must be added last. + +Proxy hosts can be cascaded. If there is another host called +@samp{jump.your.domain}, which is the only one in your local domain who +is allowed connecting @samp{bastion.your.domain}, you can add another +rule: + +@lisp +(add-to-list 'tramp-default-proxies-alist + '("\\`bastion\\.your\\.domain\\'" + "\\`bird\\'" + "@trampfnmhl{ssh, jump.your.domain,}")) +@end lisp + +@var{proxy} can contain the patterns @code{%h} or @code{%u}. These +patterns are replaced by the strings matching @var{host} or +@var{user}, respectively. + +If you, for example, wants to work as @samp{root} on hosts in the +domain @samp{your.domain}, but login as @samp{root} is disabled for +non-local access, you might add the following rule: + +@lisp +(add-to-list 'tramp-default-proxies-alist + '("\\.your\\.domain\\'" "\\`root\\'" "@trampfnmhl{ssh, %h,}")) +@end lisp + +Opening @file{@trampfnmhl{sudo, randomhost.your.domain,}} would +connect first @samp{randomhost.your.domain} via @code{ssh} under your +account name, and perform @code{sudo -u root} on that host afterwards. +It is important to know that the given method is applied on the host +which has been reached so far. @code{sudo -u root}, applied on your +local host, wouldn't be useful here. + +This is the recommended configuration to work as @samp{root} on remote +Ubuntu hosts. + +@ifset emacsgw +Finally, @code{tramp-default-proxies-alist} can be used to pass +firewalls or proxy servers. Imagine your local network has a host +@samp{proxy.your.domain} which is used on port 3128 as HTTP proxy to +the outer world. Your friendly administrator has granted you access +under your user name to @samp{host.other.domain} on that proxy +server.@footnote{HTTP tunnels are intended for secure SSL/TLS +communication. Therefore, many proxy server restrict the tunnels to +related target ports. You might need to run your ssh server on your +target host @samp{host.other.domain} on such a port, like 443 (https). +See @uref{http://savannah.gnu.org/maintenance/CvsFromBehindFirewall} +for discussion of ethical issues.} You would need to add the +following rule: + +@lisp +(add-to-list 'tramp-default-proxies-alist + '("\\`host\\.other\\.domain\\'" nil + "@trampfnmhl{tunnel, proxy.your.domain#3128,}")) +@end lisp + +Gateway methods can be declared as first hop only in a multiple hop +chain. +@end ifset + + @node Customizing Methods @section Using Non-Standard Methods @cindex customizing methods @@ -1263,8 +1510,8 @@ the following conventions: @defun my-tramp-parse file -@var{file} must be either a file name on your host, or @code{nil}. The -function must return a list of (@var{user} @var{host}), which are +@var{file} must be either a file name on your host, or @code{nil}. +The function must return a list of (@var{user} @var{host}), which are taken as candidates for user and host name completion. Example: @@ -1309,16 +1556,44 @@ @code{password-cache} (setting it to @code{nil}). Implementation Note: password caching is based on the package -password.el in No Gnus. For the time being, it is activated only when -this package is seen in the @code{load-path} while loading @value{tramp}. +@file{password.el} in No Gnus. For the time being, it is activated +only when this package is seen in the @code{load-path} while loading +@value{tramp}. @ifset installchapter -If you don't use No Gnus, you can take password.el from the @value{tramp} -@file{contrib} directory, see @ref{Installation parameters}. +If you don't use No Gnus, you can take @file{password.el} from the +@value{tramp} @file{contrib} directory, see @ref{Installation +parameters}. @end ifset It will be activated mandatory once No Gnus has found its way into @value{emacsname}. +@node Connection caching +@section Reusing connection related information. +@cindex caching + +@vindex tramp-persistency-file-name +In order to reduce initial connection time, @value{tramp} stores +connection related information persistently. The variable +@code{tramp-persistency-file-name} keeps the file name where these +information are written. Its default value is +@ifset emacs +@file{~/.emacs.d/tramp}. +@end ifset +@ifset xemacs +@file{~/.xemacs/tramp}. +@end ifset +It is recommended to choose a local file name. + +@value{tramp} reads this file during startup, and writes it when +exiting @value{emacsname}. You can simply remove this file if +@value{tramp} shall be urged to recompute these information next +@value{emacsname} startup time. + +Using such persistent information can be disabled by setting +@code{tramp-persistency-file-name} to @code{nil}. + + @node Remote Programs @section How @value{tramp} finds and uses programs on the remote machine. @@ -1327,25 +1602,32 @@ @command{cat}. In addition to these required tools, there are various tools that may be -required based on the connection method. See @ref{Inline methods} and +required based on the connection method. See @ref{Inline methods} and @ref{External transfer methods} for details on these. Certain other tools, such as @command{perl} (or @command{perl5}) and -@command{grep} will be used if they can be found. When they are +@command{grep} will be used if they can be found. When they are available, they are used to improve the performance and accuracy of remote file access. @vindex tramp-remote-path When @value{tramp} connects to the remote machine, it searches for the -programs that it can use. The variable @var{tramp-remote-path} controls -the directories searched on the remote machine. +programs that it can use. The variable @code{tramp-remote-path} +controls the directories searched on the remote machine. By default, this is set to a reasonable set of defaults for most -machines. It is possible, however, that your local (or remote ;) system +machines. The symbol @code{tramp-default-remote-path} is a place +holder, it is replaced by the list of directories received via the +command @command{getconf PATH} on your remote machine. For example, +on GNU Debian this is @file{/bin:/usr/bin}, whereas on Solaris this is +@file{/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin}. It is +recommended to apply this symbol on top of @code{tramp-remote-path}. + +It is possible, however, that your local (or remote ;) system administrator has put the tools you want in some obscure local directory. -In this case, you can still use them with @value{tramp}. You simply need to +In this case, you can still use them with @value{tramp}. You simply need to add code to your @file{.emacs} to add the directory to the remote path. This will then be searched by @value{tramp} when you connect and the software found. @@ -1433,20 +1715,37 @@ Some people invoke the @command{tset} program from their shell startup scripts which asks the user about the terminal type of the shell. -Maybe some shells ask other questions when they are started. @value{tramp} -does not know how to answer these questions. There are two approaches -for dealing with this problem. One approach is to take care that the -shell does not ask any questions when invoked from @value{tramp}. You can -do this by checking the @code{TERM} environment variable, it will be -set to @code{dumb} when connecting. +Maybe some shells ask other questions when they are started. +@value{tramp} does not know how to answer these questions. There are +two approaches for dealing with this problem. One approach is to take +care that the shell does not ask any questions when invoked from +@value{tramp}. You can do this by checking the @code{TERM} +environment variable, it will be set to @code{dumb} when connecting. @vindex tramp-terminal-type The variable @code{tramp-terminal-type} can be used to change this value to @code{dumb}. +@vindex tramp-actions-before-shell The other approach is to teach @value{tramp} about these questions. See -the variables @code{tramp-actions-before-shell} and -@code{tramp-multi-actions} (for multi-hop connections). +the variable @code{tramp-actions-before-shell}. Example: + +@lisp +(defconst my-tramp-prompt-regexp + (concat (regexp-opt '("Enter the birth date of your mother:") t) + "\\s-*") + "Regular expression matching my login prompt question.") + +(defun my-tramp-action (proc vec) + "Enter \"19000101\" in order to give a correct answer." + (save-window-excursion + (with-current-buffer (tramp-get-connection-buffer vec) + (tramp-message vec 6 "\n%s" (buffer-string)) + (tramp-send-string vec "19000101")))) + +(add-to-list 'tramp-actions-before-shell + '(my-tramp-prompt-regexp my-tramp-action)) +@end lisp @item Environment variables named like users in @file{.profile} @@ -1484,10 +1783,10 @@ What can you do about this? -Well, one possibility is to make sure that everything in @file{~/.shrc} -and @file{~/.profile} on all remote hosts is Bourne-compatible. In the -above example, instead of @command{export FOO=bar}, you might use -@command{FOO=bar; export FOO} instead. +Well, one possibility is to make sure that everything in +@file{~/.shrc} and @file{~/.profile} on all remote hosts is +Bourne-compatible. In the above example, instead of @command{export +FOO=bar}, you might use @command{FOO=bar; export FOO} instead. The other possibility is to put your non-Bourne shell setup into some other files. For example, bash reads the file @file{~/.bash_profile} @@ -1528,13 +1827,13 @@ @ifset xemacs @code{bkup-backup-directory-info}. @end ifset -In connection with @value{tramp}, this can have unexpected side effects. -Suppose that you specify that all backups should go to the directory -@file{~/.emacs.d/backups/}, and then you edit the file -@file{@value{prefix}su@value{postfixsinglehop}root@@localhost@value{postfix}/etc/secretfile}. -The effect is that the backup file will be owned by you and not by -root, thus possibly enabling others to see it even if they were not -intended to see it. +In connection with @value{tramp}, this can have unexpected side +effects. Suppose that you specify that all backups should go to the +directory @file{~/.emacs.d/backups/}, and then you edit the file +@file{@trampfn{su, root, localhost, /etc/secretfile}}. The effect is +that the backup file will be owned by you and not by root, thus +possibly enabling others to see it even if they were not intended to +see it. When @ifset emacs @@ -1608,14 +1907,15 @@ @end ifset @noindent -The backup file name of -@file{@value{prefix}su@value{postfixsinglehop}root@@localhost@value{postfix}/etc/secretfile} -would be +The backup file name of @file{@trampfn{su, root, localhost, +/etc/secretfile}} would be @ifset emacs -@file{@value{prefix}su@value{postfixsinglehop}root@@localhost@value{postfix}~/.emacs.d/backups/!su:root@@localhost:!etc!secretfile~} +@file{@trampfn{su, root, localhost, +~/.emacs.d/backups/!su:root@@localhost:!etc!secretfile~}} @end ifset @ifset xemacs -@file{@value{prefix}su@value{postfixsinglehop}root@@localhost@value{postfix}~/.emacs.d/backups/![su!root@@localhost]!etc!secretfile~} +@file{@trampfn{su, root, localhost, +~/.emacs.d/backups/![su!root@@localhost]!etc!secretfile~}} @end ifset The same problem can happen with auto-saving files. @@ -1669,8 +1969,8 @@ If you wish to use the @option{scpx} connection method, then you might have the problem that @value{emacsname} calls @command{scp} with a Windows filename such as @code{c:/foo}. The Cygwin version of -@command{scp} does not know about Windows filenames and interprets this -as a remote filename on the host @code{c}. +@command{scp} does not know about Windows filenames and interprets +this as a remote filename on the host @code{c}. One possible workaround is to write a wrapper script for @option{scp} which converts the Windows filename to a Cygwinized filename. @@ -1696,9 +1996,9 @@ @chapter Using @value{tramp} @cindex using @value{tramp} -Once you have installed @value{tramp} it will operate fairly transparently. You -will be able to access files on any remote machine that you can log in -to as though they were local. +Once you have installed @value{tramp} it will operate fairly +transparently. You will be able to access files on any remote machine +that you can log in to as though they were local. Files are specified to @value{tramp} using a formalized syntax specifying the details of the system to connect to. This is similar to the syntax used @@ -1717,10 +2017,9 @@ @menu * Filename Syntax:: @value{tramp} filename conventions. -* Multi-hop filename syntax:: Multi-hop filename conventions. +* Alternative Syntax:: URL-like filename syntax. * Filename completion:: Filename completion. -* Dired:: Dired. -* Compilation:: Compile remote files. +* Remote processes:: Integration with other @value{emacsname} packages. @end menu @@ -1729,123 +2028,107 @@ @cindex filename syntax @cindex filename examples -To access the file @var{localname} on the remote machine @var{machine} you -would specify the filename -@file{@value{prefix}@var{machine}@value{postfix}@var{localname}}. -This will connect to @var{machine} and transfer the file using the -default method. @xref{Default Method}. +To access the file @var{localname} on the remote machine @var{machine} +you would specify the filename @file{@trampfnhl{@var{machine}, +@var{localname}}}. This will connect to @var{machine} and transfer +the file using the default method. @xref{Default Method}. Some examples of @value{tramp} filenames are shown below. @table @file -@item @value{prefix}melancholia@value{postfix}.emacs +@item @trampfnhl{melancholia, .emacs} Edit the file @file{.emacs} in your home directory on the machine @code{melancholia}. -@item @value{prefix}melancholia.danann.net@value{postfix}.emacs +@item @trampfnhl{melancholia.danann.net, .emacs} This edits the same file, using the fully qualified domain name of the machine. -@item @value{prefix}melancholia@value{postfix}~/.emacs +@item @trampfnhl{melancholia, ~/.emacs} This also edits the same file --- the @file{~} is expanded to your home directory on the remote machine, just like it is locally. -@item @value{prefix}melancholia@value{postfix}~daniel/.emacs +@item @trampfnhl{melancholia, ~daniel/.emacs} This edits the file @file{.emacs} in the home directory of the user -@code{daniel} on the machine @code{melancholia}. The @file{~} +@code{daniel} on the machine @code{melancholia}. The @file{~} construct is expanded to the home directory of that user on the remote machine. -@item @value{prefix}melancholia@value{postfix}/etc/squid.conf +@item @trampfnhl{melancholia, /etc/squid.conf} This edits the file @file{/etc/squid.conf} on the machine @code{melancholia}. @end table Unless you specify a different name to use, @value{tramp} will use the -current local user name as the remote user name to log in with. If you +current local user name as the remote user name to log in with. If you need to log in as a different user, you can specify the user name as part of the filename. To log in to the remote machine as a specific user, you use the syntax -@file{@value{prefix}@var{user}@@@var{machine}@value{postfix}/@var{path/to.file}}. +@file{@trampfnuhl{@var{user}, @var{machine}, @var{path/to.file}}}. That means that connecting to @code{melancholia} as @code{daniel} and editing @file{.emacs} in your home directory you would specify -@file{@value{prefix}daniel@@melancholia@value{postfix}.emacs}. +@file{@trampfnuhl{daniel, melancholia, .emacs}}. It is also possible to specify other file transfer methods (@pxref{Default Method}) as part of the filename. @ifset emacs This is done by putting the method before the user and host name, as -in -@file{@value{prefix}@var{method}@value{postfixsinglehop}} -(Note the trailing colon). +in @file{@value{prefix}@var{method}@value{postfixhop}} (Note the +trailing colon). @end ifset @ifset xemacs -This is done by replacing the initial -@file{@value{prefix}} with -@file{@value{prefix}@value{postfixsinglehop}}. -(Note the trailing slash!). +This is done by replacing the initial @file{@value{prefix}} with +@file{@value{prefix}@value{postfixhop}}. (Note the trailing +slash!). @end ifset The user, machine and file specification remain the same. So, to connect to the machine @code{melancholia} as @code{daniel}, -using the @option{ssh} method to transfer files, and edit @file{.emacs} -in my home directory I would specify the filename -@file{@value{prefix}ssh@value{postfixsinglehop}daniel@@melancholia@value{postfix}.emacs}. - - -@node Multi-hop filename syntax -@section Multi-hop filename conventions -@cindex filename syntax for multi-hop files -@cindex multi-hop filename syntax - -The syntax of multi-hop file names is necessarily slightly different -than the syntax of other @value{tramp} file names. Here's an example -multi-hop file name: - -@example -@value{prefix}multi@value{postfixsinglehop}rsh@value{postfixmultihop}out@@gate@value{postfixsinglehop}telnet@value{postfixmultihop}kai@@real.host@value{postfix}/path/to.file -@end example - -This is quite a mouthful. So let's go through it step by step. The -file name consists of three parts. +using the @option{ssh} method to transfer files, and edit +@file{.emacs} in my home directory I would specify the filename +@file{@trampfn{ssh, daniel, melancholia, .emacs}}. + + +@node Alternative Syntax +@section URL-like filename syntax +@cindex filename syntax +@cindex filename examples + +Additionally to the syntax described in the previous chapter, it is +possible to use a URL-like syntax for @value{tramp}. This can be +switched on by customizing the variable @code{tramp-syntax}. Please +note that this feature is experimental for the time being. + +The variable @code{tramp-syntax} must be set before requiring @value{tramp}: + +@lisp +(setq tramp-syntax 'url) +(require 'tramp) +@end lisp + +Then, a @value{tramp} filename would look like this: +@file{/@var{method}://@var{user}@@@var{machine}:@var{port}/@var{path/to.file}}. +@file{/@var{method}://} is mandatory, all other parts are optional. +@file{:@var{port}} is useful for methods only who support this. + +The last example from the previous section would look like this: +@file{/ssh://daniel@@melancholia/.emacs}. + +For the time being, @code{tramp-syntax} can have the following values: + +@itemize @w{} @ifset emacs -The parts are separated by colons +@item @code{ftp} -- That is the default syntax +@item @code{url} -- URL-like syntax @end ifset @ifset xemacs -The parts are separated by slashes and square brackets. +@item @code{sep} -- That is the default syntax +@item @code{url} -- URL-like syntax +@item @code{ftp} -- EFS-like syntax @end ifset -The first part is @file{@value{prefix}multi}, the method -specification. The second part is -@file{rsh@value{postfixmultihop}out@@gate@value{postfixsinglehop}telnet@value{postfixmultihop}kai@@real.host} -and specifies the hops. The final part is @file{/path/to.file} and -specifies the file name on the remote host. - -The first part and the final part should be clear. See @ref{Multi-hop -Methods}, for a list of alternatives for the method specification. - -The second part can be subdivided again into components, so-called -hops. In the above file name, there are two hops, -@file{rsh@value{postfixmultihop}out@@gate} and -@file{telnet@value{postfixmultihop}kai@@real.host}. - -Each hop can @emph{again} be subdivided into (three) components, the -@dfn{hop method}, the @dfn{user name} and the @dfn{host name}. The -meaning of the second and third component should be clear, and the hop -method says what program to use to perform that hop. - -The first hop, @file{rsh@value{postfixmultihop}out@@gate}, -says to use @command{rsh} to log in as user @code{out} to the host -@code{gate}. Starting at that host, the second hop, -@file{telnet@value{postfixmultihop}kai@@real.host}, says to -use @command{telnet} to log in as user @code{kai} to host -@code{real.host}. - -@xref{Multi-hop Methods}, for a list of possible hop method values. -The variable @code{tramp-multi-connection-function-alist} contains the -list of possible hop methods and information on how to execute them, -should you want to add your own. +@end itemize @node Filename completion @@ -1853,10 +2136,20 @@ @cindex filename completion Filename completion works with @value{tramp} for completion of method -names, of user names and of machine names (except multi-hop methods) -as well as for completion of file names on remote machines. +names, of user names and of machine names as well as for completion of +file names on remote machines. @ifset emacs -In order to enable this, Partial Completion mode must be set on. +In order to enable this, Partial Completion mode must be set +on@footnote{If you don't use Partial Completion mode, but want to +keep full completion, load @value{tramp} like this in your +@file{.emacs}: + +@lisp +;; Preserve Tramp's completion features. +(let ((partial-completion-mode t)) + (require 'tramp)) +@end lisp +}. @ifinfo @xref{Completion Options, , , @value{emacsdir}}. @end ifinfo @@ -1867,85 +2160,184 @@ @example @ifset emacs -@value{prefixsinglehop}telnet@value{postfixsinglehop} tmp/ -@value{prefixsinglehop}toto@value{postfix} +@value{prefixhop}telnet@value{postfixhop} tmp/ +@value{prefixhop}toto@value{postfix} @end ifset @ifset xemacs -@value{prefixsinglehop}telnet@value{postfixsinglehop} @value{prefixsinglehop}toto@value{postfix} +@value{prefixhop}telnet@value{postfixhop} @value{prefixhop}toto@value{postfix} @end ifset @end example -@samp{@value{prefixsinglehop}telnet@value{postfixsinglehop}} +@samp{@value{prefixhop}telnet@value{postfixhop}} is a possible completion for the respective method, @ifset emacs @samp{tmp/} stands for the directory @file{/tmp} on your local machine, @end ifset -and @samp{@value{prefixsinglehop}toto@value{postfix}} +and @samp{@value{prefixhop}toto@value{postfix}} might be a host @value{tramp} has detected in your @file{~/.ssh/known_hosts} file (given you're using default method @option{ssh}). If you go on to type @kbd{e @key{TAB}}, the minibuffer is completed to -@samp{@value{prefix}telnet@value{postfixsinglehop}}. +@samp{@value{prefix}telnet@value{postfixhop}}. Next @kbd{@key{TAB}} brings you all machine names @value{tramp} detects in your @file{/etc/hosts} file, let's say @example -@value{prefixsinglehop}telnet@value{postfixsinglehop}127.0.0.1@value{postfix} @value{prefixsinglehop}telnet@value{postfixsinglehop}192.168.0.1@value{postfix} -@value{prefixsinglehop}telnet@value{postfixsinglehop}localhost@value{postfix} @value{prefixsinglehop}telnet@value{postfixsinglehop}melancholia.danann.net@value{postfix} -@value{prefixsinglehop}telnet@value{postfixsinglehop}melancholia@value{postfix} +@trampfnmhl{telnet,127.0.0.1,} @trampfnmhl{telnet,192.168.0.1,} +@trampfnmhl{telnet,localhost,} @trampfnmhl{telnet,melancholia.danann.net,} +@trampfnmhl{telnet,melancholia,} @end example Now you can choose the desired machine, and you can continue to complete file names on that machine. -As filename completion needs to fetch the listing of files from the -remote machine, this feature is sometimes fairly slow. As @value{tramp} -does not yet cache the results of directory listing, there is no gain -in performance the second time you complete filenames. - If the configuration files (@pxref{Customizing Completion}), which @value{tramp} uses for analysis of completion, offer user names, those user names will be taken into account as well. - -@node Dired -@section Dired -@cindex dired - -@value{tramp} works transparently with dired, enabling you to use this powerful -file management tool to manage files on any machine you have access to -over the Internet. - -If you need to browse a directory tree, Dired is a better choice, at -present, than filename completion. Dired has its own cache mechanism -and will only fetch the directory listing once. - - -@node Compilation -@section Compile remote files +Remote machines, which have been visited in the past and kept +persistently (@pxref{Connection caching}), will be offered too. + +Once the remote machine identification is completed, it comes to +filename completion on the remote host. This works pretty much like +for files on the local host, with the exception that minibuffer +killing via a double-slash works only on the filename part, except +that filename part starts with @file{//}. +@ifinfo +@xref{Minibuffer File, , , @value{emacsdir}}. +@end ifinfo + +@ifset emacs +As example, @kbd{@trampfnmhl{telnet,melancholia,/usr/local/bin//etc} +@key{TAB}} would result in +@file{@trampfnmhl{telnet,melancholia,/etc}}, whereas +@kbd{@trampfnmhl{telnet,melancholia,//etc} @key{TAB}} reduces the +minibuffer contents to @file{/etc}. A triple-slash stands for the +default behaviour, +i.e. @kbd{@trampfnmhl{telnet,melancholia,/usr/local/bin///etc} +@key{TAB}} expands directly to @file{/etc}. +@end ifset + +@ifset xemacs +As example, @kbd{@trampfnmhl{telnet,melancholia,/usr/local/bin//}} +would result in @file{@trampfnmhl{telnet,melancholia,/}}, whereas +@kbd{@trampfnmhl{telnet,melancholia,//}} expands the minibuffer +contents to @file{/}. +@end ifset + + +@node Remote processes +@section Integration with other @value{emacsname} packages. @cindex compile @cindex recompile -@value{tramp} provides commands for compilation of files on remote -machines. In order to get them loaded, you need to require -@file{tramp-util.el}: +@value{tramp} supports running processes on a remote host. This +allows to exploit @value{emacsname} packages without modification for +remote file names. It does not work for the @option{ftp} and +@option{smb} methods. + +Remote processes are started when a corresponding command is executed +from a buffer belonging to a remote file or directory. Up to now, the +packages @file{compile.el} (commands like @code{compile} and +@code{grep}) and @file{gud.el} (@code{gdb} or @code{perldb}) have been +integrated. Integration of further packages is planned, any help for +this is welcome! + +When your program is not found in the default search path +@value{tramp} sets on the remote machine, you should either use an +absolute path, or extend @code{tramp-remote-path} (see @ref{Remote +Programs}): + +@lisp +(add-to-list 'tramp-remote-path "~/bin") +(add-to-list 'tramp-remote-path "/appli/pub/bin") +@end lisp + +The environment for your program can be adapted by customizing +@code{tramp-remote-process-environment}. This variable is a list of +strings. It is structured like @code{process-environment}. Each +element is a string of the form ENVVARNAME=VALUE. An entry +ENVVARNAME= disables the corresponding environment variable, which +might have been set in your init file like @file{~/.profile}. + +@noindent +Adding an entry can be performed via @code{add-to-list}: + +@lisp +(add-to-list 'tramp-remote-process-environment "JAVA_HOME=/opt/java") +@end lisp + +Changing or removing an existing entry is not encouraged. The default +values are chosen for proper @value{tramp} work. Nevertheless, if for +example a paranoid system administrator disallows changing the +@var{$HISTORY} environment variable, you can customize +@code{tramp-remote-process-environment}, or you can apply the +following code in your @file{.emacs}: @lisp -(require 'tramp-util) +(let ((process-environment tramp-remote-process-environment)) + (setenv "HISTORY" nil) + (setq tramp-remote-process-environment process-environment)) @end lisp -Afterwards, you can use the commands @code{tramp-compile} and -@code{tramp-recompile} instead of @code{compile} and @code{recompile}, -respectively; @inforef{Compilation, ,@value{emacsdir}}. This does not -work for the @option{ftp} and @option{smb} methods. - -The corresponding key bindings and menu entries calling these commands -are redefined automatically for buffers associated with remote files. - -After finishing the compilation, you can use the usual commands like -@code{previous-error}, @code{next-error} and @code{first-error} for -navigation in the @file{*Compilation*} buffer. +If you use other @value{emacsname} packages which do not run +out-of-the-box on a remote host, please let us know. We will try to +integrate them as well. @xref{Bug Reports}. + + +@subsection Running eshell on a remote host +@cindex eshell + +@value{tramp} is integrated into @file{eshell.el}. That is, you can +open an interactive shell on your remote host, and run commands there. +After you have started @code{eshell}, you could perform commands like +this: + +@example +@b{~ $} cd @trampfnmhl{sudo, , /etc} @key{RET} +@b{@trampfn{sudo, root, host, /etc} $} hostname @key{RET} +host +@b{@trampfn{sudo, root, host, /etc} $} id @key{RET} +uid=0(root) gid=0(root) groups=0(root) +@b{@trampfn{sudo, root, host, /etc} $} find-file shadow @key{RET} +# +@b{@trampfn{sudo, root, host, /etc} $} +@end example + + +@anchor{Running a debugger on a remote host} +@subsection Running a debugger on a remote host +@cindex gud +@cindex gdb +@cindex perldb + +@file{gud.el} offers an unified interface to several symbolic +debuggers +@ifset emacs +@ifinfo +(@ref{Debuggers, , , @value{emacsdir}}). +@end ifinfo +@end ifset +With @value{tramp}, it is possible to debug programs on +remote hosts. You can call @code{gdb} with a remote file name: + +@example +@kbd{M-x gdb @key{RET}} +@b{Run gdb (like this):} gdb --annotate=3 @trampfnmhl{ssh, host, ~/myprog} @key{RET} +@end example + +The file name can also be relative to a remote default directory. +Given you are in a buffer that belongs to the remote directory +@trampfnmhl{ssh, host, /home/user}, you could call + +@example +@kbd{M-x perldb @key{RET}} +@b{Run perldb (like this):} perl -d myprog.pl @key{RET} +@end example + +It is not possible to use just the absolute local part of a remote +file name, like @kbd{perl -d /home/user/myprog.pl}, though. @node Bug Reports @@ -1953,7 +2345,7 @@ @cindex bug reports Bugs and problems with @value{tramp} are actively worked on by the -development team. Feature requests and suggestions are also more than +development team. Feature requests and suggestions are also more than welcome. The @value{tramp} mailing list is a great place to get information on @@ -1964,16 +2356,16 @@ your message. The mailing list is at @email{tramp-devel@@gnu.org}. Messages sent to -this address go to all the subscribers. This is @emph{not} the address +this address go to all the subscribers. This is @emph{not} the address to send subscription requests to. Subscribing to the list is performed via @uref{http://lists.gnu.org/mailman/listinfo/tramp-devel/, the @value{tramp} Mail Subscription Page}. -To report a bug in @value{tramp}, you should execute @kbd{M-x tramp-bug}. This -will automatically generate a buffer with the details of your system and -@value{tramp} version. +To report a bug in @value{tramp}, you should execute @kbd{M-x +tramp-bug}. This will automatically generate a buffer with the details +of your system and @value{tramp} version. When submitting a bug report, please try to describe in excruciating detail the steps required to reproduce the problem, the setup of the @@ -1982,8 +2374,20 @@ Asked Questions}. If you can identify a minimal test case that reproduces the problem, -include that with your bug report. This will make it much easier for the -development team to analyze and correct the problem. +include that with your bug report. This will make it much easier for +the development team to analyze and correct the problem. + +Before reporting the bug, you should set the verbosity level to 6 +(@pxref{Traces and Profiles, Traces}) in the @file{~/.emacs} file and +repeat the bug. Then, include the contents of the @file{*tramp/foo*} +and @file{*debug tramp/foo*} buffers in your bug report. A verbosity +level greater than 6 will produce a very huge debug buffer, which is +mostly not necessary for the analysis. + +Please be aware that, with a verbosity level of 6 or greater, the +contents of files and directories will be included in the debug +buffer. Passwords you've typed will never be included there. + @node Frequently Asked Questions @chapter Frequently Asked Questions @@ -2009,10 +2413,9 @@ @item Which systems does it work on? -The package has been used successfully on GNU Emacs 20, GNU Emacs 21 -and GNU Emacs 22, as well as XEmacs 21. XEmacs 20 is more -problematic, see the notes in @file{tramp.el}. I don't think anybody -has really tried it on GNU Emacs 19. +The package has been used successfully on GNU Emacs 21, GNU Emacs 22 +and XEmacs 21 (starting with 21.4). Gateway methods are supported for +GNU Emacs 22 only. The package was intended to work on Unix, and it really expects a Unix-like system on the remote end (except the @option{smb} method), @@ -2023,13 +2426,41 @@ many thanks to Joe Stoy for providing the information: @uref{ftp://ftp.comlab.ox.ac.uk/tmp/Joe.Stoy/} -@c The link is broken. I've contacted Tom for clarification. Michael. +@c The link is broken. I've contacted Tom for clarification. Michael. @ignore The above mostly contains patches to old ssh versions; Tom Roche has a Web page with instructions: @uref{http://www4.ncsu.edu/~tlroche/plinkTramp.html} @end ignore +@item +How could I speed up @value{tramp}? + +In the backstage, @value{tramp} needs a lot of operations on the +remote host. The time for transferring data from and to the remote +host as well as the time needed to perform the operations there count. +In order to speed up @value{tramp}, one could either try to avoid some +of the operations, or one could try to improve their performance. + +Use an external transfer method, like @option{scpc}. + +Use caching. This is already enabled by default. Information about +the remote host as well as the remote files are cached for reuse. Th +information about remote hosts is kept in the file specified in +@code{tramp-persistency-file-name}. Keep this file. + +Disable version control. If you access remote files which are not +under version control, a lot of check operations can be avoided by +disabling VC. This can be achieved by + +@lisp +(setq vc-handled-backends nil) +@end lisp + +Disable excessive traces. The default trace level of @value{tramp}, +defined in the variable @code{tramp-verbose}, is 3. You should +increase this level only temporarily, hunting bugs. + @item @value{tramp} does not connect to the remote host @@ -2048,6 +2479,17 @@ should be avoided on the remote side. @xref{Remote shell setup}. for setting the regular expression detecting the prompt. +You can check your settings after an unsuccessful connection by +switching to the @value{tramp} connection buffer @file{*tramp/foo*}, +setting the cursor at the top of the buffer, and applying the expression + +@example +@kbd{M-: (re-search-forward (concat tramp-shell-prompt-pattern "$"))} +@end example + +If it fails, or the cursor is not moved at the end of the buffer, your +prompt is not recognised correctly. + A special problem is the zsh, which uses left-hand side and right-hand side prompts in parallel. Therefore, it is necessary to disable the zsh line editor on the remote host. You shall add to @file{~/.zshrc} @@ -2057,15 +2499,34 @@ [ $TERM = "dumb" ] && unsetopt zle && PS1='$ ' @end example + @item @value{tramp} doesn't transfer strings with more than 500 characters correctly On some few systems, the implementation of @code{process-send-string} -seems to be broken for longer strings. This case, you should -customize the variable @code{tramp-chunksize} to 500. For a -description how to determine whether this is necessary see the -documentation of @code{tramp-chunksize}. +seems to be broken for longer strings. It is reported for HP-UX, +FreeBSD and Tru64 Unix, for example. This case, you should customize +the variable @code{tramp-chunksize} to 500. For a description how to +determine whether this is necessary see the documentation of +@code{tramp-chunksize}. + +Additionally, it will be useful to set @code{file-precious-flag} to +@code{t} for @value{tramp} files. Then the file contents will be +written into a temporary file first, which is checked for correct +checksum. +@ifinfo +@pxref{Saving Buffers, , , elisp} +@end ifinfo + +@lisp +(add-hook + 'find-file-hooks + '(lambda () + (when (file-remote-p default-directory) + (set (make-local-variable 'file-precious-flag) t)))) +@end lisp + @end itemize @@ -2117,11 +2578,13 @@ " make tramp beep after writing a file." (interactive) (beep)) + (defadvice tramp-handle-do-copy-or-rename-file (after tramp-copy-beep-advice activate) " make tramp beep after copying a file." (interactive) (beep)) + (defadvice tramp-handle-insert-file-contents (after tramp-copy-beep-advice activate) " make tramp beep after copying a file." @@ -2130,6 +2593,60 @@ @end lisp +@ifset emacs +@item +I'ld like to see a host indication in the mode line when I'm remote + +The following code has been tested with @value{emacsname} 22. You +should put it into your @file{~/.emacs}: + +@lisp +(defconst my-mode-line-buffer-identification + (list + '(:eval + (let ((host-name + (if (file-remote-p default-directory) + (tramp-file-name-host + (tramp-dissect-file-name default-directory)) + (system-name)))) + (if (string-match "^[^0-9][^.]*\\(\\..*\\)" host-name) + (substring host-name 0 (match-beginning 1)) + host-name))) + ": %12b")) + +(setq-default + mode-line-buffer-identification + my-mode-line-buffer-identification) + +(add-hook + 'dired-mode-hook + '(lambda () + (setq + mode-line-buffer-identification + my-mode-line-buffer-identification))) +@end lisp +@end ifset + + +@ifset emacs +@item +My remote host does not understand default directory listing options + +@value{emacsname} computes the @command{dired} options depending on +the local host you are working. If your @command{ls} command on the +remote host does not understand those options, you can change them +like this: + +@lisp +(add-hook + 'dired-before-readin-hook + '(lambda () + (when (file-remote-p default-directory) + (setq dired-actual-switches "-al")))) +@end lisp +@end ifset + + @item There's this @file{~/.sh_history} file on the remote host which keeps growing and growing. What's that? @@ -2152,6 +2669,218 @@ @end example +@item There are longish file names to type. How to shorten this? + +Let's say you need regularly access to @file{@trampfn{ssh, news, +news.my.domain, /opt/news/etc}}, which is boring to type again and +again. The following approaches can be mixed: + +@enumerate + +@item Use default values for method and user name: + +You can define default methods and user names for hosts, +(@pxref{Default Method}, @pxref{Default User}): + +@lisp +(setq tramp-default-method "ssh" + tramp-default-user "news") +@end lisp + +The file name left to type would be +@kbd{C-x C-f @trampfnhl{news.my.domain, /opt/news/etc}}. + +Note, that there are some useful settings already. Accessing your +local host as @samp{root} user, is possible just by @kbd{C-x C-f +@trampfnmhl{su,,}}. + +@item Use configuration possibilities of your method: + +Several connection methods (i.e. the programs used) offer powerful +configuration possibilities (@pxref{Customizing Completion}). In the +given case, this could be @file{~/.ssh/config}: + +@example +Host xy + HostName news.my.domain + User news +@end example + +The file name left to type would be @kbd{C-x C-f @trampfnmhl{ssh, xy, +/opt/news/etc}}. Depending on files in your directories, it is even +possible to complete the hostname with @kbd{C-x C-f +@value{prefix}ssh@value{postfixhop}x @key{TAB}}. + +@item Use environment variables: + +File names typed in the minibuffer can be expanded by environment +variables. You can set them outside @value{emacsname}, or even with +Lisp: + +@lisp +(setenv "xy" "@trampfn{ssh, news, news.my.domain, /opt/news/etc/}") +@end lisp + +Then you need simply to type @kbd{C-x C-f $xy @key{RET}}, and here you +are. The disadvantage is, that you cannot edit the file name, because +environment variables are not expanded during editing in the +minibuffer. + +@item Define own keys: + +You can define your own key sequences in @value{emacsname}, which can +be used instead of @kbd{C-x C-f}: + +@lisp +(global-set-key + [(control x) (control y)] + (lambda () + (interactive) + (find-file + (read-file-name + "Find Tramp file: " + "@trampfn{ssh, news, news.my.domain, /opt/news/etc/}")))) +@end lisp + +Simply typing @kbd{C-x C-y} would initialize the minibuffer for +editing with your beloved file name. + +See also @uref{http://www.emacswiki.org/cgi-bin/wiki/TrampMode, the +Emacs Wiki} for a more comprehensive example. + +@item Define own abbreviation (1): + +It is possible to define an own abbreviation list for expanding file +names: + +@lisp +(add-to-list + 'directory-abbrev-alist + '("^/xy" . "@trampfn{ssh, news, news.my.domain, /opt/news/etc/}")) +@end lisp + +This shortens the file openening command to @kbd{C-x C-f /xy +@key{RET}}. The disadvantage is, again, that you cannot edit the file +name, because the expansion happens after entering the file name only. + +@item Define own abbreviation (2): + +The @code{abbrev-mode} gives more flexibility for editing the +minibuffer: + +@lisp +(define-abbrev-table 'my-tramp-abbrev-table + '(("xy" "@trampfn{ssh, news, news.my.domain, /opt/news/etc/}"))) + +(add-hook + 'minibuffer-setup-hook + '(lambda () + (abbrev-mode 1) + (setq local-abbrev-table my-tramp-abbrev-table))) + +(defadvice minibuffer-complete + (before my-minibuffer-complete activate) + (expand-abbrev)) + +;; If you use partial-completion-mode +(defadvice PC-do-completion + (before my-PC-do-completion activate) + (expand-abbrev)) +@end lisp + +After entering @kbd{C-x C-f xy @key{TAB}}, the minibuffer is +expanded, and you can continue editing. + +@item Use bookmarks: + +Bookmarks can be used to visit Tramp files or directories. +@ifinfo +@pxref{Bookmarks, , , @value{emacsdir}} +@end ifinfo + +When you have opened @file{@trampfn{ssh, news, news.my.domain, +/opt/news/etc/}}, you should save the bookmark via +@ifset emacs +@kbd{@key{menu-bar} @key{edit} @key{bookmarks} @key{set}}. +@end ifset +@ifset xemacs +@kbd{@key{menu-bar} @key{view} @key{bookmarks} @key{set}}. +@end ifset + +Later on, you can always navigate to that bookmark via +@ifset emacs +@kbd{@key{menu-bar} @key{edit} @key{bookmarks} @key{jump}}. +@end ifset +@ifset xemacs +@kbd{@key{menu-bar} @key{view} @key{bookmarks} @key{jump}}. +@end ifset + +@item Use recent files: + +@ifset emacs +@file{recentf} +@end ifset +@ifset xemacs +@file{recent-files} +@end ifset +remembers visited places. +@ifinfo +@ifset emacs +@pxref{File Conveniences, , , @value{emacsdir}} +@end ifset +@ifset xemacs +@pxref{recent-files, , , edit-utils} +@end ifset +@end ifinfo + +You could keep remote file names in the recent list without checking +their readability through a remote access: + +@lisp +@ifset emacs +(require 'recentf) +(add-to-list 'recentf-keep 'file-remote-p) +(recentf-mode 1) +@end ifset +@ifset xemacs +(recent-files-initialize) +(add-hook + 'find-file-hooks + (lambda () + (when (file-remote-p (buffer-file-name)) + (recent-files-make-permanent))) + 'append) +@end ifset +@end lisp + +The list of files opened recently is reachable via +@ifset emacs +@kbd{@key{menu-bar} @key{file} @key{Open Recent}}. +@end ifset +@ifset xemacs +@kbd{@key{menu-bar} @key{Recent Files}}. +@end ifset + +@ifset emacs +@item Use filecache: + +@file{filecache} remembers visited places. Add the directory into +the cache: + +@lisp +(eval-after-load "filecache" + '(file-cache-add-directory + "@trampfn{ssh, news, news.my.domain, /opt/news/etc/}")) +@end lisp + +Whenever you want to load a file, you can enter @kbd{C-x C-f +C-@key{TAB}} in the minibuffer. The completion is done for the given +directory. +@end ifset + +@end enumerate + + @item How can I disable @value{tramp}? @@ -2180,12 +2909,12 @@ @cindex Version Control Unlike @value{ftppackagename}, @value{tramp} has full shell access to the -remote machine. This makes it possible to provide version control for +remote machine. This makes it possible to provide version control for files accessed under @value{tramp}. The actual version control binaries must be installed on the remote machine, accessible in the directories specified in -@var{tramp-remote-path}. +@code{tramp-remote-path}. This transparent integration with the version control systems is one of the most valuable features provided by @value{tramp}, but it is far from perfect. @@ -2204,7 +2933,7 @@ @section Determining if a file is under version control The VC package uses the existence of on-disk revision control master -files to determine if a given file is under revision control. These file +files to determine if a given file is under revision control. These file tests happen on the remote machine through the standard @value{tramp} mechanisms. @@ -2212,7 +2941,7 @@ @section Executing the version control commands on the remote machine There are no hooks provided by VC to allow intercepting of the version -control command execution. The calls occur through the +control command execution. The calls occur through the @code{call-process} mechanism, a function that is somewhat more efficient than the @code{shell-command} function but that does not provide hooks for remote execution of commands. @@ -2239,7 +2968,7 @@ This requires that a shell command be executed remotely, a process that is notably heavier-weight than the mtime comparison used for local -files. Unfortunately, unless a portable solution to the issue is found, +files. Unfortunately, unless a portable solution to the issue is found, this will remain the cost of remote version control. @@ -2247,7 +2976,7 @@ @section Bringing the workfile out of the repository VC will, by default, check for remote files and refuse to act on them -when checking out files from the repository. To work around this +when checking out files from the repository. To work around this problem, the function @code{vc-checkout} knows about @value{tramp} files and allows version control to occur. @@ -2266,14 +2995,14 @@ @node Remote File Ownership @subsection How VC determines who owns a workfile -@value{emacsname} provides the @code{user-full-name} function to +@value{emacsname} provides the @code{user-login-name} function to return the login name of the current user as well as mapping from -arbitrary user id values back to login names. The VC code uses this +arbitrary user id values back to login names. The VC code uses this functionality to map from the uid of the owner of a workfile to the login name in some circumstances. This will not, for obvious reasons, work if the remote system has a -different set of logins. As such, it is necessary to delegate to the +different set of logins. As such, it is necessary to delegate to the remote machine the job of determining the login name associated with a uid. @@ -2282,7 +3011,7 @@ reliable and portable method for performing this mapping. Thankfully, the only place in the VC code that depends on the mapping of -a uid to a login name is the @code{vc-file-owner} function. This returns +a uid to a login name is the @code{vc-file-owner} function. This returns the login of the owner of the file as a string. This function has been advised to use the output of @command{ls} on the @@ -2304,7 +3033,7 @@ needed. Unfortunately, life is not quite so easy when remote version control -comes into the picture. Each remote machine may have a different version +comes into the picture. Each remote machine may have a different version of the version control tools and, while this is painful, we need to ensure that unavailable features are not used remotely. @@ -2313,9 +3042,9 @@ local to each @value{tramp} buffer, forcing VC to determine these values again each time a new file is visited. -This has, quite obviously, some performance implications. Thankfully, +This has, quite obviously, some performance implications. Thankfully, most of the common operations performed by VC do not actually require -that the remote version be known. This makes the problem far less +that the remote version be known. This makes the problem far less apparent. Eventually these values will be captured by @value{tramp} on a system by @@ -2334,11 +3063,11 @@ @section Breaking a localname into its components. @value{tramp} file names are somewhat different, obviously, to ordinary file -names. As such, the lisp functions @code{file-name-directory} and +names. As such, the lisp functions @code{file-name-directory} and @code{file-name-nondirectory} are overridden within the @value{tramp} package. -Their replacements are reasonably simplistic in their approach. They +Their replacements are reasonably simplistic in their approach. They dissect the filename, call the original handler on the localname and then rebuild the @value{tramp} file name with the result. @@ -2346,6 +3075,77 @@ effect while preserving the @value{tramp} file name information. +@node Traces and Profiles +@chapter How to Customize Traces + +All @value{tramp} messages are raised with a verbosity level. The +verbosity level can be any number between 0 and 10. Only messages with +a verbosity level less than or equal to @code{tramp-verbose} are +displayed. + +The verbosity levels are + + @w{ 0} silent (no @value{tramp} messages at all) +@*@indent @w{ 1} errors +@*@indent @w{ 2} warnings +@*@indent @w{ 3} connection to remote hosts (default verbosity) +@*@indent @w{ 4} activities +@*@indent @w{ 5} internal +@*@indent @w{ 6} sent and received strings +@*@indent @w{ 7} file caching +@*@indent @w{ 8} connection properties +@*@indent @w{10} traces (huge) + +When @code{tramp-verbose} is greater than or equal to 4, the messages +are also written into a @value{tramp} debug buffer. This debug buffer +is useful for analysing problems; sending a @value{tramp} bug report +should be done with @code{tramp-verbose} set to a verbosity level of at +least 6 (@pxref{Bug Reports}). + +The debug buffer is in +@ifinfo +@ref{Outline Mode, , , @value{emacsdir}}. +@end ifinfo +@ifnotinfo +Outline Mode. +@end ifnotinfo +That means, you can change the level of messages to be viewed. If you +want, for example, see only messages up to verbosity level 5, you must +enter @kbd{C-u 6 C-c C-q}. +@ifinfo +Other keys for navigating are described in +@ref{Outline Visibility, , , @value{emacsdir}}. +@end ifinfo + +@value{tramp} errors are handled internally in order to raise the +verbosity level 1 messages. When you want to get a Lisp backtrace in +case of an error, you need to set both + +@lisp +(setq debug-on-error t + debug-on-signal t) +@end lisp + +Sometimes, it might be even necessary to step through @value{tramp} +function call traces. Such traces are enabled by the following code: + +@lisp +(require 'tramp) +(require 'trace) +(mapcar 'trace-function-background + (mapcar 'intern + (all-completions "tramp-" obarray 'functionp))) +(untrace-function 'tramp-read-passwd) +(untrace-function 'tramp-gw-basic-authentication) +@end lisp + +The function call traces are inserted in the buffer +@file{*trace-output*}. @code{tramp-read-passwd} and +@code{tramp-gw-basic-authentication} shall be disabled when the +function call traces are added to @value{tramp}, because both +functions return password strings, which should not be distributed. + + @node Issues @chapter Debatable Issues and What Was Decided @@ -2368,14 +3168,6 @@ But I have decided that this is too fragile to reliably work, so on some systems you'll have to do without the uuencode methods. -@item @value{tramp} does not work on XEmacs 20. - -This is because it requires the macro @code{with-timeout} which does not -appear to exist in XEmacs 20. I'm somewhat reluctant to add an -emulation macro to @value{tramp}, but if somebody who uses XEmacs 20 steps -forward and wishes to implement and test it, please contact me or the -mailing list. - @item The @value{tramp} filename syntax differs between GNU Emacs and XEmacs. The GNU Emacs maintainers wish to use a unified filename syntax for @@ -2402,10 +3194,10 @@ disabled. This can be achieved by setting file permissions @code{000} to the files @file{.../xemacs-packages/lisp/tramp/auto-autoloads.el*}. -In case of unified filenames, all @value{emacsname} download sites -are added to @code{tramp-default-method-alist} with default method -@option{ftp} @xref{Default Method}. These settings shouldn't be touched -for proper working of the @value{emacsname} package system. +In case of unified filenames, all @value{emacsname} download sites are +added to @code{tramp-default-method-alist} with default method +@option{ftp} @xref{Default Method}. These settings shouldn't be +touched for proper working of the @value{emacsname} package system. The syntax for unified filenames is described in the @value{tramp} manual for @value{emacsothername}. @@ -2430,7 +3222,6 @@ @c shells. @c * Explain how tramp.el works in principle: open a shell on a remote @c host and then send commands to it. -@c * Mention that bookmarks are a cool feature to go along with Tramp. @c * Make terminology "inline" vs "out-of-band" consistent. @c It seems that "external" is also used instead of "out-of-band". diff -r a37d5bf6cbb7 -r a66921565bcb man/trampver.texi --- a/man/trampver.texi Tue Jul 10 02:07:45 2007 +0000 +++ b/man/trampver.texi Sun Jul 15 04:47:46 2007 +0000 @@ -1,27 +1,26 @@ @c -*-texinfo-*- @c texi/trampver.texi. Generated from trampver.texi.in by configure. -@c This is part of the Emacs manual. -@c Copyright (C) 2003, 2004, 2005, 2006, 2007 -@c Free Software Foundation, Inc. -@c See file emacs.texi for copying conditions. - @c In the Tramp CVS, the version number is auto-frobbed from @c configure.ac, so you should edit that file and run @c "autoconf && ./configure" to change the version number. -@set trampver 2.0.56 +@set trampver 2.1.10-pre @c Other flags from configuration -@set prefix /usr/local +@set instprefix /usr/local @set lispdir /usr/local/share/emacs/site-lisp -@set infodir /usr/local/share/info +@set infodir /usr/local/info @c Formatting of the tramp program name consistent. @set tramp @sc{tramp} +@c Whether or not describe gateway methods. +@ifclear noemacsgw +@set emacsgw +@end ifclear + @c Some flags which make the text independent on the (X)Emacs flavor. @c "emacs" resp "xemacs" are set in the Makefile. Default is "emacs". - @ifclear emacs @ifclear xemacs @set emacs @@ -34,10 +33,9 @@ @set emacsdir emacs @set ftppackagename Ange-FTP @set prefix / -@set prefixsinglehop +@set prefixhop @set postfix : -@set postfixsinglehop : -@set postfixmultihop : +@set postfixhop : @set emacsothername XEmacs @set emacsotherdir xemacs @set emacsotherfilename tramp-xemacs.html @@ -50,10 +48,9 @@ @set emacsdir xemacs @set ftppackagename EFS @set prefix /[ -@set prefixsinglehop [ +@set prefixhop [ @set postfix ] -@set postfixsinglehop / -@set postfixmultihop : +@set postfixhop / @set emacsothername GNU Emacs @set emacsotherdir emacs @set emacsotherfilename tramp-emacs.html diff -r a37d5bf6cbb7 -r a66921565bcb nt/ChangeLog --- a/nt/ChangeLog Tue Jul 10 02:07:45 2007 +0000 +++ b/nt/ChangeLog Sun Jul 15 04:47:46 2007 +0000 @@ -1,3 +1,14 @@ +2007-07-14 Jason Rumney + + * inc/sys/socket.h: Include winsock2.h and ws2tcpip.h instead + of winsock.h. + +2007-07-11 Jason Rumney + + * gmake.defs (OLE32): New library to link. + + * nmake.defs (OLE32): Likewise. + 2007-06-25 Jason Rumney * cmdproxy.c (main): Set console codepages to "ANSI". diff -r a37d5bf6cbb7 -r a66921565bcb nt/gmake.defs --- a/nt/gmake.defs Tue Jul 10 02:07:45 2007 +0000 +++ b/nt/gmake.defs Sun Jul 15 04:47:46 2007 +0000 @@ -176,8 +176,9 @@ SHELL32 = -lshell32 USER32 = -luser32 WSOCK32 = -lwsock32 -WINMM = -lwinmm +WINMM = -lwinmm WINSPOOL = -lwinspool +OLE32 = -lole32 ifdef NOOPT DEBUG_CFLAGS = -DEMACSDEBUG diff -r a37d5bf6cbb7 -r a66921565bcb nt/inc/sys/socket.h --- a/nt/inc/sys/socket.h Tue Jul 10 02:07:45 2007 +0000 +++ b/nt/inc/sys/socket.h Sun Jul 15 04:47:46 2007 +0000 @@ -51,7 +51,8 @@ #define timeval ws_timeval #endif -#include +#include +#include /* redefine select to reference our version */ #ifdef MUST_REDEF_SELECT diff -r a37d5bf6cbb7 -r a66921565bcb nt/nmake.defs --- a/nt/nmake.defs Tue Jul 10 02:07:45 2007 +0000 +++ b/nt/nmake.defs Sun Jul 15 04:47:46 2007 +0000 @@ -123,8 +123,9 @@ SHELL32 = shell32.lib USER32 = user32.lib WSOCK32 = wsock32.lib -WINMM = winmm.lib +WINMM = winmm.lib WINSPOOL = winspool.lib +OLE32 = ole32.lib !ifdef NOOPT DEBUG_CFLAGS = -DEMACSDEBUG diff -r a37d5bf6cbb7 -r a66921565bcb src/ChangeLog --- a/src/ChangeLog Tue Jul 10 02:07:45 2007 +0000 +++ b/src/ChangeLog Sun Jul 15 04:47:46 2007 +0000 @@ -1,3 +1,98 @@ +2007-07-14 Jason Rumney + + * process.c [WINDOWSNT]: Don't undefine AF_INET6. + +2007-07-14 Richard Stallman + + * eval.c (maybe_call_debugger): New function. + (find_handler_clause): Use maybe_call_debugger. + Call it when the handler says `debug'. + Eliminate DEBUGGER_VALUE_PTR. + (Fsignal): Eliminate debugger_value. + (Qdebug): New variable. + (syms_of_eval): Initialize it. + +2007-07-14 Juanma Barranquero + + * eval.c (Fprogn): + * keyboard.c (Ftrack_mouse): + * print.c (Fwith_output_to_temp_buffer): + * window.c (Fsave_window_excursion): Doc fix. + +2007-07-13 Stefan Monnier + + * eval.c (init_eval_once): Bump max_lisp_eval_depth to 400. + +2007-07-12 Stefan Monnier + + * process.h (struct Lisp_Process): Turn slots infd, outfd, + kill_without_query, pty_flag, tick, update_tick, decoding_carryover, + inherit_coding_system_flag, filter_multibyte, adaptive_read_buffering, + read_output_delay, and read_output_skip from Lisp_Objects to ints. + Remove unused encoding_carryover. + * process.c: Adjust all functions accordingly. + +2007-07-12 Richard Stallman + + * term.c: Include unistd.h only if HAVE_UNISTD_H. + +2007-07-11 Jason Rumney + + * makefile.w32-in (LIBS): Include OLE32. + + * w32fns.c (w32_msg_pump) : Initialize COM. + (w32_msg_pump) : Uninitialize COM. + +2007-07-11 Stefan Monnier + + * lisp.h (struct Lisp_Hash_Table): Turn next_weak into a bare pointer. + * fns.c (weak_hash_tables): Rename from Vweak_hash_tables and turned + from a Lisp_Object into a bare pointer. + (make_hash_table, copy_hash_table, sweep_weak_hash_tables, init_fns): + Adjust the code correspondingly. + + * alloc.c (emacs_blocked_free): Remove unused var `bytes_used_now'. + + * term.c: Include unistd.h for ttyname, used in handle_one_term_event. + (term_show_mouse_face): Remove unused var `j'. + (handle_one_term_event): Remove unused vars `i' and `j'. + Don't cast return value of ttyname since it's not necessary. + +2007-07-10 Stefan Monnier + + * alloc.c (mark_maybe_pointer): Enforce mult-of-8 alignment when using + USE_LSB_TAG. Suggested by Dmitry Antipov . + + * fns.c (map_char_table): Use an array of int for `indices' rather than + an array of Lisp_Objects (which are only ever integers anyway). + (Fmap_char_table): Update caller. + * lisp.h: Update prototype. + * keymap.c (Fset_keymap_parent, map_keymap, Fcopy_keymap): + * fontset.c (Ffontset_info): + * casetab.c (set_case_table): Update callers. + + * editfns.c (Ftranspose_regions): Use EMACS_INT for positions. + + * keymap.c (struct accessible_keymaps_data) + (struct where_is_internal_data): New structures. + (accessible_keymaps_1, where_is_internal_1): Use them to change + interface to adhere to the one used by map_keymap. + (Faccessible_keymaps, where_is_internal): Use map_keymap. + (accessible_keymaps_char_table, where_is_internal_2): Remove. + + * keymap.h (map_keymap_function_t): More informative prototype. + +2007-07-10 Guanpeng Xu + + * search.c (Vinhibit_changing_match_data, search_regs_1): New vars. + (looking_at_1): Don't change search_regs and last_thing_searched + if `inhibit-changing-match-data' is non-nil. + (string_match_1, search_buffer, set_search_regs): Likewise. + (syms_of_search): Add Lisp level definition for + `inhibit-changing-match-data' and set it to nil. + (boyer_moore): If `inhibit-changing-match-data' is non-nil, compute + start and end of the match, instead of using values in search_regs. + 2007-07-01 Stefan Monnier * minibuf.c (Fcompleting_read): New value `confirm-only' @@ -1118,13 +1213,13 @@ when popup menu finishes. * w32fns.c (menubar_in_use): New flag. - (w32_wnd_proc) [WM_INITMENU, WM_EXITMENULOOP, WM_TIMER, WM_COMMAND]: + (w32_wnd_proc) : Use it. * w32menu.c (Fx_popup_menu): Don't free menu strings here. (w32_menu_show): Do it here instead. - * w32fns.c (w32_wnd_proc) [WM_INITMENU]: Set menubar_active frame + * w32fns.c (w32_wnd_proc) : Set menubar_active frame parameter. * w32menu.c (current_popup_menu): Make available globally. @@ -1132,7 +1227,7 @@ menu event into the keyboard buffer. Remove menu_command_in_progress. * w32fns.c (current_popup_menu): Use from w32menu.c. - (w32_wnd_proc) [WM_EXITMENULOOP, WM_TIMER]: Use menubar_active + (w32_wnd_proc) : Use menubar_active and current_popup_menu to determine whether a menubar menu has been cancelled. @@ -10243,7 +10338,7 @@ * w32term.h (x_output): Add focus_state. * w32term.c (x_focus_changed, w32_detect_focus_change): New functions. - (w32_read_socket) [WM_SETFOCUS]: Call w32_detect_focus_change. + (w32_read_socket) : Call w32_detect_focus_change. 2005-03-25 Stefan Monnier @@ -13531,7 +13626,7 @@ * w32term.h (AppendMenuW_Proc): Move declaration from w32menu.c. - * w32fns.c (w32_wnd_proc) [WM_MEASUREITEM, WM_DRAWITEM]: + * w32fns.c (w32_wnd_proc) : Handle Unicode menu titles. 2004-09-07 Kim F. Storm diff -r a37d5bf6cbb7 -r a66921565bcb src/alloc.c --- a/src/alloc.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/alloc.c Sun Jul 15 04:47:46 2007 +0000 @@ -1179,8 +1179,6 @@ void *ptr; const void *ptr2; { - EMACS_INT bytes_used_now; - BLOCK_INPUT_ALLOC; #ifdef GC_MALLOC_CHECK @@ -4220,9 +4218,14 @@ { struct mem_node *m; - /* Quickly rule out some values which can't point to Lisp data. We - assume that Lisp data is aligned on even addresses. */ - if ((EMACS_INT) p & 1) + /* Quickly rule out some values which can't point to Lisp data. */ + if ((EMACS_INT) p % +#ifdef USE_LSB_TAG + 8 /* USE_LSB_TAG needs Lisp data to be aligned on multiples of 8. */ +#else + 2 /* We assume that Lisp data is aligned on even addresses. */ +#endif + ) return; m = mem_find (p); diff -r a37d5bf6cbb7 -r a66921565bcb src/editfns.c --- a/src/editfns.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/editfns.c Sun Jul 15 04:47:46 2007 +0000 @@ -4256,9 +4256,9 @@ (startr1, endr1, startr2, endr2, leave_markers) Lisp_Object startr1, endr1, startr2, endr2, leave_markers; { - register int start1, end1, start2, end2; - int start1_byte, start2_byte, len1_byte, len2_byte; - int gap, len1, len_mid, len2; + register EMACS_INT start1, end1, start2, end2; + EMACS_INT start1_byte, start2_byte, len1_byte, len2_byte; + EMACS_INT gap, len1, len_mid, len2; unsigned char *start1_addr, *start2_addr, *temp; INTERVAL cur_intv, tmp_interval1, tmp_interval_mid, tmp_interval2, tmp_interval3; diff -r a37d5bf6cbb7 -r a66921565bcb src/eval.c --- a/src/eval.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/eval.c Sun Jul 15 04:47:46 2007 +0000 @@ -97,6 +97,7 @@ Lisp_Object Qand_rest, Qand_optional; Lisp_Object Qdebug_on_error; Lisp_Object Qdeclare; +Lisp_Object Qdebug; /* This holds either the symbol `run-hooks' or nil. It is nil at an early stage of startup, and when Emacs @@ -220,7 +221,7 @@ specpdl_ptr = specpdl; /* Don't forget to update docs (lispref node "Local Variables"). */ max_specpdl_size = 1000; - max_lisp_eval_depth = 300; + max_lisp_eval_depth = 400; Vrun_hooks = Qnil; } @@ -433,7 +434,7 @@ DEFUN ("progn", Fprogn, Sprogn, 0, UNEVALLED, 0, doc: /* Eval BODY forms sequentially and return value of last one. -usage: (progn BODY ...) */) +usage: (progn BODY...) */) (args) Lisp_Object args; { @@ -1585,8 +1586,7 @@ static Lisp_Object find_handler_clause P_ ((Lisp_Object, Lisp_Object, - Lisp_Object, Lisp_Object, - Lisp_Object *)); + Lisp_Object, Lisp_Object)); DEFUN ("signal", Fsignal, Ssignal, 2, 2, 0, doc: /* Signal an error. Args are ERROR-SYMBOL and associated DATA. @@ -1612,7 +1612,6 @@ Lisp_Object conditions; extern int gc_in_progress; extern int waiting_for_input; - Lisp_Object debugger_value; Lisp_Object string; Lisp_Object real_error_symbol; struct backtrace *bp; @@ -1670,7 +1669,7 @@ register Lisp_Object clause; clause = find_handler_clause (handlerlist->handler, conditions, - error_symbol, data, &debugger_value); + error_symbol, data); if (EQ (clause, Qlambda)) { @@ -1701,7 +1700,7 @@ handlerlist = allhandlers; /* If no handler is present now, try to run the debugger, and if that fails, throw to top level. */ - find_handler_clause (Qerror, conditions, error_symbol, data, &debugger_value); + find_handler_clause (Qerror, conditions, error_symbol, data); if (catchlist != 0) Fthrow (Qtop_level, Qt); @@ -1853,75 +1852,54 @@ = SIG is nil, and DATA is (SYMBOL . REST-OF-DATA). This is for memory-full errors only. - Store value returned from debugger into *DEBUGGER_VALUE_PTR. - We need to increase max_specpdl_size temporarily around anything we do that can push on the specpdl, so as not to get a second error here in case we're handling specpdl overflow. */ static Lisp_Object -find_handler_clause (handlers, conditions, sig, data, debugger_value_ptr) +find_handler_clause (handlers, conditions, sig, data) Lisp_Object handlers, conditions, sig, data; - Lisp_Object *debugger_value_ptr; { register Lisp_Object h; register Lisp_Object tem; - - if (EQ (handlers, Qt)) /* t is used by handlers for all conditions, set up by C code. */ + int debugger_called = 0; + int debugger_considered = 0; + + /* t is used by handlers for all conditions, set up by C code. */ + if (EQ (handlers, Qt)) return Qt; + + /* Don't run the debugger for a memory-full error. + (There is no room in memory to do that!) */ + if (NILP (sig)) + debugger_considered = 1; + /* error is used similarly, but means print an error message and run the debugger if that is enabled. */ if (EQ (handlers, Qerror) || !NILP (Vdebug_on_signal)) /* This says call debugger even if there is a handler. */ { - int debugger_called = 0; - Lisp_Object sig_symbol, combined_data; - /* This is set to 1 if we are handling a memory-full error, - because these must not run the debugger. - (There is no room in memory to do that!) */ - int no_debugger = 0; - - if (NILP (sig)) - { - combined_data = data; - sig_symbol = Fcar (data); - no_debugger = 1; - } - else - { - combined_data = Fcons (sig, data); - sig_symbol = sig; - } - - if (wants_debugger (Vstack_trace_on_error, conditions)) + if (!NILP (sig) && wants_debugger (Vstack_trace_on_error, conditions)) { max_specpdl_size++; -#ifdef PROTOTYPES + #ifdef PROTOTYPES internal_with_output_to_temp_buffer ("*Backtrace*", (Lisp_Object (*) (Lisp_Object)) Fbacktrace, Qnil); -#else + #else internal_with_output_to_temp_buffer ("*Backtrace*", Fbacktrace, Qnil); -#endif + #endif max_specpdl_size--; } - if (! no_debugger - /* Don't try to run the debugger with interrupts blocked. - The editing loop would return anyway. */ - && ! INPUT_BLOCKED_P - && (EQ (sig_symbol, Qquit) - ? debug_on_quit - : wants_debugger (Vdebug_on_error, conditions)) - && ! skip_debugger (conditions, combined_data) - && when_entered_debugger < num_nonmacro_input_events) + + if (!debugger_considered) { - *debugger_value_ptr - = call_debugger (Fcons (Qerror, - Fcons (combined_data, Qnil))); - debugger_called = 1; + debugger_considered = 1; + debugger_called = maybe_call_debugger (conditions, sig, data); } + /* If there is no handler, return saying whether we ran the debugger. */ if (EQ (handlers, Qerror)) { @@ -1930,6 +1908,7 @@ return Qt; } } + for (h = handlers; CONSP (h); h = Fcdr (h)) { Lisp_Object handler, condit; @@ -1948,18 +1927,55 @@ /* Handle a list of condition names in handler HANDLER. */ else if (CONSP (condit)) { - while (CONSP (condit)) + Lisp_Object tail; + for (tail = condit; CONSP (tail); tail = XCDR (tail)) { - tem = Fmemq (Fcar (condit), conditions); + tem = Fmemq (Fcar (tail), conditions); if (!NILP (tem)) - return handler; - condit = XCDR (condit); + { + /* This handler is going to apply. + Does it allow the debugger to run first? */ + if (! debugger_considered && !NILP (Fmemq (Qdebug, condit))) + maybe_call_debugger (conditions, sig, data); + return handler; + } } } } + return Qnil; } +/* Call the debugger if calling it is currently enabled for CONDITIONS. + SIG and DATA describe the signal, as in find_handler_clause. */ + +int +maybe_call_debugger (conditions, sig, data) + Lisp_Object conditions, sig, data; +{ + Lisp_Object combined_data; + + combined_data = Fcons (sig, data); + + if ( + /* Don't try to run the debugger with interrupts blocked. + The editing loop would return anyway. */ + ! INPUT_BLOCKED_P + /* Does user wants to enter debugger for this kind of error? */ + && (EQ (sig, Qquit) + ? debug_on_quit + : wants_debugger (Vdebug_on_error, conditions)) + && ! skip_debugger (conditions, combined_data) + /* rms: what's this for? */ + && when_entered_debugger < num_nonmacro_input_events) + { + call_debugger (Fcons (Qerror, Fcons (combined_data, Qnil))); + return 1; + } + + return 0; +} + /* dump an error message; called like printf */ /* VARARGS 1 */ @@ -3600,6 +3616,9 @@ Qand_optional = intern ("&optional"); staticpro (&Qand_optional); + Qdebug = intern ("debug"); + staticpro (&Qdebug); + DEFVAR_LISP ("stack-trace-on-error", &Vstack_trace_on_error, doc: /* *Non-nil means errors display a backtrace buffer. More precisely, this happens for any error that is handled diff -r a37d5bf6cbb7 -r a66921565bcb src/keyboard.c --- a/src/keyboard.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/keyboard.c Sun Jul 15 04:47:46 2007 +0000 @@ -1422,7 +1422,7 @@ Within a `track-mouse' form, mouse motion generates input events that you can read with `read-event'. Normally, mouse motion is ignored. -usage: (track-mouse BODY ...) */) +usage: (track-mouse BODY...) */) (args) Lisp_Object args; { diff -r a37d5bf6cbb7 -r a66921565bcb src/keymap.c --- a/src/keymap.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/keymap.c Sun Jul 15 04:47:46 2007 +0000 @@ -1179,7 +1179,7 @@ if (SYMBOLP (def) && !EQ (Vdefine_key_rebound_commands, Qt)) Vdefine_key_rebound_commands = Fcons (def, Vdefine_key_rebound_commands); - meta_bit = (VECTORP (key) || STRINGP (key) && STRING_MULTIBYTE (key) + meta_bit = (VECTORP (key) || (STRINGP (key) && STRING_MULTIBYTE (key)) ? meta_modifier : 0x80); if (VECTORP (def) && ASIZE (def) > 0 && CONSP (AREF (def, 0))) @@ -2079,12 +2079,23 @@ /* Help functions for describing and documenting keymaps. */ +struct accessible_keymaps_data { + Lisp_Object maps, tail, thisseq; + /* Does the current sequence end in the meta-prefix-char? */ + int is_metized; +}; static void -accessible_keymaps_1 (key, cmd, maps, tail, thisseq, is_metized) - Lisp_Object maps, tail, thisseq, key, cmd; - int is_metized; /* If 1, `key' is assumed to be INTEGERP. */ +accessible_keymaps_1 (key, cmd, args, data) + Lisp_Object key, cmd, args; + /* Use void* to be compatible with map_keymap_function_t. */ + void *data; { + struct accessible_keymaps_data *d = data; /* Cast! */ + Lisp_Object maps = d->maps; + Lisp_Object tail = d->tail; + Lisp_Object thisseq = d->thisseq; + int is_metized = d->is_metized && INTEGERP (key); Lisp_Object tem; cmd = get_keymap (get_keyelt (cmd, 0), 0, 0); @@ -2138,17 +2149,6 @@ } } -static void -accessible_keymaps_char_table (args, index, cmd) - Lisp_Object args, index, cmd; -{ - accessible_keymaps_1 (index, cmd, - XCAR (XCAR (args)), - XCAR (XCDR (args)), - XCDR (XCDR (args)), - XINT (XCDR (XCAR (args)))); -} - /* This function cannot GC. */ DEFUN ("accessible-keymaps", Faccessible_keymaps, Saccessible_keymaps, @@ -2163,14 +2163,11 @@ Lisp_Object keymap, prefix; { Lisp_Object maps, tail; - int prefixlen = 0; + int prefixlen = XINT (Flength (prefix)); /* no need for gcpro because we don't autoload any keymaps. */ if (!NILP (prefix)) - prefixlen = XINT (Flength (prefix)); - - if (!NILP (prefix)) { /* If a prefix was specified, start with the keymap (if any) for that prefix, so we don't waste time considering other prefixes. */ @@ -2180,7 +2177,9 @@ if the prefix is not defined in this particular map. It might even give us a list that isn't a keymap. */ tem = get_keymap (tem, 0, 0); - if (CONSP (tem)) + /* If the keymap is autoloaded `tem' is not a cons-cell, but we still + want to return it. */ + if (!NILP (tem)) { /* Convert PREFIX to a vector now, so that later on we don't have to deal with the possibility of a string. */ @@ -2620,8 +2619,8 @@ /* where-is - finding a command in a set of keymaps. */ static Lisp_Object where_is_internal (); -static Lisp_Object where_is_internal_1 (); -static void where_is_internal_2 (); +static void where_is_internal_1 P_ ((Lisp_Object key, Lisp_Object binding, + Lisp_Object args, void *data)); /* Like Flookup_key, but uses a list of keymaps SHADOW instead of a single map. Returns the first non-nil binding found in any of those maps. */ @@ -2650,6 +2649,12 @@ static Lisp_Object Vmouse_events; +struct where_is_internal_data { + Lisp_Object definition, noindirect, this, last; + int last_is_meta; + Lisp_Object sequences; +}; + /* This function can GC if Flookup_key autoloads any keymaps. */ static Lisp_Object @@ -2687,6 +2692,7 @@ { /* Key sequence to reach map, and the map that it reaches */ register Lisp_Object this, map, tem; + struct where_is_internal_data data; /* In order to fold [META-PREFIX-CHAR CHAR] sequences into [M-CHAR] sequences, check if last character of the sequence @@ -3059,7 +3065,7 @@ || EQ (binding, definition) || (CONSP (definition) && !NILP (Fequal (binding, definition))))) /* Doesn't match. */ - return Qnil; + return; /* We have found a match. Construct the key sequence where we found it. */ if (INTEGERP (key) && last_is_meta) @@ -3074,10 +3080,9 @@ { Lisp_Object sequences = Fgethash (binding, where_is_cache, Qnil); Fputhash (binding, Fcons (sequence, sequences), where_is_cache); - return Qnil; } else - return sequence; + d->sequences = Fcons (sequence, d->sequences); } /* describe-bindings - summarizing all the bindings in a set of keymaps. */ diff -r a37d5bf6cbb7 -r a66921565bcb src/keymap.h --- a/src/keymap.h Tue Jul 10 02:07:45 2007 +0000 +++ b/src/keymap.h Sun Jul 15 04:47:46 2007 +0000 @@ -47,7 +47,7 @@ extern void keys_of_keymap P_ ((void)); typedef void (*map_keymap_function_t) - P_ ((Lisp_Object, Lisp_Object, Lisp_Object, void*)); + P_ ((Lisp_Object key, Lisp_Object val, Lisp_Object args, void* data)); extern void map_keymap P_ ((Lisp_Object map, map_keymap_function_t fun, Lisp_Object largs, void* cargs, int autoload)); #endif diff -r a37d5bf6cbb7 -r a66921565bcb src/lisp.h --- a/src/lisp.h Tue Jul 10 02:07:45 2007 +0000 +++ b/src/lisp.h Sun Jul 15 04:47:46 2007 +0000 @@ -56,7 +56,7 @@ #ifdef GC_CHECK_CONS_LIST #define CHECK_CONS_LIST() check_cons_list() #else -#define CHECK_CONS_LIST() 0 +#define CHECK_CONS_LIST() ((void)0) #endif /* These are default choices for the types to use. */ @@ -3234,6 +3234,7 @@ #endif /* Defined in xfaces.c */ +EXFUN (Fclear_face_cache, 1); extern void syms_of_xfaces P_ ((void)); #ifndef HAVE_GETLOADAVG @@ -3249,6 +3250,7 @@ extern void syms_of_xsmfns P_ ((void)); /* Defined in xselect.c */ +EXFUN (Fx_send_client_event, 6); extern void syms_of_xselect P_ ((void)); /* Defined in xterm.c */ diff -r a37d5bf6cbb7 -r a66921565bcb src/makefile.w32-in --- a/src/makefile.w32-in Tue Jul 10 02:07:45 2007 +0000 +++ b/src/makefile.w32-in Sun Jul 15 04:47:46 2007 +0000 @@ -147,7 +147,7 @@ $(TLIB1) \ $(TLIBW32) \ $(TLASTLIB) \ - $(WINMM) \ + $(WINMM) \ $(ADVAPI32) \ $(GDI32) \ $(COMDLG32) \ @@ -155,6 +155,7 @@ $(MPR) \ $(SHELL32) \ $(WINSPOOL) \ + $(OLE32) \ $(libc) # diff -r a37d5bf6cbb7 -r a66921565bcb src/print.c --- a/src/print.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/print.c Sun Jul 15 04:47:46 2007 +0000 @@ -690,7 +690,7 @@ to get the buffer displayed instead of just displaying the non-selected buffer and calling the hook. It gets one argument, the buffer to display. -usage: (with-output-to-temp-buffer BUFNAME BODY ...) */) +usage: (with-output-to-temp-buffer BUFNAME BODY...) */) (args) Lisp_Object args; { diff -r a37d5bf6cbb7 -r a66921565bcb src/process.c --- a/src/process.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/process.c Sun Jul 15 04:47:46 2007 +0000 @@ -121,14 +121,6 @@ #include #endif -/* Disable IPv6 support for w32 until someone figures out how to do it - properly. */ -#ifdef WINDOWSNT -# ifdef AF_INET6 -# undef AF_INET6 -# endif -#endif - #include "lisp.h" #include "systime.h" #include "systty.h" @@ -393,7 +385,7 @@ int len; } datagram_address[MAXDESC]; #define DATAGRAM_CHAN_P(chan) (datagram_address[chan].sa != 0) -#define DATAGRAM_CONN_P(proc) (PROCESSP (proc) && datagram_address[XINT (XPROCESS (proc)->infd)].sa != 0) +#define DATAGRAM_CONN_P(proc) (PROCESSP (proc) && datagram_address[XPROCESS (proc)->infd].sa != 0) #else #define DATAGRAM_CHAN_P(chan) (0) #define DATAGRAM_CONN_P(proc) (0) @@ -628,19 +620,19 @@ p = allocate_process (); - XSETINT (p->infd, -1); - XSETINT (p->outfd, -1); - XSETFASTINT (p->tick, 0); - XSETFASTINT (p->update_tick, 0); + p->infd = -1; + p->outfd = -1; + p->tick = 0; + p->update_tick = 0; p->pid = 0; p->raw_status_new = 0; p->status = Qrun; p->mark = Fmake_marker (); #ifdef ADAPTIVE_READ_BUFFERING - p->adaptive_read_buffering = Qnil; - XSETFASTINT (p->read_output_delay, 0); - p->read_output_skip = Qnil; + p->adaptive_read_buffering = 0; + p->read_output_delay = 0; + p->read_output_skip = 0; #endif /* If name is already in use, modify it until it is unused. */ @@ -679,8 +671,8 @@ Lisp_Object process; { struct Lisp_Process *p = XPROCESS (process); - int inch = XINT (p->infd); - int outch = XINT (p->outfd); + int inch = p->infd; + int outch = p->outfd; Lisp_Object coding_system; if (inch < 0 || outch < 0) @@ -692,7 +684,7 @@ coding_system = p->decode_coding_system; if (! NILP (p->filter)) { - if (NILP (p->filter_multibyte)) + if (!p->filter_multibyte) coding_system = raw_text_coding_system (coding_system); } else if (BUFFERP (p->buffer)) @@ -814,10 +806,10 @@ if (NETCONN1_P (p)) { p->status = Fcons (Qexit, Fcons (make_number (0), Qnil)); - XSETINT (p->tick, ++process_tick); + p->tick = ++process_tick; status_notify (p); } - else if (XINT (p->infd) >= 0) + else if (p->infd >= 0) { #ifdef SIGCHLD Lisp_Object symbol; @@ -845,7 +837,7 @@ /* Do this now, since remove_process will make sigchld_handler do nothing. */ p->status = Fcons (Qsignal, Fcons (make_number (SIGKILL), Qnil)); - XSETINT (p->tick, ++process_tick); + p->tick = ++process_tick; status_notify (p); } } @@ -1037,18 +1029,18 @@ (debug) (set-process-filter process ...) */ - if (XINT (p->infd) >= 0) + if (p->infd >= 0) { if (EQ (filter, Qt) && !EQ (p->status, Qlisten)) { - FD_CLR (XINT (p->infd), &input_wait_mask); - FD_CLR (XINT (p->infd), &non_keyboard_wait_mask); + FD_CLR (p->infd, &input_wait_mask); + FD_CLR (p->infd, &non_keyboard_wait_mask); } else if (EQ (p->filter, Qt) && !EQ (p->command, Qt)) /* Network process not stopped. */ { - FD_SET (XINT (p->infd), &input_wait_mask); - FD_SET (XINT (p->infd), &non_keyboard_wait_mask); + FD_SET (p->infd, &input_wait_mask); + FD_SET (p->infd, &non_keyboard_wait_mask); } } @@ -1110,8 +1102,8 @@ CHECK_NATNUM (height); CHECK_NATNUM (width); - if (XINT (XPROCESS (process)->infd) < 0 - || set_window_size (XINT (XPROCESS (process)->infd), + if (XPROCESS (process)->infd < 0 + || set_window_size (XPROCESS (process)->infd, XINT (height), XINT (width)) <= 0) return Qnil; else @@ -1139,7 +1131,7 @@ register Lisp_Object process, flag; { CHECK_PROCESS (process); - XPROCESS (process)->inherit_coding_system_flag = flag; + XPROCESS (process)->inherit_coding_system_flag = !NILP (flag); return flag; } @@ -1154,7 +1146,7 @@ register Lisp_Object process; { CHECK_PROCESS (process); - return XPROCESS (process)->inherit_coding_system_flag; + return XPROCESS (process)->inherit_coding_system_flag ? Qt : Qnil; } DEFUN ("set-process-query-on-exit-flag", @@ -1167,7 +1159,7 @@ register Lisp_Object process, flag; { CHECK_PROCESS (process); - XPROCESS (process)->kill_without_query = Fnull (flag); + XPROCESS (process)->kill_without_query = NILP (flag); return flag; } @@ -1179,7 +1171,7 @@ register Lisp_Object process; { CHECK_PROCESS (process); - return Fnull (XPROCESS (process)->kill_without_query); + return (XPROCESS (process)->kill_without_query ? Qnil : Qt); } #ifdef DATAGRAM_SOCKETS @@ -1354,7 +1346,7 @@ p = XPROCESS (proc); if (NILP (p->childp)) continue; - if (!NILP (query_only) && !NILP (p->kill_without_query)) + if (!NILP (query_only) && p->kill_without_query) continue; if (STRINGP (p->name) && ( i = SCHARS (p->name), (i > w_proc))) @@ -1417,7 +1409,7 @@ p = XPROCESS (proc); if (NILP (p->childp)) continue; - if (!NILP (query_only) && !NILP (p->kill_without_query)) + if (!NILP (query_only) && p->kill_without_query) continue; Finsert (1, &p->name); @@ -1493,7 +1485,7 @@ if (NILP (port)) port = Fformat_network_address (Fplist_get (p->childp, QClocal), Qnil); sprintf (tembuf, "(network %s server on %s)\n", - (DATAGRAM_CHAN_P (XINT (p->infd)) ? "datagram" : "stream"), + (DATAGRAM_CHAN_P (p->infd) ? "datagram" : "stream"), (STRINGP (port) ? (char *)SDATA (port) : "?")); insert_string (tembuf); } @@ -1511,7 +1503,7 @@ if (NILP (host)) host = Fformat_network_address (Fplist_get (p->childp, QCremote), Qnil); sprintf (tembuf, "(network %s connection to %s)\n", - (DATAGRAM_CHAN_P (XINT (p->infd)) ? "datagram" : "stream"), + (DATAGRAM_CHAN_P (p->infd) ? "datagram" : "stream"), (STRINGP (host) ? (char *)SDATA (host) : "?")); insert_string (tembuf); } @@ -1642,11 +1634,13 @@ XPROCESS (proc)->sentinel = Qnil; XPROCESS (proc)->filter = Qnil; XPROCESS (proc)->filter_multibyte - = buffer_defaults.enable_multibyte_characters; + = !NILP (buffer_defaults.enable_multibyte_characters); XPROCESS (proc)->command = Flist (nargs - 2, args + 2); #ifdef ADAPTIVE_READ_BUFFERING - XPROCESS (proc)->adaptive_read_buffering = Vprocess_adaptive_read_buffering; + XPROCESS (proc)->adaptive_read_buffering + = (NILP (Vprocess_adaptive_read_buffering) ? 0 + : EQ (Vprocess_adaptive_read_buffering, Qt) ? 1 : 2); #endif /* Make the process marker point into the process buffer (if any). */ @@ -1777,13 +1771,11 @@ #endif /* not VMS */ XPROCESS (proc)->decoding_buf = make_uninit_string (0); - XPROCESS (proc)->decoding_carryover = make_number (0); + XPROCESS (proc)->decoding_carryover = 0; XPROCESS (proc)->encoding_buf = make_uninit_string (0); - XPROCESS (proc)->encoding_carryover = make_number (0); XPROCESS (proc)->inherit_coding_system_flag - = (NILP (buffer) || !inherit_process_coding_system - ? Qnil : Qt); + = (NILP (buffer) || !inherit_process_coding_system); create_process (proc, (char **) new_argv, current_dir); @@ -1955,15 +1947,15 @@ /* Record this as an active process, with its channels. As a result, child_setup will close Emacs's side of the pipes. */ chan_process[inchannel] = process; - XSETINT (XPROCESS (process)->infd, inchannel); - XSETINT (XPROCESS (process)->outfd, outchannel); + XPROCESS (process)->infd = inchannel; + XPROCESS (process)->outfd = outchannel; /* Previously we recorded the tty descriptor used in the subprocess. It was only used for getting the foreground tty process, so now we just reopen the device (see emacs_get_tty_pgrp) as this is more portable (see USG_SUBTTY_WORKS above). */ - XPROCESS (process)->pty_flag = (pty_flag ? Qt : Qnil); + XPROCESS (process)->pty_flag = pty_flag; XPROCESS (process)->status = Qrun; setup_process_coding_systems (process); @@ -2480,7 +2472,7 @@ if (!DATAGRAM_CONN_P (process)) return Qnil; - channel = XINT (XPROCESS (process)->infd); + channel = XPROCESS (process)->infd; return conv_sockaddr_to_lisp (datagram_address[channel].sa, datagram_address[channel].len); } @@ -2500,7 +2492,7 @@ if (!DATAGRAM_CONN_P (process)) return Qnil; - channel = XINT (XPROCESS (process)->infd); + channel = XPROCESS (process)->infd; len = get_lisp_to_sockaddr_size (address, &family); if (datagram_address[channel].len != len) @@ -2665,7 +2657,7 @@ if (!NETCONN1_P (p)) error ("Process is not a network process"); - s = XINT (p->infd); + s = p->infd; if (s < 0) error ("Process is not running"); @@ -3419,18 +3411,18 @@ p->buffer = buffer; p->sentinel = sentinel; p->filter = filter; - p->filter_multibyte = buffer_defaults.enable_multibyte_characters; + p->filter_multibyte = !NILP (buffer_defaults.enable_multibyte_characters); /* Override the above only if :filter-multibyte is specified. */ if (! NILP (Fplist_member (contact, QCfilter_multibyte))) - p->filter_multibyte = Fplist_get (contact, QCfilter_multibyte); + p->filter_multibyte = !NILP (Fplist_get (contact, QCfilter_multibyte)); p->log = Fplist_get (contact, QClog); if (tem = Fplist_get (contact, QCnoquery), !NILP (tem)) - p->kill_without_query = Qt; + p->kill_without_query = 1; if ((tem = Fplist_get (contact, QCstop), !NILP (tem))) p->command = Qt; p->pid = 0; - XSETINT (p->infd, inch); - XSETINT (p->outfd, outch); + p->infd = inch; + p->outfd = outch; if (is_server && socktype == SOCK_STREAM) p->status = Qlisten; @@ -3551,13 +3543,11 @@ setup_process_coding_systems (proc); p->decoding_buf = make_uninit_string (0); - p->decoding_carryover = make_number (0); + p->decoding_carryover = 0; p->encoding_buf = make_uninit_string (0); - p->encoding_carryover = make_number (0); p->inherit_coding_system_flag - = (!NILP (tem) || NILP (buffer) || !inherit_process_coding_system - ? Qnil : Qt); + = (!NILP (tem) || NILP (buffer) || !inherit_process_coding_system); UNGCPRO; return proc; @@ -3820,16 +3810,16 @@ register int inchannel, outchannel; register struct Lisp_Process *p = XPROCESS (proc); - inchannel = XINT (p->infd); - outchannel = XINT (p->outfd); + inchannel = p->infd; + outchannel = p->outfd; #ifdef ADAPTIVE_READ_BUFFERING - if (XINT (p->read_output_delay) > 0) + if (p->read_output_delay > 0) { if (--process_output_delay_count < 0) process_output_delay_count = 0; - XSETINT (p->read_output_delay, 0); - p->read_output_skip = Qnil; + p->read_output_delay = 0; + p->read_output_skip = 0; } #endif @@ -3851,8 +3841,8 @@ emacs_close (outchannel); #endif - XSETINT (p->infd, -1); - XSETINT (p->outfd, -1); + p->infd = -1; + p->outfd = -1; #ifdef DATAGRAM_SOCKETS if (DATAGRAM_CHAN_P (inchannel)) { @@ -3900,8 +3890,8 @@ process = chan_process[i]; if (!NILP (process)) { - int in = XINT (XPROCESS (process)->infd); - int out = XINT (XPROCESS (process)->outfd); + int in = XPROCESS (process)->infd; + int out = XPROCESS (process)->outfd; if (in >= 0) emacs_close (in); if (out >= 0 && in != out) @@ -4145,8 +4135,8 @@ p->filter = ps->filter; p->command = Qnil; p->pid = 0; - XSETINT (p->infd, s); - XSETINT (p->outfd, s); + p->infd = s; + p->outfd = s; p->status = Qrun; /* Client processes for accepted connections are not stopped initially. */ @@ -4169,12 +4159,11 @@ setup_process_coding_systems (proc); p->decoding_buf = make_uninit_string (0); - p->decoding_carryover = make_number (0); + p->decoding_carryover = 0; p->encoding_buf = make_uninit_string (0); - p->encoding_carryover = make_number (0); p->inherit_coding_system_flag - = (NILP (buffer) ? Qnil : ps->inherit_coding_system_flag); + = (NILP (buffer) ? 0 : ps->inherit_coding_system_flag); if (!NILP (ps->log)) call3 (ps->log, server, proc, @@ -4299,7 +4288,7 @@ /* If wait_proc is a process to watch, set wait_channel accordingly. */ if (wait_proc != NULL) - wait_channel = XINT (wait_proc->infd); + wait_channel = wait_proc->infd; record_unwind_protect (wait_reading_process_output_unwind, make_number (waiting_for_user_input_p)); @@ -4484,9 +4473,9 @@ XSETPROCESS (proc, wait_proc); /* Read data from the process, until we exhaust it. */ - while (XINT (wait_proc->infd) >= 0) + while (wait_proc->infd >= 0) { - nread = read_process_output (proc, XINT (wait_proc->infd)); + nread = read_process_output (proc, wait_proc->infd); if (nread == 0) break; @@ -4516,9 +4505,9 @@ if (wait_proc && just_wait_proc) { - if (XINT (wait_proc->infd) < 0) /* Terminated */ + if (wait_proc->infd < 0) /* Terminated */ break; - FD_SET (XINT (wait_proc->infd), &Available); + FD_SET (wait_proc->infd, &Available); check_delay = 0; IF_NON_BLOCKING_CONNECT (check_connect = 0); } @@ -4566,7 +4555,7 @@ #ifdef ADAPTIVE_READ_BUFFERING /* Set the timeout for adaptive read buffering if any - process has non-nil read_output_skip and non-zero + process has non-zero read_output_skip and non-zero read_output_delay, and we are not reading output for a specific wait_channel. It is not executed if Vprocess_adaptive_read_buffering is nil. */ @@ -4581,16 +4570,16 @@ if (NILP (proc)) continue; /* Find minimum non-zero read_output_delay among the - processes with non-nil read_output_skip. */ - if (XINT (XPROCESS (proc)->read_output_delay) > 0) + processes with non-zero read_output_skip. */ + if (XPROCESS (proc)->read_output_delay > 0) { check_delay--; - if (NILP (XPROCESS (proc)->read_output_skip)) + if (!XPROCESS (proc)->read_output_skip) continue; FD_CLR (channel, &Available); - XPROCESS (proc)->read_output_skip = Qnil; - if (XINT (XPROCESS (proc)->read_output_delay) < usecs) - usecs = XINT (XPROCESS (proc)->read_output_delay); + XPROCESS (proc)->read_output_skip = 0; + if (XPROCESS (proc)->read_output_delay < usecs) + usecs = XPROCESS (proc)->read_output_delay; } } EMACS_SET_SECS_USECS (timeout, 0, usecs); @@ -4863,7 +4852,7 @@ else { /* Preserve status of processes already terminated. */ - XSETINT (XPROCESS (proc)->tick, ++process_tick); + XPROCESS (proc)->tick = ++process_tick; deactivate_process (proc); if (XPROCESS (proc)->raw_status_new) update_status (XPROCESS (proc)); @@ -4915,7 +4904,7 @@ #endif if (xerrno) { - XSETINT (p->tick, ++process_tick); + p->tick = ++process_tick; p->status = Fcons (Qfailed, Fcons (make_number (xerrno), Qnil)); deactivate_process (proc); } @@ -4928,8 +4917,8 @@ exec_sentinel (proc, build_string ("open\n")); if (!EQ (p->filter, Qt) && !EQ (p->command, Qt)) { - FD_SET (XINT (p->infd), &input_wait_mask); - FD_SET (XINT (p->infd), &non_keyboard_wait_mask); + FD_SET (p->infd, &input_wait_mask); + FD_SET (p->infd, &non_keyboard_wait_mask); } } } @@ -5003,7 +4992,7 @@ register struct Lisp_Process *p = XPROCESS (proc); register int opoint; struct coding_system *coding = proc_decode_coding_system[channel]; - int carryover = XINT (p->decoding_carryover); + int carryover = p->decoding_carryover; int readmax = 4096; #ifdef VMS @@ -5056,9 +5045,9 @@ { nbytes = emacs_read (channel, chars + carryover, readmax); #ifdef ADAPTIVE_READ_BUFFERING - if (nbytes > 0 && !NILP (p->adaptive_read_buffering)) + if (nbytes > 0 && p->adaptive_read_buffering) { - int delay = XINT (p->read_output_delay); + int delay = p->read_output_delay; if (nbytes < 256) { if (delay < READ_OUTPUT_DELAY_MAX_MAX) @@ -5074,10 +5063,10 @@ if (delay == 0) process_output_delay_count--; } - XSETINT (p->read_output_delay, delay); + p->read_output_delay = delay; if (delay) { - p->read_output_skip = Qt; + p->read_output_skip = 1; process_output_skip = 1; } } @@ -5095,7 +5084,7 @@ } #endif /* not VMS */ - XSETINT (p->decoding_carryover, 0); + p->decoding_carryover = 0; /* At this point, NBYTES holds number of bytes just received (including the one in proc_buffered_char[channel]). */ @@ -5169,12 +5158,12 @@ valid memory because p->outfd will be changed once EOF is sent to the process. */ if (NILP (p->encode_coding_system) - && proc_encode_coding_system[XINT (p->outfd)]) + && proc_encode_coding_system[p->outfd]) { p->encode_coding_system = coding_inherit_eol_type (Vlast_coding_system_used, Qnil); setup_coding_system (p->encode_coding_system, - proc_encode_coding_system[XINT (p->outfd)]); + proc_encode_coding_system[p->outfd]); } } @@ -5184,10 +5173,10 @@ p->decoding_buf = make_uninit_string (coding->carryover_bytes); bcopy (coding->carryover, SDATA (p->decoding_buf), coding->carryover_bytes); - XSETINT (p->decoding_carryover, coding->carryover_bytes); + p->decoding_carryover = coding->carryover_bytes; } /* Adjust the multibyteness of TEXT to that of the filter. */ - if (NILP (p->filter_multibyte) != ! STRING_MULTIBYTE (text)) + if (p->filter_multibyte != STRING_MULTIBYTE (text)) text = (STRING_MULTIBYTE (text) ? Fstring_as_unibyte (text) : Fstring_to_multibyte (text)); @@ -5279,12 +5268,12 @@ { p->decode_coding_system = Vlast_coding_system_used; if (NILP (p->encode_coding_system) - && proc_encode_coding_system[XINT (p->outfd)]) + && proc_encode_coding_system[p->outfd]) { p->encode_coding_system = coding_inherit_eol_type (Vlast_coding_system_used, Qnil); setup_coding_system (p->encode_coding_system, - proc_encode_coding_system[XINT (p->outfd)]); + proc_encode_coding_system[p->outfd]); } } if (coding->carryover_bytes > 0) @@ -5293,7 +5282,7 @@ p->decoding_buf = make_uninit_string (coding->carryover_bytes); bcopy (coding->carryover, SDATA (p->decoding_buf), coding->carryover_bytes); - XSETINT (p->decoding_carryover, coding->carryover_bytes); + p->decoding_carryover = coding->carryover_bytes; } /* Adjust the multibyteness of TEXT to that of the buffer. */ if (NILP (current_buffer->enable_multibyte_characters) @@ -5412,10 +5401,10 @@ update_status (p); if (! EQ (p->status, Qrun)) error ("Process %s not running", SDATA (p->name)); - if (XINT (p->outfd) < 0) + if (p->outfd < 0) error ("Output file descriptor of %s is closed", SDATA (p->name)); - coding = proc_encode_coding_system[XINT (p->outfd)]; + coding = proc_encode_coding_system[p->outfd]; Vlast_coding_system_used = CODING_ID_NAME (coding->id); if ((STRINGP (object) && STRING_MULTIBYTE (object)) @@ -5499,7 +5488,7 @@ if (pty_max_bytes == 0) { #if defined (HAVE_FPATHCONF) && defined (_PC_MAX_CANON) - pty_max_bytes = fpathconf (XFASTINT (p->outfd), _PC_MAX_CANON); + pty_max_bytes = fpathconf (p->outfd, _PC_MAX_CANON); if (pty_max_bytes < 0) pty_max_bytes = 250; #else @@ -5521,7 +5510,7 @@ /* Decide how much data we can send in one batch. Long lines need to be split into multiple batches. */ - if (!NILP (p->pty_flag)) + if (p->pty_flag) { /* Starting this at zero is always correct when not the first iteration because the previous iteration ended by sending C-d. @@ -5550,7 +5539,7 @@ /* Send this batch, using one or more write calls. */ while (this > 0) { - int outfd = XINT (p->outfd); + int outfd = p->outfd; old_sigpipe = (SIGTYPE (*) ()) signal (SIGPIPE, send_process_trap); #ifdef DATAGRAM_SOCKETS if (DATAGRAM_CHAN_P (outfd)) @@ -5570,12 +5559,12 @@ { rv = emacs_write (outfd, (char *) buf, this); #ifdef ADAPTIVE_READ_BUFFERING - if (XINT (p->read_output_delay) > 0 - && EQ (p->adaptive_read_buffering, Qt)) + if (p->read_output_delay > 0 + && p->adaptive_read_buffering == 1) { - XSETFASTINT (p->read_output_delay, 0); + p->read_output_delay = 0; process_output_delay_count--; - p->read_output_skip = Qnil; + p->read_output_skip = 0; } #endif } @@ -5618,7 +5607,7 @@ if (errno == EAGAIN) { int flags = FWRITE; - ioctl (XINT (p->outfd), TIOCFLUSH, &flags); + ioctl (p->outfd, TIOCFLUSH, &flags); } #endif /* BROKEN_PTY_READ_AFTER_EAGAIN */ @@ -5667,7 +5656,7 @@ #endif p->raw_status_new = 0; p->status = Fcons (Qexit, Fcons (make_number (256), Qnil)); - XSETINT (p->tick, ++process_tick); + p->tick = ++process_tick; deactivate_process (proc); #ifdef VMS error ("Error writing to process %s; closed it", SDATA (p->name)); @@ -5736,7 +5725,7 @@ int gid = -1; #ifdef TIOCGPGRP - if (ioctl (XINT (p->infd), TIOCGPGRP, &gid) == -1 && ! NILP (p->tty_name)) + if (ioctl (p->infd, TIOCGPGRP, &gid) == -1 && ! NILP (p->tty_name)) { int fd; /* Some OS:es (Solaris 8/9) does not allow TIOCGPGRP from the @@ -5774,7 +5763,7 @@ if (!EQ (p->childp, Qt)) error ("Process %s is not a subprocess", SDATA (p->name)); - if (XINT (p->infd) < 0) + if (p->infd < 0) error ("Process %s is not active", SDATA (p->name)); @@ -5817,11 +5806,11 @@ if (!EQ (p->childp, Qt)) error ("Process %s is not a subprocess", SDATA (p->name)); - if (XINT (p->infd) < 0) + if (p->infd < 0) error ("Process %s is not active", SDATA (p->name)); - if (NILP (p->pty_flag)) + if (!p->pty_flag) current_group = Qnil; /* If we are using pgrps, get a pgrp number and make it negative. */ @@ -5840,7 +5829,7 @@ struct termios t; cc_t *sig_char = NULL; - tcgetattr (XINT (p->infd), &t); + tcgetattr (p->infd, &t); switch (signo) { @@ -5880,16 +5869,16 @@ switch (signo) { case SIGINT: - ioctl (XINT (p->infd), TIOCGETC, &c); + ioctl (p->infd, TIOCGETC, &c); send_process (proc, &c.t_intrc, 1, Qnil); return; case SIGQUIT: - ioctl (XINT (p->infd), TIOCGETC, &c); + ioctl (p->infd, TIOCGETC, &c); send_process (proc, &c.t_quitc, 1, Qnil); return; #ifdef SIGTSTP case SIGTSTP: - ioctl (XINT (p->infd), TIOCGLTC, &lc); + ioctl (p->infd, TIOCGLTC, &lc); send_process (proc, &lc.t_suspc, 1, Qnil); return; #endif /* ! defined (SIGTSTP) */ @@ -5904,16 +5893,16 @@ switch (signo) { case SIGINT: - ioctl (XINT (p->infd), TCGETA, &t); + ioctl (p->infd, TCGETA, &t); send_process (proc, &t.c_cc[VINTR], 1, Qnil); return; case SIGQUIT: - ioctl (XINT (p->infd), TCGETA, &t); + ioctl (p->infd, TCGETA, &t); send_process (proc, &t.c_cc[VQUIT], 1, Qnil); return; #ifdef SIGTSTP case SIGTSTP: - ioctl (XINT (p->infd), TCGETA, &t); + ioctl (p->infd, TCGETA, &t); send_process (proc, &t.c_cc[VSWTCH], 1, Qnil); return; #endif /* ! defined (SIGTSTP) */ @@ -5971,7 +5960,7 @@ case SIGCONT: p->raw_status_new = 0; p->status = Qrun; - XSETINT (p->tick, ++process_tick); + p->tick = ++process_tick; if (!nomsg) status_notify (NULL); break; @@ -5991,7 +5980,7 @@ sys$forcex (&(p->pid), 0, 1); whoosh: #endif - flush_pending_output (XINT (p->infd)); + flush_pending_output (p->infd); break; } @@ -6008,7 +5997,7 @@ #ifdef TIOCSIGSEND if (!NILP (current_group)) { - if (ioctl (XINT (p->infd), TIOCSIGSEND, signo) == -1) + if (ioctl (p->infd, TIOCSIGSEND, signo) == -1) EMACS_KILLPG (gid, signo); } else @@ -6074,10 +6063,10 @@ p = XPROCESS (process); if (NILP (p->command) - && XINT (p->infd) >= 0) + && p->infd >= 0) { - FD_CLR (XINT (p->infd), &input_wait_mask); - FD_CLR (XINT (p->infd), &non_keyboard_wait_mask); + FD_CLR (p->infd, &input_wait_mask); + FD_CLR (p->infd, &non_keyboard_wait_mask); } p->command = Qt; return process; @@ -6105,11 +6094,11 @@ p = XPROCESS (process); if (EQ (p->command, Qt) - && XINT (p->infd) >= 0 + && p->infd >= 0 && (!EQ (p->filter, Qt) || EQ (p->status, Qlisten))) { - FD_SET (XINT (p->infd), &input_wait_mask); - FD_SET (XINT (p->infd), &non_keyboard_wait_mask); + FD_SET (p->infd, &input_wait_mask); + FD_SET (p->infd, &non_keyboard_wait_mask); } p->command = Qnil; return process; @@ -6306,7 +6295,7 @@ return process; proc = get_process (process); - coding = proc_encode_coding_system[XINT (XPROCESS (proc)->outfd)]; + coding = proc_encode_coding_system[XPROCESS (proc)->outfd]; /* Make sure the process is really alive. */ if (XPROCESS (proc)->raw_status_new) @@ -6323,7 +6312,7 @@ #ifdef VMS send_process (proc, "\032", 1, Qnil); /* ^z */ #else - if (!NILP (XPROCESS (proc)->pty_flag)) + if (XPROCESS (proc)->pty_flag) send_process (proc, "\004", 1, Qnil); else { @@ -6335,18 +6324,18 @@ (In some old system, shutdown to socketpair doesn't work. Then we just can't win.) */ if (XPROCESS (proc)->pid == 0 - || XINT (XPROCESS (proc)->outfd) == XINT (XPROCESS (proc)->infd)) - shutdown (XINT (XPROCESS (proc)->outfd), 1); + || XPROCESS (proc)->outfd == XPROCESS (proc)->infd) + shutdown (XPROCESS (proc)->outfd, 1); /* In case of socketpair, outfd == infd, so don't close it. */ - if (XINT (XPROCESS (proc)->outfd) != XINT (XPROCESS (proc)->infd)) - emacs_close (XINT (XPROCESS (proc)->outfd)); + if (XPROCESS (proc)->outfd != XPROCESS (proc)->infd) + emacs_close (XPROCESS (proc)->outfd); #else /* not HAVE_SHUTDOWN */ - emacs_close (XINT (XPROCESS (proc)->outfd)); + emacs_close (XPROCESS (proc)->outfd); #endif /* not HAVE_SHUTDOWN */ new_outfd = emacs_open (NULL_DEVICE, O_WRONLY, 0); if (new_outfd < 0) abort (); - old_outfd = XINT (XPROCESS (proc)->outfd); + old_outfd = XPROCESS (proc)->outfd; if (!proc_encode_coding_system[new_outfd]) proc_encode_coding_system[new_outfd] @@ -6357,7 +6346,7 @@ bzero (proc_encode_coding_system[old_outfd], sizeof (struct coding_system)); - XSETINT (XPROCESS (proc)->outfd, new_outfd); + XPROCESS (proc)->outfd = new_outfd; } #endif /* VMS */ return process; @@ -6380,7 +6369,7 @@ { if (NETCONN_P (proc)) Fdelete_process (proc); - else if (XINT (XPROCESS (proc)->infd) >= 0) + else if (XPROCESS (proc)->infd >= 0) process_send_signal (proc, SIGHUP, Qnil, 1); } } @@ -6510,21 +6499,21 @@ union { int i; WAITTYPE wt; } u; int clear_desc_flag = 0; - XSETINT (p->tick, ++process_tick); + p->tick = ++process_tick; u.wt = w; p->raw_status = u.i; p->raw_status_new = 1; /* If process has terminated, stop waiting for its output. */ if ((WIFSIGNALED (w) || WIFEXITED (w)) - && XINT (p->infd) >= 0) + && p->infd >= 0) clear_desc_flag = 1; /* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */ if (clear_desc_flag) { - FD_CLR (XINT (p->infd), &input_wait_mask); - FD_CLR (XINT (p->infd), &non_keyboard_wait_mask); + FD_CLR (p->infd, &input_wait_mask); + FD_CLR (p->infd, &non_keyboard_wait_mask); } /* Tell wait_reading_process_output that it needs to wake up and @@ -6701,18 +6690,18 @@ proc = Fcdr (Fcar (tail)); p = XPROCESS (proc); - if (XINT (p->tick) != XINT (p->update_tick)) + if (p->tick != p->update_tick) { - XSETINT (p->update_tick, XINT (p->tick)); + p->update_tick = p->tick; /* If process is still active, read any output that remains. */ while (! EQ (p->filter, Qt) && ! EQ (p->status, Qconnect) && ! EQ (p->status, Qlisten) && ! EQ (p->command, Qt) /* Network process not stopped. */ - && XINT (p->infd) >= 0 + && p->infd >= 0 && p != deleting_process - && read_process_output (proc, XINT (p->infd)) > 0); + && read_process_output (proc, p->infd) > 0); buffer = p->buffer; @@ -6739,7 +6728,7 @@ So set p->update_tick again so that an error in the sentinel will not cause this code to be run again. */ - XSETINT (p->update_tick, XINT (p->tick)); + p->update_tick = p->tick; /* Now output the message suitably. */ if (!NILP (p->sentinel)) exec_sentinel (proc, msg); @@ -6812,9 +6801,9 @@ CHECK_PROCESS (process); p = XPROCESS (process); - if (XINT (p->infd) < 0) + if (p->infd < 0) error ("Input file descriptor of %s closed", SDATA (p->name)); - if (XINT (p->outfd) < 0) + if (p->outfd < 0) error ("Output file descriptor of %s closed", SDATA (p->name)); Fcheck_coding_system (decoding); Fcheck_coding_system (encoding); @@ -6851,7 +6840,7 @@ CHECK_PROCESS (process); p = XPROCESS (process); - p->filter_multibyte = flag; + p->filter_multibyte = !NILP (flag); setup_process_coding_systems (process); return Qnil; @@ -6868,7 +6857,7 @@ CHECK_PROCESS (process); p = XPROCESS (process); - return (NILP (p->filter_multibyte) ? Qnil : Qt); + return (p->filter_multibyte ? Qt : Qnil); } diff -r a37d5bf6cbb7 -r a66921565bcb src/process.h --- a/src/process.h Tue Jul 10 02:07:45 2007 +0000 +++ b/src/process.h Sun Jul 15 04:47:46 2007 +0000 @@ -36,10 +36,6 @@ { EMACS_INT size; struct Lisp_Vector *v_next; - /* Descriptor by which we read from this process */ - Lisp_Object infd; - /* Descriptor by which we write to this process */ - Lisp_Object outfd; /* Name of subprocess terminal. */ Lisp_Object tty_name; /* Name of this process */ @@ -64,61 +60,65 @@ Lisp_Object plist; /* Marker set to end of last buffer-inserted output from this process */ Lisp_Object mark; - /* Non-nil means kill silently if Emacs is exited. - This is the inverse of the `query-on-exit' flag. */ - Lisp_Object kill_without_query; /* Symbol indicating status of process. This may be a symbol: run, open, or closed. Or it may be a list, whose car is stop, exit or signal and whose cdr is a pair (EXIT_CODE . COREDUMP_FLAG) or (SIGNAL_NUMBER . COREDUMP_FLAG). */ Lisp_Object status; - /* Non-nil if communicating through a pty. */ - Lisp_Object pty_flag; - /* Event-count of last event in which this process changed status. */ - Lisp_Object tick; - /* Event-count of last such event reported. */ - Lisp_Object update_tick; /* Coding-system for decoding the input from this process. */ Lisp_Object decode_coding_system; /* Working buffer for decoding. */ Lisp_Object decoding_buf; - /* Size of carryover in decoding. */ - Lisp_Object decoding_carryover; /* Coding-system for encoding the output to this process. */ Lisp_Object encode_coding_system; /* Working buffer for encoding. */ Lisp_Object encoding_buf; - /* Size of carryover in encoding. */ - Lisp_Object encoding_carryover; - /* Flag to set coding-system of the process buffer from the - coding_system used to decode process output. */ - Lisp_Object inherit_coding_system_flag; - /* Flat to decide the multibyteness of a string given to the - filter (if any). It is initialized to the value of - `default-enable-multibyte-characters' when the process is - generated, and can be changed by the function - `set-process-fileter-multibyte'. */ - Lisp_Object filter_multibyte; - /* Should we delay reading output from this process. - Initialized from `Vprocess_adaptive_read_buffering'. */ - Lisp_Object adaptive_read_buffering; - /* Hysteresis to try to read process output in larger blocks. - On some systems, e.g. GNU/Linux, Emacs is seen as - an interactive app also when reading process output, meaning - that process output can be read in as little as 1 byte at a - time. Value is micro-seconds to delay reading output from - this process. Range is 0 .. 50000. */ - Lisp_Object read_output_delay; - /* Skip reading this process on next read. */ - Lisp_Object read_output_skip; /* After this point, there are no Lisp_Objects any more. */ + /* alloc.c assumes that `pid' is the first such non-Lisp slot. */ /* Number of this process. allocate_process assumes this is the first non-Lisp_Object field. A value 0 is used for pseudo-processes such as network connections. */ pid_t pid; + /* Descriptor by which we read from this process */ + int infd; + /* Descriptor by which we write to this process */ + int outfd; + /* Event-count of last event in which this process changed status. */ + int tick; + /* Event-count of last such event reported. */ + int update_tick; + /* Size of carryover in decoding. */ + int decoding_carryover; + /* Hysteresis to try to read process output in larger blocks. + On some systems, e.g. GNU/Linux, Emacs is seen as + an interactive app also when reading process output, meaning + that process output can be read in as little as 1 byte at a + time. Value is micro-seconds to delay reading output from + this process. Range is 0 .. 50000. */ + int read_output_delay; + /* Should we delay reading output from this process. + Initialized from `Vprocess_adaptive_read_buffering'. + 0 = nil, 1 = t, 2 = other. */ + int adaptive_read_buffering : 2; + /* Skip reading this process on next read. */ + int read_output_skip : 1; + /* Non-nil means kill silently if Emacs is exited. + This is the inverse of the `query-on-exit' flag. */ + int kill_without_query : 1; + /* Non-nil if communicating through a pty. */ + int pty_flag : 1; + /* Flag to set coding-system of the process buffer from the + coding_system used to decode process output. */ + int inherit_coding_system_flag : 1; + /* Flag to decide the multibyteness of a string given to the + filter (if any). It is initialized to the value of + `default-enable-multibyte-characters' when the process is + generated, and can be changed by the function + `set-process-filter-multibyte'. */ + int filter_multibyte : 1; /* Record the process status in the raw form in which it comes from `wait'. This is to avoid consing in a signal handler. The `raw_status_new' flag indicates that `raw_status' contains a new status that still diff -r a37d5bf6cbb7 -r a66921565bcb src/search.c --- a/src/search.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/search.c Sun Jul 15 04:47:46 2007 +0000 @@ -93,6 +93,11 @@ Lisp_Object Vsearch_spaces_regexp; +/* If non-nil, the match data will not be changed during call to + searching or matching functions. This variable is for internal use + only. */ +Lisp_Object Vinhibit_changing_match_data; + static void set_search_regs (); static void save_search_regs (); static int simple_search (); @@ -289,7 +294,9 @@ = current_buffer->case_eqv_table; CHECK_STRING (string); - bufp = compile_pattern (string, &search_regs, + bufp = compile_pattern (string, + (NILP (Vinhibit_changing_match_data) + ? &search_regs : NULL), (!NILP (current_buffer->case_fold_search) ? current_buffer->case_canon_table : Qnil), posix, @@ -320,7 +327,9 @@ re_match_object = Qnil; i = re_match_2 (bufp, (char *) p1, s1, (char *) p2, s2, - PT_BYTE - BEGV_BYTE, &search_regs, + PT_BYTE - BEGV_BYTE, + (NILP (Vinhibit_changing_match_data) + ? &search_regs : NULL), ZV_BYTE - BEGV_BYTE); immediate_quit = 0; @@ -328,7 +337,7 @@ matcher_overflow (); val = (0 <= i ? Qt : Qnil); - if (i >= 0) + if (NILP (Vinhibit_changing_match_data) && i >= 0) for (i = 0; i < search_regs.num_regs; i++) if (search_regs.start[i] >= 0) { @@ -337,7 +346,11 @@ search_regs.end[i] = BYTE_TO_CHAR (search_regs.end[i] + BEGV_BYTE); } - XSETBUFFER (last_thing_searched, current_buffer); + + /* Set last_thing_searched only when match data is changed. */ + if (NILP (Vinhibit_changing_match_data)) + XSETBUFFER (last_thing_searched, current_buffer); + return val; } @@ -399,7 +412,9 @@ XCHAR_TABLE (current_buffer->case_canon_table)->extras[2] = current_buffer->case_eqv_table; - bufp = compile_pattern (regexp, &search_regs, + bufp = compile_pattern (regexp, + (NILP (Vinhibit_changing_match_data) + ? &search_regs : NULL), (!NILP (current_buffer->case_fold_search) ? current_buffer->case_canon_table : Qnil), posix, @@ -410,21 +425,27 @@ val = re_search (bufp, (char *) SDATA (string), SBYTES (string), pos_byte, SBYTES (string) - pos_byte, - &search_regs); + (NILP (Vinhibit_changing_match_data) + ? &search_regs : NULL)); immediate_quit = 0; - last_thing_searched = Qt; + + /* Set last_thing_searched only when match data is changed. */ + if (NILP (Vinhibit_changing_match_data)) + last_thing_searched = Qt; + if (val == -2) matcher_overflow (); if (val < 0) return Qnil; - for (i = 0; i < search_regs.num_regs; i++) - if (search_regs.start[i] >= 0) - { - search_regs.start[i] - = string_byte_to_char (string, search_regs.start[i]); - search_regs.end[i] - = string_byte_to_char (string, search_regs.end[i]); - } + if (NILP (Vinhibit_changing_match_data)) + for (i = 0; i < search_regs.num_regs; i++) + if (search_regs.start[i] >= 0) + { + search_regs.start[i] + = string_byte_to_char (string, search_regs.start[i]); + search_regs.end[i] + = string_byte_to_char (string, search_regs.end[i]); + } return make_number (string_byte_to_char (string, val)); } @@ -1042,6 +1063,11 @@ } \ while (0) +/* Only used in search_buffer, to record the end position of the match + when searching regexps and SEARCH_REGS should not be changed + (i.e. Vinhibit_changing_match_data is non-nil). */ +static struct re_registers search_regs_1; + static int search_buffer (string, pos, pos_byte, lim, lim_byte, n, RE, trt, inverse_trt, posix) @@ -1077,7 +1103,10 @@ int s1, s2; struct re_pattern_buffer *bufp; - bufp = compile_pattern (string, &search_regs, trt, posix, + bufp = compile_pattern (string, + (NILP (Vinhibit_changing_match_data) + ? &search_regs : &search_regs_1), + trt, posix, !NILP (current_buffer->enable_multibyte_characters)); immediate_quit = 1; /* Quit immediately if user types ^G, @@ -1110,7 +1139,8 @@ int val; val = re_search_2 (bufp, (char *) p1, s1, (char *) p2, s2, pos_byte - BEGV_BYTE, lim_byte - pos_byte, - &search_regs, + (NILP (Vinhibit_changing_match_data) + ? &search_regs : &search_regs_1), /* Don't allow match past current point */ pos_byte - BEGV_BYTE); if (val == -2) @@ -1119,18 +1149,27 @@ } if (val >= 0) { - pos_byte = search_regs.start[0] + BEGV_BYTE; - for (i = 0; i < search_regs.num_regs; i++) - if (search_regs.start[i] >= 0) - { - search_regs.start[i] - = BYTE_TO_CHAR (search_regs.start[i] + BEGV_BYTE); - search_regs.end[i] - = BYTE_TO_CHAR (search_regs.end[i] + BEGV_BYTE); - } - XSETBUFFER (last_thing_searched, current_buffer); - /* Set pos to the new position. */ - pos = search_regs.start[0]; + if (NILP (Vinhibit_changing_match_data)) + { + pos_byte = search_regs.start[0] + BEGV_BYTE; + for (i = 0; i < search_regs.num_regs; i++) + if (search_regs.start[i] >= 0) + { + search_regs.start[i] + = BYTE_TO_CHAR (search_regs.start[i] + BEGV_BYTE); + search_regs.end[i] + = BYTE_TO_CHAR (search_regs.end[i] + BEGV_BYTE); + } + XSETBUFFER (last_thing_searched, current_buffer); + /* Set pos to the new position. */ + pos = search_regs.start[0]; + } + else + { + pos_byte = search_regs_1.start[0] + BEGV_BYTE; + /* Set pos to the new position. */ + pos = BYTE_TO_CHAR (search_regs_1.start[0] + BEGV_BYTE); + } } else { @@ -1144,7 +1183,8 @@ int val; val = re_search_2 (bufp, (char *) p1, s1, (char *) p2, s2, pos_byte - BEGV_BYTE, lim_byte - pos_byte, - &search_regs, + (NILP (Vinhibit_changing_match_data) + ? &search_regs : &search_regs_1), lim_byte - BEGV_BYTE); if (val == -2) { @@ -1152,17 +1192,25 @@ } if (val >= 0) { - pos_byte = search_regs.end[0] + BEGV_BYTE; - for (i = 0; i < search_regs.num_regs; i++) - if (search_regs.start[i] >= 0) - { - search_regs.start[i] - = BYTE_TO_CHAR (search_regs.start[i] + BEGV_BYTE); - search_regs.end[i] - = BYTE_TO_CHAR (search_regs.end[i] + BEGV_BYTE); - } - XSETBUFFER (last_thing_searched, current_buffer); - pos = search_regs.end[0]; + if (NILP (Vinhibit_changing_match_data)) + { + pos_byte = search_regs.end[0] + BEGV_BYTE; + for (i = 0; i < search_regs.num_regs; i++) + if (search_regs.start[i] >= 0) + { + search_regs.start[i] + = BYTE_TO_CHAR (search_regs.start[i] + BEGV_BYTE); + search_regs.end[i] + = BYTE_TO_CHAR (search_regs.end[i] + BEGV_BYTE); + } + XSETBUFFER (last_thing_searched, current_buffer); + pos = search_regs.end[0]; + } + else + { + pos_byte = search_regs_1.end[0] + BEGV_BYTE; + pos = BYTE_TO_CHAR (search_regs_1.end[0] + BEGV_BYTE); + } } else { @@ -1907,7 +1955,7 @@ cursor += dirlen - i - direction; /* fix cursor */ if (i + direction == 0) { - int position; + int position, start, end; cursor -= direction; @@ -1915,11 +1963,24 @@ ? 1 - len_byte : 0); set_search_regs (position, len_byte); + if (NILP (Vinhibit_changing_match_data)) + { + start = search_regs.start[0]; + end = search_regs.end[0]; + } + else + /* If Vinhibit_changing_match_data is non-nil, + search_regs will not be changed. So let's + compute start and end here. */ + { + start = BYTE_TO_CHAR (position); + end = BYTE_TO_CHAR (position + len_byte); + } + if ((n -= direction) != 0) cursor += dirlen; /* to resume search */ else - return ((direction > 0) - ? search_regs.end[0] : search_regs.start[0]); + return direction > 0 ? end : start; } else cursor += stride_for_teases; /* we lose - */ @@ -1984,18 +2045,30 @@ pos_byte += dirlen - i- direction; if (i + direction == 0) { - int position; + int position, start, end; pos_byte -= direction; position = pos_byte + ((direction > 0) ? 1 - len_byte : 0); - set_search_regs (position, len_byte); + if (NILP (Vinhibit_changing_match_data)) + { + start = search_regs.start[0]; + end = search_regs.end[0]; + } + else + /* If Vinhibit_changing_match_data is non-nil, + search_regs will not be changed. So let's + compute start and end here. */ + { + start = BYTE_TO_CHAR (position); + end = BYTE_TO_CHAR (position + len_byte); + } + if ((n -= direction) != 0) pos_byte += dirlen; /* to resume search */ else - return ((direction > 0) - ? search_regs.end[0] : search_regs.start[0]); + return direction > 0 ? end : start; } else pos_byte += stride_for_teases; @@ -2018,6 +2091,9 @@ { int i; + if (!NILP (Vinhibit_changing_match_data)) + return; + /* Make sure we have registers in which to store the match position. */ if (search_regs.num_regs == 0) @@ -3145,6 +3221,13 @@ A value of nil (which is the normal value) means treat spaces literally. */); Vsearch_spaces_regexp = Qnil; + DEFVAR_LISP ("inhibit-changing-match-data", &Vinhibit_changing_match_data, + doc: /* Internal use only. +If non-nil, the match data will not be changed during call to searching or +matching functions, such as `looking-at', `string-match', `re-search-forward' +etc. */); + Vinhibit_changing_match_data = Qnil; + defsubr (&Slooking_at); defsubr (&Sposix_looking_at); defsubr (&Sstring_match); diff -r a37d5bf6cbb7 -r a66921565bcb src/term.c --- a/src/term.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/term.c Sun Jul 15 04:47:46 2007 +0000 @@ -25,6 +25,9 @@ #include #include #include +#ifdef HAVE_UNISTD_H +#include +#endif #include "termchar.h" #include "termopts.h" @@ -2493,9 +2496,9 @@ void term_mouse_moveto (int x, int y) { + /* TODO: how to set mouse position? const char *name; int fd; - /* TODO: how to set mouse position? name = (const char *) ttyname (0); fd = open (name, O_WRONLY); SOME_FUNCTION (x, y, fd); @@ -2509,7 +2512,7 @@ { struct window *w = XWINDOW (Qmouse_face_window); int save_x, save_y; - int i, j; + int i; if (/* If window is in the process of being destroyed, don't bother to do anything. */ @@ -3029,7 +3032,7 @@ handle_one_term_event (Gpm_Event *event, struct input_event* hold_quit) { struct frame *f = SELECTED_FRAME (); - int i, j, fd; + int fd; struct input_event ie; int do_help = 0; int count = 0; @@ -3053,7 +3056,7 @@ arg[1] = arg[3] = (unsigned short) event->y + gpm_zerobased; arg[4] = (unsigned short) 3; - name = (const char *) ttyname (0); + name = ttyname (0); fd = open (name, O_WRONLY); ioctl (fd, TIOCLINUX, buf + sizeof (short) - 1); close (fd); diff -r a37d5bf6cbb7 -r a66921565bcb src/w32fns.c --- a/src/w32fns.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/w32fns.c Sun Jul 15 04:47:46 2007 +0000 @@ -53,6 +53,7 @@ #include #include #include +#include #include #define FILE_NAME_TEXT_FIELD edt1 @@ -2518,6 +2519,13 @@ /* Produced by complete_deferred_msg; just ignore. */ break; case WM_EMACS_CREATEWINDOW: + /* Initialize COM for this window. Even though we don't use it, + some third party shell extensions can cause it to be used in + system dialogs, which causes a crash if it is not initialized. + This is a known bug in Windows, which was fixed long ago, but + the patch for XP is not publically available until XP SP3, + and older versions will never be patched. */ + CoInitialize (NULL); w32_createwindow ((struct frame *) msg.wParam); if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0)) abort (); @@ -3664,6 +3672,10 @@ my_post_msg (&wmsg, hwnd, msg, wParam, lParam); goto dflt; + case WM_DESTROY: + CoUninitialize (); + return 0; + case WM_CLOSE: wmsg.dwModifiers = w32_get_modifiers (); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); diff -r a37d5bf6cbb7 -r a66921565bcb src/window.c --- a/src/window.c Tue Jul 10 02:07:45 2007 +0000 +++ b/src/window.c Sun Jul 15 04:47:46 2007 +0000 @@ -6643,7 +6643,7 @@ Also restore the choice of selected window. Also restore which buffer is current. Does not restore the value of point in current buffer. -usage: (save-window-excursion BODY ...) */) +usage: (save-window-excursion BODY...) */) (args) Lisp_Object args; {