Merge from origin/emacs-26
3e72298
Improve documentation of 'pcase-defmacro rx'ba9b9bb
Fix TTY colors breakage by 'clear-face-cache'f56ad42
* admin/MAINTAINERS: Add files maintained by me (Michael Albi...7a258fa
Adapt shadowfile.el for Tramp (Bug#4526, Bug#4846)cb50077
Fix auth-source-delete (Bug#26184)a4767a6
Avoid assertion violations in gnutls.c90110f8
Don't use a literal "C-u" in ispell.el help message textf4e7f6d
Improve documentation of 'seqp'ed13639
Clarify usage and dependencies between several Flyspell features Conflicts: etc/NEWS test/lisp/auth-source-tests.el
This commit is contained in:
commit
6ee0032461
12 changed files with 1221 additions and 266 deletions
|
@ -61,7 +61,7 @@ Michael Albinus
|
|||
lisp/net/tramp*.el
|
||||
lisp/url/url-tramp.el
|
||||
doc/misc/tramp*.texi
|
||||
test/lisp/net/tramp-tests.el
|
||||
test/lisp/net/tramp*-tests.el
|
||||
test/lisp/url/url-tramp-tests.el
|
||||
|
||||
D-Bus
|
||||
|
@ -210,11 +210,21 @@ Paul Eggert
|
|||
Michael Albinus
|
||||
src/inotify.c
|
||||
lisp/autorevert.el
|
||||
lisp/files.el (file-name-non-special)
|
||||
lisp/eshell/em-tramp.el
|
||||
lisp/net/ange-ftp.el
|
||||
lisp/notifications.el
|
||||
lisp/shadowfile.el
|
||||
test/lisp/autorevert-tests.el
|
||||
test/lisp/files-tests.el (file-name-non-special)
|
||||
test/lisp/shadowfile-tests.el
|
||||
test/src/inotify-test.el
|
||||
|
||||
Secret Service API in
|
||||
lisp/auth-source.el
|
||||
doc/misc/auth.texi
|
||||
test/lisp/auth-source-tests.el
|
||||
|
||||
Nicolas Petton
|
||||
lisp/emacs-lisp/subr-x.el
|
||||
lisp/arc-mode.el
|
||||
|
|
|
@ -63,7 +63,8 @@ But it is possible to add elements to the list, or remove elements.
|
|||
|
||||
@defun sequencep object
|
||||
This function returns @code{t} if @var{object} is a list, vector,
|
||||
string, bool-vector, or char-table, @code{nil} otherwise.
|
||||
string, bool-vector, or char-table, @code{nil} otherwise. See also
|
||||
@code{seqp} below.
|
||||
@end defun
|
||||
|
||||
@defun length sequence
|
||||
|
@ -479,7 +480,8 @@ built-in sequence types, @code{seq-length} behaves like @code{length}.
|
|||
@defun seqp object
|
||||
This function returns non-@code{nil} if @var{object} is a sequence
|
||||
(a list or array), or any additional type of sequence defined via
|
||||
@file{seq.el} generic functions.
|
||||
@file{seq.el} generic functions. This is an extensible variant of
|
||||
@code{sequencep}.
|
||||
|
||||
@example
|
||||
@group
|
||||
|
|
|
@ -85,12 +85,20 @@ it now shows the global revision number, in the form of its changeset
|
|||
hash value. To get back the previous behavior, customize the new
|
||||
option 'vc-hg-symbolic-revision-styles' to the value '("{rev}")'.
|
||||
|
||||
---
|
||||
** shadowfile.el has been rewritten to support Tramp file names.
|
||||
|
||||
|
||||
* New Modes and Packages in Emacs 26.2
|
||||
|
||||
|
||||
* Incompatible Lisp Changes in Emacs 26.2
|
||||
|
||||
---
|
||||
** shadowfile config files have changed their syntax.
|
||||
Existing files "~/.emacs.d/shadows" and "~/.emacs.d/shadow_todo" must
|
||||
be removed prior using the changed 'shadow-*' commands.
|
||||
|
||||
|
||||
* Lisp Changes in Emacs 26.2
|
||||
|
||||
|
|
|
@ -779,7 +779,7 @@ Calls `auth-source-search' with the :delete property in SPEC set to t.
|
|||
The backend may not actually delete the entries.
|
||||
|
||||
Returns the deleted entries."
|
||||
(auth-source-search (plist-put spec :delete t)))
|
||||
(apply #'auth-source-search (plist-put spec :delete t)))
|
||||
|
||||
(defun auth-source-search-collection (collection value)
|
||||
"Returns t is VALUE is t or COLLECTION is t or COLLECTION contains VALUE."
|
||||
|
|
|
@ -1183,24 +1183,28 @@ enclosed in `(and ...)'.
|
|||
|
||||
|
||||
(pcase-defmacro rx (&rest regexps)
|
||||
"Build a `pcase' pattern matching `rx' regexps.
|
||||
The REGEXPS are interpreted as by `rx'. The pattern matches if
|
||||
the regular expression so constructed matches EXPVAL, as if
|
||||
by `string-match'.
|
||||
"Build a `pcase' pattern matching `rx' REGEXPS in sexp form.
|
||||
The REGEXPS are interpreted as in `rx'. The pattern matches any
|
||||
string that is a match for the regular expression so constructed,
|
||||
as if by `string-match'.
|
||||
|
||||
In addition to the usual `rx' constructs, REGEXPS can contain the
|
||||
following constructs:
|
||||
|
||||
(let VAR FORM...) creates a new explicitly numbered submatch
|
||||
that matches FORM and binds the match to
|
||||
VAR.
|
||||
(backref VAR) creates a backreference to the submatch
|
||||
introduced by a previous (let VAR ...)
|
||||
construct.
|
||||
(let REF SEXP...) creates a new explicitly named reference to
|
||||
a submatch that matches regular expressions
|
||||
SEXP, and binds the match to REF.
|
||||
(backref REF) creates a backreference to the submatch
|
||||
introduced by a previous (let REF ...)
|
||||
construct. REF can be the same symbol
|
||||
in the first argument of the corresponding
|
||||
(let REF ...) construct, or it can be a
|
||||
submatch number. It matches the referenced
|
||||
submatch.
|
||||
|
||||
The VARs are associated with explicitly numbered submatches
|
||||
starting from 1. Multiple occurrences of the same VAR refer to
|
||||
the same submatch.
|
||||
The REFs are associated with explicitly named submatches starting
|
||||
from 1. Multiple occurrences of the same REF refer to the same
|
||||
submatch.
|
||||
|
||||
If a case matches, the match data is modified as usual so you can
|
||||
use it in the case body, but you still have to pass the correct
|
||||
|
|
|
@ -25,37 +25,38 @@
|
|||
;; This package helps you to keep identical copies of files in more than one
|
||||
;; place - possibly on different machines. When you save a file, it checks
|
||||
;; whether it is on the list of files with "shadows", and if so, it tries to
|
||||
;; copy it when you exit Emacs (or use the shadow-copy-files command).
|
||||
;; copy it when you exit Emacs (or use the `shadow-copy-files' command).
|
||||
|
||||
;; Installation & Use:
|
||||
|
||||
;; Add clusters (if necessary) and file groups with shadow-define-cluster,
|
||||
;; shadow-define-literal-group, and shadow-define-regexp-group (see the
|
||||
;; Add clusters (if necessary) and file groups with `shadow-define-cluster',
|
||||
;; `shadow-define-literal-group', and `shadow-define-regexp-group' (see the
|
||||
;; documentation for these functions for information on how and when to use
|
||||
;; them). After doing this once, everything should be automatic.
|
||||
|
||||
;; The lists of clusters and shadows are saved in a ~/.emacs.d/shadows
|
||||
;; (`shadow-info-file') file, so that they can be remembered from one
|
||||
;; Emacs session to another, even (as much as possible) if the Emacs
|
||||
;; session terminates abnormally. The files needing to be copied are
|
||||
;; stored in `shadow-todo-file'; if a file cannot be copied for any
|
||||
;; reason, it will stay on the list to be tried again next time. The
|
||||
;; `shadow-info-file' file should itself have shadows on all your accounts
|
||||
;; so that the information in it is consistent everywhere, but
|
||||
;; `shadow-todo-file' is local information and should have no shadows.
|
||||
;; The lists of clusters and shadows are saved in `shadow-info-file',
|
||||
;; so that they can be remembered from one Emacs session to another,
|
||||
;; even (as much as possible) if the Emacs session terminates
|
||||
;; abnormally. The files needing to be copied are stored in
|
||||
;; `shadow-todo-file'; if a file cannot be copied for any reason, it
|
||||
;; will stay on the list to be tried again next time. The
|
||||
;; `shadow-info-file' file should itself have shadows on all your
|
||||
;; accounts so that the information in it is consistent everywhere,
|
||||
;; but `shadow-todo-file' is local information and should have no
|
||||
;; shadows.
|
||||
|
||||
;; If you do not want to copy a particular file, you can answer "no" and
|
||||
;; be asked again next time you hit C-x 4 s or exit Emacs. If you do not
|
||||
;; want to be asked again, use shadow-cancel, and you will not be asked
|
||||
;; be asked again next time you hit "C-x 4 s" or exit Emacs. If you do not
|
||||
;; want to be asked again, use "M-x shadow-cancel", and you will not be asked
|
||||
;; until you change the file and save it again. If you do not want to
|
||||
;; shadow that file ever again, you can edit it out of the shadows
|
||||
;; buffer. Anytime you edit the shadows buffer, you must type M-x
|
||||
;; shadow-read-files to load in the new information, or your changes will
|
||||
;; buffer. Anytime you edit the shadows buffer, you must type "M-x
|
||||
;; shadow-read-files" to load in the new information, or your changes will
|
||||
;; be overwritten!
|
||||
|
||||
;; Bugs & Warnings:
|
||||
;;
|
||||
;; - It is bad to have two emacses both running shadowfile at the same
|
||||
;; - It is bad to have two Emacsen both running shadowfile at the same
|
||||
;; time. It tries to detect this condition, but is not always successful.
|
||||
;;
|
||||
;; - You have to be careful not to edit a file in two locations
|
||||
|
@ -64,19 +65,16 @@
|
|||
;;
|
||||
;; - It ought to check modification times of both files to make sure
|
||||
;; it is doing the right thing. This will have to wait until
|
||||
;; file-newer-than-file-p works between machines.
|
||||
;; `file-newer-than-file-p' works between machines.
|
||||
;;
|
||||
;; - It will not make directories for you, it just fails to copy files
|
||||
;; that belong in non-existent directories.
|
||||
;;
|
||||
;; Please report any bugs to me (boris@gnu.org). Also let me know
|
||||
;; if you have suggestions or would like to be informed of updates.
|
||||
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'ange-ftp)
|
||||
(require 'tramp)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Variables
|
||||
|
@ -107,35 +105,35 @@ files that have been changed and need to be copied to other systems."
|
|||
:type 'boolean
|
||||
:group 'shadow)
|
||||
|
||||
;; FIXME in a sense, this changed in 24.4 (addition of locate-user-emacs-file),
|
||||
;; but due to the weird way this variable is initialized to nil, it didn't
|
||||
;; literally change. Same for shadow-todo-file.
|
||||
(defcustom shadow-info-file nil
|
||||
(defcustom shadow-info-file (locate-user-emacs-file "shadows" ".shadows")
|
||||
"File to keep shadow information in.
|
||||
The `shadow-info-file' should be shadowed to all your accounts to
|
||||
ensure consistency. Default: ~/.emacs.d/shadows"
|
||||
:type '(choice (const nil) file)
|
||||
:group 'shadow)
|
||||
:type 'file
|
||||
:group 'shadow
|
||||
:version "26.2")
|
||||
|
||||
(defcustom shadow-todo-file nil
|
||||
(defcustom shadow-todo-file
|
||||
(locate-user-emacs-file "shadow_todo" ".shadow_todo")
|
||||
"File to store the list of uncopied shadows in.
|
||||
This means that if a remote system is down, or for any reason you cannot or
|
||||
decide not to copy your shadow files at the end of one Emacs session, it will
|
||||
remember and ask you again in your next Emacs session.
|
||||
This file must NOT be shadowed to any other system, it is host-specific.
|
||||
Default: ~/.emacs.d/shadow_todo"
|
||||
:type '(choice (const nil) file)
|
||||
:group 'shadow)
|
||||
:type 'file
|
||||
:group 'shadow
|
||||
:version "26.2")
|
||||
|
||||
|
||||
;;; The following two variables should in most cases initialize themselves
|
||||
;;; correctly. They are provided as variables in case the defaults are wrong
|
||||
;;; on your machine (and for efficiency).
|
||||
|
||||
(defvar shadow-system-name (system-name)
|
||||
"The complete hostname of this machine.")
|
||||
(defvar shadow-system-name (concat "/" (system-name) ":")
|
||||
"The identification for local files on this machine.")
|
||||
|
||||
(defvar shadow-homedir nil
|
||||
(defvar shadow-homedir "~"
|
||||
"Your home directory on this machine.")
|
||||
|
||||
;;;
|
||||
|
@ -186,12 +184,12 @@ created by `shadow-define-regexp-group'.")
|
|||
(car list))
|
||||
|
||||
(defun shadow-regexp-superquote (string)
|
||||
"Like `regexp-quote', but includes the ^ and $.
|
||||
"Like `regexp-quote', but includes the \\` and \\'.
|
||||
This makes sure regexp matches nothing but STRING."
|
||||
(concat "^" (regexp-quote string) "$"))
|
||||
(concat "\\`" (regexp-quote string) "\\'"))
|
||||
|
||||
(defun shadow-suffix (prefix string)
|
||||
"If PREFIX begins STRING, return the rest.
|
||||
"If PREFIX begins with STRING, return the rest.
|
||||
Return value is non-nil if PREFIX and STRING are `string=' up to the length of
|
||||
PREFIX."
|
||||
(let ((lp (length prefix))
|
||||
|
@ -204,70 +202,66 @@ PREFIX."
|
|||
;;; Clusters and sites
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;; I use the term `site' to refer to a string which may be the name of a
|
||||
;;; cluster or a literal hostname. All user-level commands should accept
|
||||
;;; either.
|
||||
;;; I use the term `site' to refer to a string which may be the
|
||||
;;; cluster identification "/name:", a remote identification
|
||||
;;; "/method:user@host:", or "/system-name:' (the value of
|
||||
;;; `shadow-system-name') for the location of local files. All
|
||||
;;; user-level commands should accept either.
|
||||
|
||||
(defun shadow-make-cluster (name primary regexp)
|
||||
"Create a shadow cluster.
|
||||
It is called NAME, uses the PRIMARY hostname and REGEXP matching all
|
||||
hosts in the cluster. The variable `shadow-clusters' associates the
|
||||
names of clusters to these structures. This function is for program
|
||||
use: to create clusters interactively, use `shadow-define-cluster'
|
||||
instead."
|
||||
(list name primary regexp))
|
||||
|
||||
(defmacro shadow-cluster-name (cluster)
|
||||
"Return the name of the CLUSTER."
|
||||
(list 'elt cluster 0))
|
||||
|
||||
(defmacro shadow-cluster-primary (cluster)
|
||||
"Return the primary hostname of a CLUSTER."
|
||||
(list 'elt cluster 1))
|
||||
|
||||
(defmacro shadow-cluster-regexp (cluster)
|
||||
"Return the regexp matching hosts in a CLUSTER."
|
||||
(list 'elt cluster 2))
|
||||
(cl-defstruct (shadow-cluster (:type list) :named) name primary regexp)
|
||||
|
||||
(defun shadow-set-cluster (name primary regexp)
|
||||
"Put cluster NAME on the list of clusters.
|
||||
Replace old definition, if any. PRIMARY and REGEXP are the
|
||||
information defining the cluster. For interactive use, call
|
||||
`shadow-define-cluster' instead."
|
||||
(let ((rest (cl-remove-if (lambda (x) (equal name (car x)))
|
||||
(let ((rest (cl-remove-if (lambda (x) (equal name (shadow-cluster-name x)))
|
||||
shadow-clusters)))
|
||||
(setq shadow-clusters
|
||||
(cons (shadow-make-cluster name primary regexp)
|
||||
(cons (make-shadow-cluster :name name :primary primary :regexp regexp)
|
||||
rest))))
|
||||
|
||||
(defmacro shadow-get-cluster (name)
|
||||
(defun shadow-get-cluster (name)
|
||||
"Return cluster named NAME, or nil."
|
||||
(list 'assoc name 'shadow-clusters))
|
||||
|
||||
(defun shadow-site-primary (site)
|
||||
"If SITE is a cluster, return primary host, otherwise return SITE."
|
||||
(let ((c (shadow-get-cluster site)))
|
||||
(if c
|
||||
(shadow-cluster-primary c)
|
||||
site)))
|
||||
(shadow-find
|
||||
(lambda (x) (string-equal (shadow-cluster-name x) name))
|
||||
shadow-clusters))
|
||||
|
||||
;;; SITES
|
||||
|
||||
(defun shadow-site-name (site)
|
||||
"Return name if SITE has the form \"/name:\", otherwise SITE."
|
||||
(if (string-match "\\`/\\(\\w+\\):\\'" site)
|
||||
(match-string 1 site) site))
|
||||
|
||||
(defun shadow-name-site (name)
|
||||
"Return \"/name:\" if NAME has word syntax, otherwise NAME."
|
||||
(if (string-match "\\`\\w+\\'" name)
|
||||
(format "/%s:"name) name))
|
||||
|
||||
(defun shadow-site-primary (site)
|
||||
"If SITE is a cluster, return primary identification, otherwise return SITE."
|
||||
(let ((cluster (shadow-get-cluster (shadow-site-name site))))
|
||||
(if cluster
|
||||
(shadow-cluster-primary cluster)
|
||||
site)))
|
||||
|
||||
(defun shadow-site-cluster (site)
|
||||
"Given a SITE (hostname or cluster name), return cluster it is in, or nil."
|
||||
(or (assoc site shadow-clusters)
|
||||
"Given a SITE, return cluster it is in, or nil."
|
||||
(or (shadow-get-cluster (shadow-site-name site))
|
||||
(shadow-find
|
||||
(function (lambda (x)
|
||||
(string-match (shadow-cluster-regexp x)
|
||||
site)))
|
||||
(lambda (x)
|
||||
(string-match (shadow-cluster-regexp x) (shadow-name-site site)))
|
||||
shadow-clusters)))
|
||||
|
||||
(defun shadow-read-site ()
|
||||
"Read a cluster name or hostname from the minibuffer."
|
||||
(let ((ans (completing-read "Host or cluster name [RET when done]: "
|
||||
"Read a cluster name or host identification from the minibuffer."
|
||||
(let ((ans (completing-read "Host identification or cluster name: "
|
||||
shadow-clusters)))
|
||||
(if (equal "" ans)
|
||||
nil
|
||||
(when (or (shadow-get-cluster (shadow-site-name ans))
|
||||
(string-equal ans shadow-system-name)
|
||||
(string-equal ans (shadow-site-name shadow-system-name))
|
||||
(setq ans (file-remote-p ans)))
|
||||
ans)))
|
||||
|
||||
(defun shadow-site-match (site1 site2)
|
||||
|
@ -281,63 +275,95 @@ be matched against the primary of SITE2."
|
|||
(string-match (shadow-cluster-regexp cluster1) primary2)
|
||||
(string-equal site1 primary2)))))
|
||||
|
||||
(defun shadow-get-user (site)
|
||||
"Return the default username for a SITE."
|
||||
(ange-ftp-get-user (shadow-site-primary site)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Filename manipulation
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun shadow-parse-fullname (fullname)
|
||||
"Parse FULLNAME into (site user path) list.
|
||||
Leave it alone if it already is one. Return nil if the argument is
|
||||
not a full ange-ftp pathname."
|
||||
(if (listp fullname)
|
||||
fullname
|
||||
(ange-ftp-ftp-name fullname)))
|
||||
|
||||
(defun shadow-parse-name (name)
|
||||
"Parse any NAME into (site user name) list.
|
||||
Argument can be a simple name, full ange-ftp name, or already a hup list."
|
||||
(or (shadow-parse-fullname name)
|
||||
(list shadow-system-name
|
||||
(user-login-name)
|
||||
name)))
|
||||
"Parse any NAME into a `tramp-file-name' structure.
|
||||
Argument can be a simple name, remote file name, or already a
|
||||
`tramp-file-name' structure."
|
||||
(cond
|
||||
((null name) nil)
|
||||
((tramp-file-name-p name) name)
|
||||
((file-remote-p name) (tramp-dissect-file-name name))
|
||||
((shadow-local-file name)
|
||||
(make-tramp-file-name
|
||||
:host (shadow-site-name shadow-system-name)
|
||||
:localname (shadow-local-file name)))
|
||||
;; Cluster name.
|
||||
((string-match "^/\\([^:/]+\\):\\([^:]*\\)$" name)
|
||||
(let ((name (match-string 1 name))
|
||||
(file (match-string 2 name)))
|
||||
(when (shadow-get-cluster name)
|
||||
(make-tramp-file-name :host name :localname file))))))
|
||||
|
||||
(defsubst shadow-make-fullname (host user name)
|
||||
"Make an ange-ftp style fullname out of HOST, USER (optional), and NAME.
|
||||
This is probably not as general as it ought to be."
|
||||
(concat "/"
|
||||
(if user (concat user "@"))
|
||||
host ":"
|
||||
name))
|
||||
(defsubst shadow-make-fullname (hup &optional host name)
|
||||
"Make a Tramp style fullname out of HUP, a `tramp-file-name' structure.
|
||||
Replace HOST, and NAME when non-nil."
|
||||
(let ((hup (copy-tramp-file-name hup)))
|
||||
(when host (setf (tramp-file-name-host hup) host))
|
||||
(when name (setf (tramp-file-name-localname hup) name))
|
||||
(if (null (tramp-file-name-method hup))
|
||||
(format
|
||||
"/%s:%s" (tramp-file-name-host hup) (tramp-file-name-localname hup))
|
||||
(tramp-make-tramp-file-name
|
||||
(tramp-file-name-method hup)
|
||||
(tramp-file-name-user hup)
|
||||
(tramp-file-name-domain hup)
|
||||
(tramp-file-name-host hup)
|
||||
(tramp-file-name-port hup)
|
||||
(tramp-file-name-localname hup)
|
||||
(tramp-file-name-hop hup)))))
|
||||
|
||||
(defun shadow-replace-name-component (fullname newname)
|
||||
"Return FULLNAME with the name component changed to NEWNAME."
|
||||
(let ((hup (shadow-parse-fullname fullname)))
|
||||
(shadow-make-fullname (nth 0 hup) (nth 1 hup) newname)))
|
||||
(concat (file-remote-p fullname) newname))
|
||||
|
||||
(defun shadow-local-file (file)
|
||||
"If FILE is at this site, remove /user@host part.
|
||||
If refers to a different system or a different user on this system,
|
||||
return nil."
|
||||
(let ((hup (shadow-parse-fullname file)))
|
||||
(cond ((null hup) file)
|
||||
((and (shadow-site-match (nth 0 hup) shadow-system-name)
|
||||
(string-equal (nth 1 hup) (user-login-name)))
|
||||
(nth 2 hup))
|
||||
(t nil))))
|
||||
"If FILE is not remote, return it.
|
||||
If it refers to a different system, return nil."
|
||||
(cond
|
||||
((null file) nil)
|
||||
;; `tramp-file-name' structure.
|
||||
((and (tramp-file-name-p file) (null (tramp-file-name-method file)))
|
||||
(tramp-file-name-localname file))
|
||||
((tramp-file-name-p file) nil)
|
||||
;; Local host name.
|
||||
((string-match
|
||||
(format "^%s\\([^:]*\\)$" (regexp-quote shadow-system-name)) file)
|
||||
(match-string 1 file))
|
||||
;; Cluster name.
|
||||
((and (string-match "^/\\([^:/]+\\):\\([^:]*\\)$" file)
|
||||
(shadow-get-cluster (match-string 1 file)))
|
||||
(let ((file (match-string 2 file))
|
||||
(primary
|
||||
(shadow-cluster-primary
|
||||
(shadow-get-cluster (match-string 1 file)))))
|
||||
(when (string-equal primary shadow-system-name) (setq primary nil))
|
||||
(shadow-local-file (concat primary file))))
|
||||
;; Local name.
|
||||
((null (file-remote-p file)) file)))
|
||||
|
||||
(defun shadow-expand-cluster-in-file-name (file)
|
||||
"If hostname part of FILE is a cluster, expand it to cluster's primary hostname.
|
||||
Will return the name bare if it is a local file."
|
||||
(let ((hup (shadow-parse-name file)))
|
||||
(cond ((null hup) file)
|
||||
((shadow-local-file hup))
|
||||
((shadow-make-fullname (shadow-site-primary (nth 0 hup))
|
||||
(nth 1 hup)
|
||||
(nth 2 hup))))))
|
||||
(when (stringp file)
|
||||
(cond
|
||||
;; Local file.
|
||||
((shadow-local-file file))
|
||||
;; Cluster name.
|
||||
((string-match "^\\(/[^:/]+:\\)[^:]*$" file)
|
||||
(let ((primary
|
||||
(save-match-data
|
||||
(shadow-cluster-primary
|
||||
(shadow-get-cluster
|
||||
(shadow-site-name (match-string 1 file)))))))
|
||||
(if (not primary)
|
||||
file
|
||||
(setq file (replace-match primary nil nil file 1))
|
||||
(or (shadow-local-file file) file))))
|
||||
(t file))))
|
||||
|
||||
(defun shadow-expand-file-name (file &optional default)
|
||||
"Expand file name and get FILE's true name."
|
||||
|
@ -352,46 +378,50 @@ true."
|
|||
(homedir (if (shadow-local-file hup)
|
||||
shadow-homedir
|
||||
(file-name-as-directory
|
||||
(nth 2 (shadow-parse-fullname
|
||||
(expand-file-name
|
||||
(shadow-make-fullname
|
||||
(nth 0 hup) (nth 1 hup) "~")))))))
|
||||
(suffix (shadow-suffix homedir (nth 2 hup)))
|
||||
(cluster (shadow-site-cluster (nth 0 hup))))
|
||||
(file-local-name
|
||||
(expand-file-name (shadow-make-fullname hup nil "~"))))))
|
||||
(suffix (shadow-suffix homedir (tramp-file-name-localname hup)))
|
||||
(cluster (shadow-site-cluster (shadow-make-fullname hup nil ""))))
|
||||
(when cluster
|
||||
(setf (tramp-file-name-method hup) nil
|
||||
(tramp-file-name-host hup) (shadow-cluster-name cluster)))
|
||||
(shadow-make-fullname
|
||||
(if cluster
|
||||
(shadow-cluster-name cluster)
|
||||
(nth 0 hup))
|
||||
(nth 1 hup)
|
||||
hup nil
|
||||
(if suffix
|
||||
(concat "~/" suffix)
|
||||
(nth 2 hup)))))
|
||||
(concat "~/" suffix)
|
||||
(tramp-file-name-localname hup)))))
|
||||
|
||||
(defun shadow-same-site (pattern file)
|
||||
"True if the site of PATTERN and of FILE are on the same site.
|
||||
If usernames are supplied, they must also match exactly. PATTERN and FILE may
|
||||
be lists of host, user, name, or ange-ftp file names. FILE may also be just a
|
||||
local filename."
|
||||
(let ((pattern-sup (shadow-parse-fullname pattern))
|
||||
PATTERN and FILE may be Tramp vectors, or remote file names.
|
||||
FILE may also be just a local filename."
|
||||
(let ((pattern-sup (shadow-parse-name pattern))
|
||||
(file-sup (shadow-parse-name file)))
|
||||
(and
|
||||
(shadow-site-match (nth 0 pattern-sup) (nth 0 file-sup))
|
||||
(or (null (nth 1 pattern-sup))
|
||||
(string-equal (nth 1 pattern-sup) (nth 1 file-sup))))))
|
||||
(shadow-site-match
|
||||
(tramp-file-name-host pattern-sup) (tramp-file-name-host file-sup))
|
||||
(or (null (tramp-file-name-user pattern-sup))
|
||||
(string-equal
|
||||
(tramp-file-name-user pattern-sup)
|
||||
(tramp-file-name-user file-sup))))))
|
||||
|
||||
(defun shadow-file-match (pattern file &optional regexp)
|
||||
"Return t if PATTERN matches FILE.
|
||||
If REGEXP is supplied and non-nil, the file part of the pattern is a regular
|
||||
expression, otherwise it must match exactly. The sites and usernames must
|
||||
match---see `shadow-same-site'. The pattern must be in full ange-ftp format,
|
||||
expression, otherwise it must match exactly. The sites must
|
||||
match---see `shadow-same-site'. The pattern must be in full Tramp format,
|
||||
but the file can be any valid filename. This function does not do any
|
||||
filename expansion or contraction, you must do that yourself first."
|
||||
(let* ((pattern-sup (shadow-parse-fullname pattern))
|
||||
(let* ((pattern-sup (shadow-parse-name pattern))
|
||||
(file-sup (shadow-parse-name file)))
|
||||
(and (shadow-same-site pattern-sup file-sup)
|
||||
(if regexp
|
||||
(string-match (nth 2 pattern-sup) (nth 2 file-sup))
|
||||
(string-equal (nth 2 pattern-sup) (nth 2 file-sup))))))
|
||||
(string-match
|
||||
(tramp-file-name-localname pattern-sup)
|
||||
(tramp-file-name-localname file-sup))
|
||||
(string-equal
|
||||
(tramp-file-name-localname pattern-sup)
|
||||
(tramp-file-name-localname file-sup))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; User-level Commands
|
||||
|
@ -405,30 +435,34 @@ one of them is sufficient to update the file on all of them. Clusters are
|
|||
defined by a name, the network address of a primary host (the one we copy
|
||||
files to), and a regular expression that matches the hostnames of all the
|
||||
sites in the cluster."
|
||||
(interactive (list (completing-read "Cluster name: " shadow-clusters () ())))
|
||||
(interactive (list (completing-read "Cluster name: " shadow-clusters)))
|
||||
(let* ((old (shadow-get-cluster name))
|
||||
(primary (read-string "Primary host: "
|
||||
(if old (shadow-cluster-primary old)
|
||||
name)))
|
||||
(regexp (let (try-regexp)
|
||||
(while (not
|
||||
(string-match
|
||||
(setq try-regexp
|
||||
(primary (let (try-primary)
|
||||
(while (not
|
||||
(or
|
||||
(string-equal
|
||||
(setq try-primary
|
||||
(read-string
|
||||
"Regexp matching all host names: "
|
||||
(if old (shadow-cluster-regexp old)
|
||||
(shadow-regexp-superquote primary))))
|
||||
primary))
|
||||
(message "Regexp doesn't include the primary host!")
|
||||
(sit-for 2))
|
||||
try-regexp))
|
||||
; (username (read-no-blanks-input
|
||||
; (format "Username (default %s): "
|
||||
; (shadow-get-user primary))
|
||||
; (if old (or (shadow-cluster-username old) "")
|
||||
; (user-login-name))))
|
||||
)
|
||||
; (if (string-equal "" username) (setq username nil))
|
||||
"Primary host: "
|
||||
(if old (shadow-cluster-primary old)
|
||||
name)))
|
||||
shadow-system-name)
|
||||
(file-remote-p try-primary)))
|
||||
(message "Not a valid primary!")
|
||||
(sit-for 2))
|
||||
try-primary))
|
||||
(regexp (let (try-regexp)
|
||||
(while (not
|
||||
(string-match
|
||||
(setq try-regexp
|
||||
(read-string
|
||||
"Regexp matching all host names: "
|
||||
(if old (shadow-cluster-regexp old)
|
||||
(shadow-regexp-superquote primary))))
|
||||
primary))
|
||||
(message "Regexp doesn't include the primary host!")
|
||||
(sit-for 2))
|
||||
try-regexp)))
|
||||
(shadow-set-cluster name primary regexp)))
|
||||
|
||||
;;;###autoload
|
||||
|
@ -438,20 +472,14 @@ It may have different filenames on each site. When this file is edited, the
|
|||
new version will be copied to each of the other locations. Sites can be
|
||||
specific hostnames, or names of clusters (see `shadow-define-cluster')."
|
||||
(interactive)
|
||||
(let* ((hup (shadow-parse-fullname
|
||||
(let* ((hup (shadow-parse-name
|
||||
(shadow-contract-file-name (buffer-file-name))))
|
||||
(name (nth 2 hup))
|
||||
user site group)
|
||||
(name (tramp-file-name-localname hup))
|
||||
site group)
|
||||
(while (setq site (shadow-read-site))
|
||||
(setq user (read-string (format "Username (default %s): "
|
||||
(shadow-get-user site)))
|
||||
name (read-string "Filename: " name))
|
||||
(setq group (cons (shadow-make-fullname site
|
||||
(if (string-equal "" user)
|
||||
(shadow-get-user site)
|
||||
user)
|
||||
name)
|
||||
group)))
|
||||
(setq name (read-string "Filename: " name)
|
||||
hup (shadow-parse-name (shadow-contract-file-name name))
|
||||
group (cons (shadow-make-fullname hup site) group)))
|
||||
(setq shadow-literal-groups (cons group shadow-literal-groups)))
|
||||
(shadow-write-info-file))
|
||||
|
||||
|
@ -468,19 +496,12 @@ function). Each site can be either a hostname or the name of a cluster (see
|
|||
"Filename regexp: "
|
||||
(if (buffer-file-name)
|
||||
(shadow-regexp-superquote
|
||||
(nth 2
|
||||
(shadow-parse-name
|
||||
(shadow-contract-file-name
|
||||
(buffer-file-name))))))))
|
||||
site sites usernames)
|
||||
(file-local-name (buffer-file-name))))))
|
||||
site sites)
|
||||
(while (setq site (shadow-read-site))
|
||||
(setq sites (cons site sites))
|
||||
(setq usernames
|
||||
(cons (read-string (format "Username for %s: " site)
|
||||
(shadow-get-user site))
|
||||
usernames)))
|
||||
(setq sites (cons site sites)))
|
||||
(setq shadow-regexp-groups
|
||||
(cons (shadow-make-group regexp sites usernames)
|
||||
(cons (shadow-make-group regexp sites)
|
||||
shadow-regexp-groups))
|
||||
(shadow-write-info-file)))
|
||||
|
||||
|
@ -537,14 +558,14 @@ permanently, remove the group from `shadow-literal-groups' or
|
|||
;;; Internal functions
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun shadow-make-group (regexp sites usernames)
|
||||
(defun shadow-make-group (regexp sites)
|
||||
"Make a description of a file group---
|
||||
actually a list of regexp ange-ftp file names---from REGEXP (name of file to
|
||||
be shadowed), list of SITES, and corresponding list of USERNAMES for each
|
||||
site."
|
||||
actually a list of regexp Tramp file names---from REGEXP (name of file to
|
||||
be shadowed), and list of SITES"
|
||||
(if sites
|
||||
(cons (shadow-make-fullname (car sites) (car usernames) regexp)
|
||||
(shadow-make-group regexp (cdr sites) (cdr usernames)))
|
||||
(cons (shadow-make-fullname
|
||||
(shadow-parse-name (shadow-site-primary (car sites))) nil regexp)
|
||||
(shadow-make-group regexp (cdr sites)))
|
||||
nil))
|
||||
|
||||
(defun shadow-copy-file (s)
|
||||
|
@ -601,7 +622,9 @@ Consider them as regular expressions if third arg REGEXP is true."
|
|||
(car groups))))
|
||||
(append (cond ((equal nonmatching (car groups)) nil)
|
||||
(regexp
|
||||
(let ((realname (nth 2 (shadow-parse-fullname file))))
|
||||
(let ((realname
|
||||
(tramp-file-name-localname
|
||||
(shadow-parse-name file))))
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (x)
|
||||
|
@ -636,9 +659,8 @@ PAIR must be `eq' to one of the elements of that list."
|
|||
Thus restores shadowfile's state from your last Emacs session.
|
||||
Return t unless files were locked; then return nil."
|
||||
(interactive)
|
||||
(if (and (fboundp 'file-locked-p)
|
||||
(or (stringp (file-locked-p shadow-info-file))
|
||||
(stringp (file-locked-p shadow-todo-file))))
|
||||
(if (or (stringp (file-locked-p shadow-info-file))
|
||||
(stringp (file-locked-p shadow-todo-file)))
|
||||
(progn
|
||||
(message "Shadowfile is running in another Emacs; can't have two.")
|
||||
(beep)
|
||||
|
@ -647,7 +669,7 @@ Return t unless files were locked; then return nil."
|
|||
(save-current-buffer
|
||||
(when shadow-info-file
|
||||
(set-buffer (setq shadow-info-buffer
|
||||
(find-file-noselect shadow-info-file)))
|
||||
(find-file-noselect shadow-info-file 'nowarn)))
|
||||
(when (and (not (buffer-modified-p))
|
||||
(file-newer-than-file-p (make-auto-save-file-name)
|
||||
shadow-info-file))
|
||||
|
@ -680,6 +702,7 @@ defined, the old hashtable info is invalid."
|
|||
(if (not shadow-info-buffer)
|
||||
(setq shadow-info-buffer (find-file-noselect shadow-info-file)))
|
||||
(set-buffer shadow-info-buffer)
|
||||
(setq buffer-read-only nil)
|
||||
(delete-region (point-min) (point-max))
|
||||
(shadow-insert-var 'shadow-clusters)
|
||||
(shadow-insert-var 'shadow-literal-groups)
|
||||
|
@ -692,6 +715,7 @@ With non-nil argument also saves the buffer."
|
|||
(if (not shadow-todo-buffer)
|
||||
(setq shadow-todo-buffer (find-file-noselect shadow-todo-file)))
|
||||
(set-buffer shadow-todo-buffer)
|
||||
(setq buffer-read-only nil)
|
||||
(delete-region (point-min) (point-max))
|
||||
(shadow-insert-var 'shadow-files-to-copy)
|
||||
(if save (shadow-save-todo-file))))
|
||||
|
@ -764,24 +788,6 @@ look for files that have been changed and need to be copied to other systems."
|
|||
(yes-or-no-p "Active processes exist; kill them and exit anyway? "))))
|
||||
(kill-emacs)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Lucid Emacs compatibility
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; This is on hold until someone tells me about a working version of
|
||||
;; map-ynp for Lucid Emacs.
|
||||
|
||||
;(when (string-match "Lucid" emacs-version)
|
||||
; (require 'symlink-fix)
|
||||
; (require 'ange-ftp)
|
||||
; (require 'map-ynp)
|
||||
; (if (not (fboundp 'file-truename))
|
||||
; (fset 'shadow-expand-file-name
|
||||
; (symbol-function 'symlink-expand-file-name)))
|
||||
; (if (not (fboundp 'ange-ftp-ftp-name))
|
||||
; (fset 'ange-ftp-ftp-name
|
||||
; (symbol-function 'ange-ftp-ftp-name))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;; Hook us up
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -790,18 +796,10 @@ look for files that have been changed and need to be copied to other systems."
|
|||
(defun shadow-initialize ()
|
||||
"Set up file shadowing."
|
||||
(interactive)
|
||||
(if (null shadow-homedir)
|
||||
(setq shadow-homedir
|
||||
(file-name-as-directory (shadow-expand-file-name "~"))))
|
||||
(if (null shadow-info-file)
|
||||
(setq shadow-info-file
|
||||
;; FIXME: Move defaults to their defcustom.
|
||||
(shadow-expand-file-name
|
||||
(locate-user-emacs-file "shadows" ".shadows"))))
|
||||
(if (null shadow-todo-file)
|
||||
(setq shadow-todo-file
|
||||
(shadow-expand-file-name
|
||||
(locate-user-emacs-file "shadow_todo" ".shadow_todo"))))
|
||||
(setq shadow-homedir
|
||||
(file-name-as-directory (shadow-expand-file-name shadow-homedir))
|
||||
shadow-info-file (shadow-expand-file-name shadow-info-file)
|
||||
shadow-todo-file (shadow-expand-file-name shadow-todo-file))
|
||||
(if (not (shadow-read-files))
|
||||
(progn
|
||||
(message "Shadowfile information files not found - aborting")
|
||||
|
|
|
@ -824,10 +824,12 @@ A canonicalized color name is all-lower case, with any blanks removed."
|
|||
(replace-regexp-in-string " +" "" (downcase color))
|
||||
color)))
|
||||
|
||||
(defun tty-color-24bit (rgb)
|
||||
"Return pixel value on 24-bit terminals. Return nil if RGB is
|
||||
nil or not on 24-bit terminal."
|
||||
(when (and rgb (= (display-color-cells) 16777216))
|
||||
(defun tty-color-24bit (rgb &optional display)
|
||||
"Return 24-bit color pixel value for RGB value on DISPLAY.
|
||||
DISPLAY can be a display name or a frame, and defaults to the
|
||||
selected frame's display.
|
||||
If DISPLAY is not on a 24-but TTY terminal, return nil."
|
||||
(when (and rgb (= (display-color-cells display) 16777216))
|
||||
(let ((r (lsh (car rgb) -8))
|
||||
(g (lsh (cadr rgb) -8))
|
||||
(b (lsh (nth 2 rgb) -8)))
|
||||
|
@ -850,7 +852,7 @@ If FRAME is not specified or is nil, it defaults to the selected frame."
|
|||
(error "Invalid specification for tty color \"%s\"" name))
|
||||
(tty-modify-color-alist
|
||||
(append (list (tty-color-canonicalize name)
|
||||
(or (tty-color-24bit rgb) index))
|
||||
(or (tty-color-24bit rgb frame) index))
|
||||
rgb)
|
||||
frame))
|
||||
|
||||
|
@ -1026,7 +1028,7 @@ might need to be approximated if it is not supported directly."
|
|||
(or (assoc color (tty-color-alist frame))
|
||||
(let ((rgb (tty-color-standard-values color)))
|
||||
(and rgb
|
||||
(let ((pixel (tty-color-24bit rgb)))
|
||||
(let ((pixel (tty-color-24bit rgb frame)))
|
||||
(or (and pixel (cons color (cons pixel rgb)))
|
||||
(tty-color-approximate rgb frame)))))))))
|
||||
|
||||
|
|
|
@ -138,7 +138,9 @@ This variable specifies how far to search to find such a duplicate.
|
|||
"Non-nil means misspelled words remain highlighted until corrected.
|
||||
If this variable is nil, only the most recently detected misspelled word
|
||||
is highlighted, and the highlight is turned off as soon as point moves
|
||||
off the misspelled word."
|
||||
off the misspelled word.
|
||||
|
||||
Make sure this variable is non-nil if you use `flyspell-region'."
|
||||
:group 'flyspell
|
||||
:type 'boolean)
|
||||
|
||||
|
@ -1372,7 +1374,10 @@ language."
|
|||
;;* flyspell-small-region ... */
|
||||
;;*---------------------------------------------------------------------*/
|
||||
(defun flyspell-small-region (beg end)
|
||||
"Flyspell text between BEG and END."
|
||||
"Flyspell text between BEG and END.
|
||||
|
||||
This function is intended to work on small regions, as
|
||||
determined by `flyspell-large-region'."
|
||||
(save-excursion
|
||||
(if (> beg end)
|
||||
(let ((old beg))
|
||||
|
@ -1643,7 +1648,10 @@ The buffer to mark them in is `flyspell-large-region-buffer'."
|
|||
;;*---------------------------------------------------------------------*/
|
||||
;;;###autoload
|
||||
(defun flyspell-region (beg end)
|
||||
"Flyspell text between BEG and END."
|
||||
"Flyspell text between BEG and END.
|
||||
|
||||
Make sure `flyspell-mode' is turned on if you want the highlight
|
||||
of a misspelled word removed when you've corrected it."
|
||||
(interactive "r")
|
||||
(ispell-set-spellchecker-params) ; Initialize variables and dicts alists
|
||||
(if (= beg end)
|
||||
|
|
|
@ -2262,8 +2262,9 @@ Global `ispell-quit' set to start location to continue spell session."
|
|||
(ispell-pdict-save ispell-silently-savep)
|
||||
(message "%s"
|
||||
(substitute-command-keys
|
||||
(concat "Spell-checking suspended;"
|
||||
" use C-u \\[ispell-word] to resume")))
|
||||
(concat
|
||||
"Spell-checking suspended; use "
|
||||
"\\[universal-argument] \\[ispell-word] to resume")))
|
||||
(setq ispell-quit start)
|
||||
nil)
|
||||
((= char ?q)
|
||||
|
|
38
src/gnutls.c
38
src/gnutls.c
|
@ -2071,7 +2071,14 @@ gnutls_symmetric (bool encrypting, Lisp_Object cipher,
|
|||
cipher = intern (SSDATA (cipher));
|
||||
|
||||
if (SYMBOLP (cipher))
|
||||
info = XCDR (Fassq (cipher, Fgnutls_ciphers ()));
|
||||
{
|
||||
info = Fassq (cipher, Fgnutls_ciphers ());
|
||||
if (!CONSP (info))
|
||||
xsignal2 (Qerror,
|
||||
build_string ("GnuTLS cipher is invalid or not found"),
|
||||
cipher);
|
||||
info = XCDR (info);
|
||||
}
|
||||
else if (TYPE_RANGED_INTEGERP (gnutls_cipher_algorithm_t, cipher))
|
||||
gca = XINT (cipher);
|
||||
else
|
||||
|
@ -2086,7 +2093,8 @@ gnutls_symmetric (bool encrypting, Lisp_Object cipher,
|
|||
|
||||
ptrdiff_t key_size = gnutls_cipher_get_key_size (gca);
|
||||
if (key_size == 0)
|
||||
error ("GnuTLS cipher is invalid or not found");
|
||||
xsignal2 (Qerror,
|
||||
build_string ("GnuTLS cipher is invalid or not found"), cipher);
|
||||
|
||||
ptrdiff_t kstart_byte, kend_byte;
|
||||
const char *kdata = extract_data_from_object (key, &kstart_byte, &kend_byte);
|
||||
|
@ -2342,7 +2350,14 @@ itself. */)
|
|||
hash_method = intern (SSDATA (hash_method));
|
||||
|
||||
if (SYMBOLP (hash_method))
|
||||
info = XCDR (Fassq (hash_method, Fgnutls_macs ()));
|
||||
{
|
||||
info = Fassq (hash_method, Fgnutls_macs ());
|
||||
if (!CONSP (info))
|
||||
xsignal2 (Qerror,
|
||||
build_string ("GnuTLS MAC-method is invalid or not found"),
|
||||
hash_method);
|
||||
info = XCDR (info);
|
||||
}
|
||||
else if (TYPE_RANGED_INTEGERP (gnutls_mac_algorithm_t, hash_method))
|
||||
gma = XINT (hash_method);
|
||||
else
|
||||
|
@ -2357,7 +2372,9 @@ itself. */)
|
|||
|
||||
ptrdiff_t digest_length = gnutls_hmac_get_len (gma);
|
||||
if (digest_length == 0)
|
||||
error ("GnuTLS MAC-method is invalid or not found");
|
||||
xsignal2 (Qerror,
|
||||
build_string ("GnuTLS MAC-method is invalid or not found"),
|
||||
hash_method);
|
||||
|
||||
ptrdiff_t kstart_byte, kend_byte;
|
||||
const char *kdata = extract_data_from_object (key, &kstart_byte, &kend_byte);
|
||||
|
@ -2423,7 +2440,14 @@ the number itself. */)
|
|||
digest_method = intern (SSDATA (digest_method));
|
||||
|
||||
if (SYMBOLP (digest_method))
|
||||
info = XCDR (Fassq (digest_method, Fgnutls_digests ()));
|
||||
{
|
||||
info = Fassq (digest_method, Fgnutls_digests ());
|
||||
if (!CONSP (info))
|
||||
xsignal2 (Qerror,
|
||||
build_string ("GnuTLS digest-method is invalid or not found"),
|
||||
digest_method);
|
||||
info = XCDR (info);
|
||||
}
|
||||
else if (TYPE_RANGED_INTEGERP (gnutls_digest_algorithm_t, digest_method))
|
||||
gda = XINT (digest_method);
|
||||
else
|
||||
|
@ -2438,7 +2462,9 @@ the number itself. */)
|
|||
|
||||
ptrdiff_t digest_length = gnutls_hash_get_len (gda);
|
||||
if (digest_length == 0)
|
||||
error ("GnuTLS digest-method is invalid or not found");
|
||||
xsignal2 (Qerror,
|
||||
build_string ("GnuTLS digest-method is invalid or not found"),
|
||||
digest_method);
|
||||
|
||||
gnutls_hash_hd_t hash;
|
||||
int ret = gnutls_hash_init (&hash, gda);
|
||||
|
|
|
@ -344,5 +344,25 @@
|
|||
"session"
|
||||
(format "%s@%s" (plist-get auth-info :user) (plist-get auth-info :host)))))
|
||||
|
||||
(ert-deftest auth-source-delete ()
|
||||
(let* ((netrc-file (make-temp-file "auth-source-test" nil nil "\
|
||||
machine a1 port a2 user a3 password a4
|
||||
machine b1 port b2 user b3 password b4
|
||||
machine c1 port c2 user c3 password c4\n"))
|
||||
(auth-sources (list netrc-file))
|
||||
(auth-source-do-cache nil)
|
||||
(expected '((:host "a1" :port "a2" :user "a3" :secret "a4")))
|
||||
(parameters '(:max 1 :host t)))
|
||||
(unwind-protect
|
||||
(let ((found (apply #'auth-source-delete parameters)))
|
||||
(dolist (f found)
|
||||
(let ((s (plist-get f :secret)))
|
||||
(setf f (plist-put f :secret
|
||||
(if (functionp s) (funcall s) s)))))
|
||||
;; Note: The netrc backend doesn't delete anything, so
|
||||
;; this is actually the same as `auth-source-search'.
|
||||
(should (equal found expected)))
|
||||
(delete-file netrc-file))))
|
||||
|
||||
(provide 'auth-source-tests)
|
||||
;;; auth-source-tests.el ends here
|
||||
|
|
876
test/lisp/shadowfile-tests.el
Normal file
876
test/lisp/shadowfile-tests.el
Normal file
|
@ -0,0 +1,876 @@
|
|||
;;; shadowfile-tests.el --- Tests of shadowfile
|
||||
|
||||
;; Copyright (C) 2018 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Michael Albinus <michael.albinus@gmx.de>
|
||||
|
||||
;; This program 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.
|
||||
;;
|
||||
;; This program 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 this program. If not, see `http://www.gnu.org/licenses/'.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; A whole test run can be performed calling the command `shadowfile-test-all'.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
(require 'shadowfile)
|
||||
(require 'tramp)
|
||||
|
||||
;; There is no default value on w32 systems, which could work out of the box.
|
||||
(defconst shadow-test-remote-temporary-file-directory
|
||||
(cond
|
||||
((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY"))
|
||||
((eq system-type 'windows-nt) null-device)
|
||||
(t (add-to-list
|
||||
'tramp-methods
|
||||
'("mock"
|
||||
(tramp-login-program "sh")
|
||||
(tramp-login-args (("-i")))
|
||||
(tramp-remote-shell "/bin/sh")
|
||||
(tramp-remote-shell-args ("-c"))
|
||||
(tramp-connection-timeout 10)))
|
||||
(add-to-list
|
||||
'tramp-default-host-alist
|
||||
`("\\`mock\\'" nil ,(system-name)))
|
||||
;; Emacs' Makefile sets $HOME to a nonexistent value. Needed in
|
||||
;; batch mode only, therefore. It cannot be
|
||||
;; `temporary-directory', because the tests with "~" would fail.
|
||||
(unless (and (null noninteractive) (file-directory-p "~/"))
|
||||
(setenv "HOME" invocation-directory))
|
||||
(format "/mock::%s" temporary-file-directory)))
|
||||
"Temporary directory for Tramp tests.")
|
||||
|
||||
(defconst shadow-test-info-file
|
||||
(expand-file-name "shadows_test" temporary-file-directory)
|
||||
"File to keep shadow information in during tests.")
|
||||
|
||||
(defconst shadow-test-todo-file
|
||||
(expand-file-name "shadow_todo_test" temporary-file-directory)
|
||||
"File to store the list of uncopied shadows in during tests.")
|
||||
|
||||
(ert-deftest shadow-test00-clusters ()
|
||||
"Check cluster definitions.
|
||||
Per definition, all files are identical on the different hosts of
|
||||
a cluster (or site). This is not tested here; it must be
|
||||
guaranteed by the originator of a cluster definition."
|
||||
(skip-unless (file-remote-p shadow-test-remote-temporary-file-directory))
|
||||
|
||||
(let ((text-quoting-style 'grave) ;; We inspect the *Messages* buffer!
|
||||
(inhibit-message t)
|
||||
(shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
shadow-clusters
|
||||
cluster primary regexp mocked-input)
|
||||
(unwind-protect
|
||||
;; We must mock `read-from-minibuffer' and `read-string', in
|
||||
;; order to avoid interactive arguments.
|
||||
(cl-letf* (((symbol-function 'read-from-minibuffer)
|
||||
(lambda (&rest args) (pop mocked-input)))
|
||||
((symbol-function 'read-string)
|
||||
(lambda (&rest args) (pop mocked-input))))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define a cluster.
|
||||
(setq cluster "cluster"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary)
|
||||
mocked-input `(,cluster ,primary ,regexp))
|
||||
(call-interactively 'shadow-define-cluster)
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-name (shadow-get-cluster cluster)) cluster))
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-primary (shadow-get-cluster cluster)) primary))
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-regexp (shadow-get-cluster cluster)) regexp))
|
||||
(should-not (shadow-get-cluster "non-existent-cluster-name"))
|
||||
|
||||
;; Test `shadow-set-cluster' and `make-shadow-cluster'.
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
(should
|
||||
(equal (shadow-get-cluster cluster)
|
||||
(make-shadow-cluster
|
||||
:name cluster :primary primary :regexp regexp)))
|
||||
|
||||
;; The primary must be either `shadow-system-name', or a remote file.
|
||||
(setq ;; The second "cluster" is wrong.
|
||||
mocked-input `(,cluster ,cluster ,primary ,regexp))
|
||||
(with-current-buffer (messages-buffer)
|
||||
(narrow-to-region (point-max) (point-max)))
|
||||
(call-interactively 'shadow-define-cluster)
|
||||
(should
|
||||
(string-match
|
||||
(regexp-quote "Not a valid primary!")
|
||||
(with-current-buffer (messages-buffer) (buffer-string))))
|
||||
;; The first cluster definition is still valid.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-name (shadow-get-cluster cluster)) cluster))
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-primary (shadow-get-cluster cluster)) primary))
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-regexp (shadow-get-cluster cluster)) regexp))
|
||||
|
||||
;; The regexp must match the primary name.
|
||||
(setq ;; The second "cluster" is wrong.
|
||||
mocked-input `(,cluster ,primary ,cluster ,regexp))
|
||||
(with-current-buffer (messages-buffer)
|
||||
(narrow-to-region (point-max) (point-max)))
|
||||
(call-interactively 'shadow-define-cluster)
|
||||
(should
|
||||
(string-match
|
||||
(regexp-quote "Regexp doesn't include the primary host!")
|
||||
(with-current-buffer (messages-buffer) (buffer-string))))
|
||||
;; The first cluster definition is still valid.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-name (shadow-get-cluster cluster)) cluster))
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-primary (shadow-get-cluster cluster)) primary))
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-regexp (shadow-get-cluster cluster)) regexp))
|
||||
|
||||
;; Redefine the cluster.
|
||||
(setq primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (shadow-regexp-superquote primary)
|
||||
mocked-input `(,cluster ,primary ,regexp))
|
||||
(call-interactively 'shadow-define-cluster)
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-name (shadow-get-cluster cluster)) cluster))
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-primary (shadow-get-cluster cluster)) primary))
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-cluster-regexp (shadow-get-cluster cluster)) regexp))
|
||||
|
||||
;; Test `shadow-set-cluster' and `make-shadow-cluster'.
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
(should
|
||||
(equal (shadow-get-cluster cluster)
|
||||
(make-shadow-cluster
|
||||
:name cluster :primary primary :regexp regexp))))
|
||||
|
||||
;; Cleanup.
|
||||
(with-current-buffer (messages-buffer) (widen))
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file)))))
|
||||
|
||||
(ert-deftest shadow-test01-sites ()
|
||||
"Check site definitions.
|
||||
Per definition, all files are identical on the different hosts of
|
||||
a cluster (or site). This is not tested here; it must be
|
||||
guaranteed by the originator of a cluster definition."
|
||||
(skip-unless (file-remote-p shadow-test-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
shadow-clusters
|
||||
cluster1 cluster2 primary1 primary2 regexp1 regexp2 mocked-input)
|
||||
(unwind-protect
|
||||
;; We must mock `read-from-minibuffer' and `read-string', in
|
||||
;; order to avoid interactive arguments.
|
||||
(cl-letf* (((symbol-function 'read-from-minibuffer)
|
||||
(lambda (&rest args) (pop mocked-input)))
|
||||
((symbol-function 'read-string)
|
||||
(lambda (&rest args) (pop mocked-input))))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define a cluster.
|
||||
(setq cluster1 "cluster1"
|
||||
primary1 shadow-system-name
|
||||
regexp1 (shadow-regexp-superquote primary1))
|
||||
(shadow-set-cluster cluster1 primary1 regexp1)
|
||||
|
||||
;; A site is either a cluster identification, or a primary host.
|
||||
(should (string-equal cluster1 (shadow-site-name cluster1)))
|
||||
(should (string-equal primary1 (shadow-name-site primary1)))
|
||||
(should
|
||||
(string-equal (format "/%s:" cluster1) (shadow-name-site cluster1)))
|
||||
(should (string-equal (system-name) (shadow-site-name primary1)))
|
||||
(should
|
||||
(string-equal
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
(shadow-name-site
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory))))
|
||||
(should
|
||||
(string-equal
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
(shadow-site-name
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory))))
|
||||
|
||||
(should (equal (shadow-site-cluster cluster1)
|
||||
(shadow-get-cluster cluster1)))
|
||||
(should (equal (shadow-site-cluster (shadow-name-site cluster1))
|
||||
(shadow-get-cluster cluster1)))
|
||||
(should (equal (shadow-site-cluster primary1)
|
||||
(shadow-get-cluster cluster1)))
|
||||
(should (equal (shadow-site-cluster (shadow-site-name primary1))
|
||||
(shadow-get-cluster cluster1)))
|
||||
(should (string-equal (shadow-site-primary cluster1) primary1))
|
||||
(should (string-equal (shadow-site-primary primary1) primary1))
|
||||
|
||||
;; `shadow-read-site' accepts "cluster", "/cluster:",
|
||||
;; "system", "/system:". It shall reject bad site names.
|
||||
(setq mocked-input
|
||||
`(,cluster1 ,(shadow-name-site cluster1)
|
||||
,primary1 ,(shadow-site-name primary1)
|
||||
,shadow-system-name "" "bad" "/bad:"))
|
||||
(should (string-equal (shadow-read-site) cluster1))
|
||||
(should (string-equal (shadow-read-site) (shadow-name-site cluster1)))
|
||||
(should (string-equal (shadow-read-site) primary1))
|
||||
(should (string-equal (shadow-read-site) (shadow-site-name primary1)))
|
||||
(should (string-equal (shadow-read-site) shadow-system-name))
|
||||
(should-not (shadow-read-site)) ; ""
|
||||
(should-not (shadow-read-site)) ; "bad"
|
||||
(should-not (shadow-read-site)) ; "/bad:"
|
||||
(should-error (shadow-read-site)) ; no input at all
|
||||
|
||||
;; Define a second cluster.
|
||||
(setq cluster2 "cluster2"
|
||||
primary2
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp2 (format "^\\(%s\\|%s\\)$" shadow-system-name primary2))
|
||||
(shadow-set-cluster cluster2 primary2 regexp2)
|
||||
|
||||
;; `shadow-site-match' shall know all different kind of site names.
|
||||
(should (shadow-site-match cluster1 cluster1))
|
||||
(should (shadow-site-match primary1 primary1))
|
||||
(should (shadow-site-match cluster1 primary1))
|
||||
(should (shadow-site-match primary1 cluster1))
|
||||
(should (shadow-site-match cluster2 cluster2))
|
||||
(should (shadow-site-match primary2 primary2))
|
||||
(should (shadow-site-match cluster2 primary2))
|
||||
(should (shadow-site-match primary2 cluster2))
|
||||
|
||||
;; The regexp of `cluster2' matches the primary of
|
||||
;; `cluster1'. Not vice versa.
|
||||
(should (shadow-site-match cluster2 cluster1))
|
||||
(should-not (shadow-site-match cluster1 cluster2))
|
||||
|
||||
;; If we use the primaries of a cluster, it doesn't match.
|
||||
(should-not
|
||||
(shadow-site-match (shadow-site-primary cluster2) cluster1))
|
||||
(should-not
|
||||
(shadow-site-match (shadow-site-primary cluster1) cluster2)))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file)))))
|
||||
|
||||
(ert-deftest shadow-test02-files ()
|
||||
"Check file manipulation functions."
|
||||
(skip-unless (file-remote-p shadow-test-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
shadow-clusters
|
||||
cluster primary regexp file hup)
|
||||
(unwind-protect
|
||||
(progn
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define a cluster.
|
||||
(setq cluster "cluster"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary)
|
||||
file (make-temp-name
|
||||
(expand-file-name
|
||||
"shadowfile-tests" temporary-file-directory)))
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
|
||||
;; The constant structure to compare with.
|
||||
(setq hup (make-tramp-file-name :host (system-name) :localname file))
|
||||
|
||||
;; The structure a local file is transformed in.
|
||||
(should (equal (shadow-parse-name file) hup))
|
||||
(should (equal (shadow-parse-name (concat "/" cluster ":" file)) hup))
|
||||
(should (equal (shadow-parse-name (concat primary file)) hup))
|
||||
|
||||
;; A local file name is kept.
|
||||
(should
|
||||
(string-equal (shadow-local-file file) file))
|
||||
;; A file on this cluster is also local.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-local-file (concat "/" cluster ":" file)) file))
|
||||
;; A file on the primary host is also local.
|
||||
(should
|
||||
(string-equal (shadow-local-file (concat primary file)) file))
|
||||
|
||||
;; Redefine the cluster.
|
||||
(setq primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
|
||||
;; The structure of the local file is still the same.
|
||||
(should (equal (shadow-parse-name file) hup))
|
||||
;; The cluster name must be used.
|
||||
(setf (tramp-file-name-host hup) cluster)
|
||||
(should (equal (shadow-parse-name (concat "/" cluster ":" file)) hup))
|
||||
;; The structure of a remote file is different.
|
||||
(should
|
||||
(equal (shadow-parse-name (concat primary file))
|
||||
(tramp-dissect-file-name (concat primary file))))
|
||||
|
||||
;; A local file is still local.
|
||||
(should (shadow-local-file file))
|
||||
;; A file on this cluster is not local.
|
||||
(should-not (shadow-local-file (concat "/" cluster ":" file)))
|
||||
;; A file on the primary host is not local.
|
||||
(should-not (shadow-local-file (concat primary file)))
|
||||
;; There's no error on wrong FILE.
|
||||
(should-not (shadow-local-file nil)))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file)))))
|
||||
|
||||
(ert-deftest shadow-test03-expand-cluster-in-file-name ()
|
||||
"Check canonical file name of a cluster or site."
|
||||
(skip-unless (file-remote-p shadow-test-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
shadow-clusters
|
||||
cluster primary regexp file1 file2)
|
||||
(unwind-protect
|
||||
(progn
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define a cluster.
|
||||
(setq cluster "cluster"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
|
||||
(setq file1
|
||||
(make-temp-name
|
||||
(expand-file-name "shadowfile-tests" temporary-file-directory))
|
||||
file2
|
||||
(make-temp-name
|
||||
(expand-file-name
|
||||
"shadowfile-tests"
|
||||
shadow-test-remote-temporary-file-directory)))
|
||||
|
||||
;; A local file name is kept.
|
||||
(should
|
||||
(string-equal (shadow-expand-cluster-in-file-name file1) file1))
|
||||
;; A remote file is kept.
|
||||
(should
|
||||
(string-equal (shadow-expand-cluster-in-file-name file2) file2))
|
||||
;; A cluster name is expanded to the primary name.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-expand-cluster-in-file-name (format "/%s:%s" cluster file1))
|
||||
(shadow-expand-cluster-in-file-name (concat primary file1))))
|
||||
;; A primary name is expanded if it is a local file name.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-expand-cluster-in-file-name (concat primary file1)) file1))
|
||||
|
||||
;; Redefine the cluster.
|
||||
(setq primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
|
||||
;; A cluster name is expanded to the primary name.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-expand-cluster-in-file-name (format "/%s:%s" cluster file1))
|
||||
(shadow-expand-cluster-in-file-name (concat primary file1))))
|
||||
;; A primary name is not expanded if it isn't is a local file name.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-expand-cluster-in-file-name (concat primary file1))
|
||||
(concat primary file1))))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file)))))
|
||||
|
||||
(ert-deftest shadow-test04-contract-file-name ()
|
||||
"Check canonical file name of a cluster or site."
|
||||
(skip-unless (file-remote-p shadow-test-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
shadow-clusters
|
||||
cluster primary regexp file)
|
||||
(unwind-protect
|
||||
(progn
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define a cluster.
|
||||
(setq cluster "cluster"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary)
|
||||
file (make-temp-name
|
||||
(expand-file-name
|
||||
"shadowfile-tests" temporary-file-directory)))
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
|
||||
;; The cluster name is prepended for local files.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-contract-file-name file) (concat "/cluster:" file)))
|
||||
;; A cluster file name is preserved.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-contract-file-name (concat "/cluster:" file))
|
||||
(concat "/cluster:" file)))
|
||||
;; `shadow-system-name' is mapped to the cluster.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-contract-file-name (concat shadow-system-name file))
|
||||
(concat "/cluster:" file)))
|
||||
|
||||
;; Redefine the cluster.
|
||||
(setq primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
|
||||
;; A remote file name is mapped to the cluster.
|
||||
(should
|
||||
(string-equal
|
||||
(shadow-contract-file-name
|
||||
(concat
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory) file))
|
||||
(concat "/cluster:" file))))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file)))))
|
||||
|
||||
(ert-deftest shadow-test05-file-match ()
|
||||
"Check `shadow-same-site' and `shadow-file-match'."
|
||||
(skip-unless (file-remote-p shadow-test-remote-temporary-file-directory))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
shadow-clusters
|
||||
cluster primary regexp file)
|
||||
(unwind-protect
|
||||
(progn
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define a cluster.
|
||||
(setq cluster "cluster"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary)
|
||||
file (make-temp-name
|
||||
(expand-file-name
|
||||
"shadowfile-tests" temporary-file-directory)))
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
|
||||
(should (shadow-same-site (shadow-parse-name "/cluster:") file))
|
||||
(should
|
||||
(shadow-same-site (shadow-parse-name shadow-system-name) file))
|
||||
(should (shadow-same-site (shadow-parse-name file) file))
|
||||
|
||||
(should
|
||||
(shadow-file-match
|
||||
(shadow-parse-name (concat "/cluster:" file)) file))
|
||||
(should
|
||||
(shadow-file-match
|
||||
(shadow-parse-name (concat shadow-system-name file)) file))
|
||||
(should (shadow-file-match (shadow-parse-name file) file))
|
||||
|
||||
;; Redefine the cluster.
|
||||
(setq primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster primary regexp)
|
||||
|
||||
(should
|
||||
(shadow-file-match
|
||||
(shadow-parse-name
|
||||
(concat
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
file))
|
||||
file)))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file)))))
|
||||
|
||||
(ert-deftest shadow-test06-literal-groups ()
|
||||
"Check literal group definitions."
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
shadow-clusters shadow-literal-groups
|
||||
cluster1 cluster2 primary regexp file1 file2 mocked-input)
|
||||
(unwind-protect
|
||||
;; We must mock `read-from-minibuffer' and `read-string', in
|
||||
;; order to avoid interactive arguments.
|
||||
(cl-letf* (((symbol-function 'read-from-minibuffer)
|
||||
(lambda (&rest args) (pop mocked-input)))
|
||||
((symbol-function 'read-string)
|
||||
(lambda (&rest args) (pop mocked-input))))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define clusters.
|
||||
(setq cluster1 "cluster1"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster1 primary regexp)
|
||||
|
||||
(setq cluster2 "cluster2"
|
||||
primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (format "^\\(%s\\|%s\\)$" shadow-system-name primary))
|
||||
(shadow-set-cluster cluster2 primary regexp)
|
||||
|
||||
;; Define a literal group.
|
||||
(setq file1
|
||||
(make-temp-name
|
||||
(expand-file-name "shadowfile-tests" temporary-file-directory))
|
||||
file2
|
||||
(make-temp-name
|
||||
(expand-file-name
|
||||
"shadowfile-tests"
|
||||
shadow-test-remote-temporary-file-directory))
|
||||
mocked-input `(,cluster1 ,file1 ,cluster2 ,file2 ,(kbd "RET")))
|
||||
(with-temp-buffer
|
||||
(setq-local buffer-file-name file1)
|
||||
(call-interactively 'shadow-define-literal-group))
|
||||
|
||||
;; `shadow-literal-groups' is a list of lists.
|
||||
(should (consp shadow-literal-groups))
|
||||
(should (consp (car shadow-literal-groups)))
|
||||
(should-not (cdr shadow-literal-groups))
|
||||
|
||||
(should (member (format "/%s:%s" cluster1 (file-local-name file1))
|
||||
(car shadow-literal-groups)))
|
||||
(should (member (format "/%s:%s" cluster2 (file-local-name file2))
|
||||
(car shadow-literal-groups))))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file)))))
|
||||
|
||||
(ert-deftest shadow-test07-regexp-groups ()
|
||||
"Check regexp group definitions."
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
shadow-clusters shadow-regexp-groups
|
||||
cluster1 cluster2 primary regexp file mocked-input)
|
||||
(unwind-protect
|
||||
;; We must mock `read-from-minibuffer' and `read-string', in
|
||||
;; order to avoid interactive arguments.
|
||||
(cl-letf* (((symbol-function 'read-from-minibuffer)
|
||||
(lambda (&rest args) (pop mocked-input)))
|
||||
((symbol-function 'read-string)
|
||||
(lambda (&rest args) (pop mocked-input))))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define clusters.
|
||||
(setq cluster1 "cluster1"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster1 primary regexp)
|
||||
|
||||
(setq cluster2 "cluster2"
|
||||
primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (format "^\\(%s\\|%s\\)$" shadow-system-name primary))
|
||||
(shadow-set-cluster cluster2 primary regexp)
|
||||
|
||||
;; Define a regexp group.
|
||||
(setq file
|
||||
(make-temp-name
|
||||
(expand-file-name "shadowfile-tests" temporary-file-directory))
|
||||
mocked-input `(,(shadow-regexp-superquote file)
|
||||
,cluster1 ,cluster2 ,(kbd "RET")))
|
||||
(with-temp-buffer
|
||||
(setq-local buffer-file-name nil)
|
||||
(call-interactively 'shadow-define-regexp-group))
|
||||
|
||||
;; `shadow-regexp-groups' is a list of lists.
|
||||
(should (consp shadow-regexp-groups))
|
||||
(should (consp (car shadow-regexp-groups)))
|
||||
(should-not (cdr shadow-regexp-groups))
|
||||
|
||||
(should
|
||||
(member
|
||||
(concat
|
||||
(shadow-site-primary cluster1) (shadow-regexp-superquote file))
|
||||
(car shadow-regexp-groups)))
|
||||
(should
|
||||
(member
|
||||
(concat
|
||||
(shadow-site-primary cluster2) (shadow-regexp-superquote file))
|
||||
(car shadow-regexp-groups))))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file)))))
|
||||
|
||||
(ert-deftest shadow-test08-shadow-todo ()
|
||||
"Check that needed shadows are added to todo."
|
||||
(let ((backup-inhibited t)
|
||||
(shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
(shadow-inhibit-message t)
|
||||
shadow-clusters shadow-literal-groups shadow-regexp-groups
|
||||
shadow-files-to-copy
|
||||
cluster1 cluster2 primary regexp file)
|
||||
(unwind-protect
|
||||
(progn
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
|
||||
;; Define clusters.
|
||||
(setq cluster1 "cluster1"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster1 primary regexp)
|
||||
|
||||
(setq cluster2 "cluster2"
|
||||
primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster2 primary regexp)
|
||||
|
||||
;; Define a literal group.
|
||||
(setq file
|
||||
(make-temp-name
|
||||
(expand-file-name "shadowfile-tests" temporary-file-directory))
|
||||
shadow-literal-groups
|
||||
`((,(concat "/cluster1:" file) ,(concat "/cluster2:" file))))
|
||||
|
||||
;; Save file from "cluster1" definition.
|
||||
(with-temp-buffer
|
||||
(setq buffer-file-name file)
|
||||
(insert "foo")
|
||||
(save-buffer))
|
||||
(should
|
||||
(member
|
||||
(cons file (shadow-contract-file-name (concat "/cluster2:" file)))
|
||||
shadow-files-to-copy))
|
||||
|
||||
;; Save file from "cluster2" definition.
|
||||
(with-temp-buffer
|
||||
(setq buffer-file-name (concat (shadow-site-primary cluster2) file))
|
||||
(insert "foo")
|
||||
(save-buffer))
|
||||
(should
|
||||
(member
|
||||
(cons
|
||||
(concat (shadow-site-primary cluster2) file)
|
||||
(shadow-contract-file-name (concat "/cluster1:" file)))
|
||||
shadow-files-to-copy))
|
||||
|
||||
;; Define a regexp group.
|
||||
(setq shadow-files-to-copy nil
|
||||
shadow-regexp-groups
|
||||
`((,(concat (shadow-site-primary cluster1)
|
||||
(shadow-regexp-superquote file))
|
||||
,(concat (shadow-site-primary cluster2)
|
||||
(shadow-regexp-superquote file)))))
|
||||
|
||||
;; Save file from "cluster1" definition.
|
||||
(with-temp-buffer
|
||||
(setq buffer-file-name file)
|
||||
(insert "foo")
|
||||
(save-buffer))
|
||||
(should
|
||||
(member
|
||||
(cons file (shadow-contract-file-name (concat "/cluster2:" file)))
|
||||
shadow-files-to-copy))
|
||||
|
||||
;; Save file from "cluster2" definition.
|
||||
(with-temp-buffer
|
||||
(setq buffer-file-name (concat (shadow-site-primary cluster2) file))
|
||||
(insert "foo")
|
||||
(save-buffer))
|
||||
(should
|
||||
(member
|
||||
(cons
|
||||
(concat (shadow-site-primary cluster2) file)
|
||||
(shadow-contract-file-name (concat "/cluster1:" file)))
|
||||
shadow-files-to-copy)))
|
||||
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
(when (file-exists-p file)
|
||||
(delete-file file))
|
||||
(when (file-exists-p (concat (shadow-site-primary cluster2) file))
|
||||
(delete-file (concat (shadow-site-primary cluster2) file))))))
|
||||
|
||||
(ert-deftest shadow-test09-shadow-copy-files ()
|
||||
"Check that needed shadow files are copied."
|
||||
(let ((backup-inhibited t)
|
||||
(shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file)
|
||||
(shadow-inhibit-message t)
|
||||
(shadow-noquery t)
|
||||
shadow-clusters shadow-files-to-copy
|
||||
cluster1 cluster2 primary regexp file mocked-input)
|
||||
(unwind-protect
|
||||
(progn
|
||||
;; Cleanup.
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
(when (buffer-live-p shadow-todo-buffer)
|
||||
(with-current-buffer shadow-todo-buffer (erase-buffer)))
|
||||
|
||||
;; Define clusters.
|
||||
(setq cluster1 "cluster1"
|
||||
primary shadow-system-name
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster1 primary regexp)
|
||||
|
||||
(setq cluster2 "cluster2"
|
||||
primary
|
||||
(file-remote-p shadow-test-remote-temporary-file-directory)
|
||||
regexp (shadow-regexp-superquote primary))
|
||||
(shadow-set-cluster cluster2 primary regexp)
|
||||
|
||||
;; Define files to copy.
|
||||
(setq file
|
||||
(make-temp-name
|
||||
(expand-file-name "shadowfile-tests" temporary-file-directory))
|
||||
shadow-literal-groups
|
||||
`((,(concat "/cluster1:" file) ,(concat "/cluster2:" file)))
|
||||
shadow-regexp-groups
|
||||
`((,(concat (shadow-site-primary cluster1)
|
||||
(shadow-regexp-superquote file))
|
||||
,(concat (shadow-site-primary cluster2)
|
||||
(shadow-regexp-superquote file))))
|
||||
mocked-input `(,(concat (shadow-site-primary cluster2) file)
|
||||
,file))
|
||||
|
||||
;; Save files.
|
||||
(with-temp-buffer
|
||||
(setq buffer-file-name file)
|
||||
(insert "foo")
|
||||
(save-buffer))
|
||||
(with-temp-buffer
|
||||
(setq buffer-file-name (concat (shadow-site-primary cluster2) file))
|
||||
(insert "foo")
|
||||
(save-buffer))
|
||||
|
||||
;; We must mock `write-region', in order to check proper
|
||||
;; action.
|
||||
(add-function
|
||||
:before (symbol-function 'write-region)
|
||||
(lambda (&rest args)
|
||||
(when (and (buffer-file-name) mocked-input)
|
||||
(should (equal (buffer-file-name) (pop mocked-input)))))
|
||||
'((name . "write-region-mock")))
|
||||
|
||||
;; Copy the files.
|
||||
(shadow-copy-files 'noquery)
|
||||
(should-not shadow-files-to-copy)
|
||||
(with-current-buffer shadow-todo-buffer
|
||||
(goto-char (point-min))
|
||||
(should
|
||||
(looking-at (regexp-quote "(setq shadow-files-to-copy nil)")))))
|
||||
|
||||
;; Cleanup.
|
||||
(remove-function (symbol-function 'write-region) "write-region-mock")
|
||||
(when (file-exists-p shadow-info-file)
|
||||
(delete-file shadow-info-file))
|
||||
(when (file-exists-p shadow-todo-file)
|
||||
(delete-file shadow-todo-file))
|
||||
(when (file-exists-p file)
|
||||
(delete-file file))
|
||||
(when (file-exists-p (concat (shadow-site-primary cluster2) file))
|
||||
(delete-file (concat (shadow-site-primary cluster2) file))))))
|
||||
|
||||
(defun shadowfile-test-all (&optional interactive)
|
||||
"Run all tests for \\[shadowfile]."
|
||||
(interactive "p")
|
||||
(if interactive
|
||||
(ert-run-tests-interactively "^shadowfile-")
|
||||
(ert-run-tests-batch "^shadowfile-")))
|
||||
|
||||
(let ((shadow-info-file shadow-test-info-file)
|
||||
(shadow-todo-file shadow-test-todo-file))
|
||||
(shadow-initialize))
|
||||
|
||||
(provide 'shadowfile-tests)
|
||||
;;; shadowfile-tests.el ends here
|
Loading…
Add table
Reference in a new issue