*** empty log message ***
[bpt/emacs.git] / lisp / diff.el
CommitLineData
646bd331 1;;; diff.el --- Run `diff' in compilation-mode.
c0274f38 2
646bd331 3;; Copyright (C) 1992 Free Software Foundation, Inc.
3a801d0c 4
fd7fa35a 5;; Keyword: unix, tools
e5167999 6
b10a4379
JB
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software; you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
e5167999 11;; the Free Software Foundation; either version 2, or (at your option)
b10a4379
JB
12;; any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs; see the file COPYING. If not, write to
21;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22
e5167999
ER
23;;; Code:
24
646bd331 25(require 'compile)
aa228418 26
646bd331
RM
27(defvar diff-switches nil
28 "*A string or list of strings specifying switches to be be passed to diff.")
29
30(defvar diff-regexp-alist
31 '(
32 ;; -u format: @@ -OLDSTART,OLDEND +NEWSTART,NEWEND @@
33 ("^@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@$" 1 2)
34
35 ;; -c format: *** OLDSTART,OLDEND ****
36 ("^\\*\\*\\* \\([0-9]+\\),[0-9]+ \\*\\*\\*\\*$" 1 nil)
37 ;; --- NEWSTART,NEWEND ----
38 ("^--- \\([0-9]+\\),[0-9]+ ----$" nil 1)
39
40 ;; plain diff format: OLDSTART[,OLDEND]{a,d,c}NEWSTART[,NEWEND]
41 ("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]\\([0-9]+\\)\\(,[0-9]+\\)?$" 1 3)
42
43 ;; -e (ed) format: OLDSTART[,OLDEND]{a,d,c}
44 ("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]$" 1)
45
46 ;; -f format: {a,d,c}OLDSTART[ OLDEND]
47 ;; -n format: {a,d,c}OLDSTART LINES-CHANGED
48 ("^[adc]\\([0-9]+\\)\\( [0-9]+\\)?$" 1)
49 )
50 "Alist (REGEXP OLD-IDX NEW-IDX) of regular expressions to match difference
51sections in \\[diff] output. If REGEXP matches, the OLD-IDX'th
52subexpression gives the line number in the old file, and NEW-IDX'th
53subexpression gives the line number in the new file. If OLD-IDX or NEW-IDX
54is nil, REGEXP matches only half a section.")
55
56;; See compilation-parse-errors-function (compile.el).
c540863c 57(defun diff-parse-differences (limit-search find-at-least)
646bd331
RM
58 (setq compilation-error-list nil)
59 (message "Parsing differences...")
60
61 ;; Don't reparse diffs already seen at last parse.
62 (goto-char compilation-parsing-end)
63
64 ;; Construct in REGEXP a regexp composed of all those in dired-regexp-alist.
65 (let ((regexp (mapconcat (lambda (elt)
66 (concat "\\(" (car elt) "\\)"))
67 diff-regexp-alist
68 "\\|"))
69 ;; (GROUP-IDX OLD-IDX NEW-IDX)
70 (groups (let ((subexpr 1))
71 (mapcar (lambda (elt)
72 (prog1
73 (cons subexpr
74 (mapcar (lambda (n)
75 (and n
76 (+ subexpr n)))
77 (cdr elt)))
78 (setq subexpr (+ subexpr 1
79 (count-regexp-groupings
80 (car elt))))))
81 diff-regexp-alist)))
82
83 (new-error
84 (function (lambda (file subexpr)
85 (setq compilation-error-list
86 (cons
87 (cons (set-marker (make-marker)
88 (match-beginning subexpr)
89 (current-buffer))
90 (let ((line (string-to-int
91 (buffer-substring
92 (match-beginning subexpr)
93 (match-end subexpr)))))
94 (save-excursion
95 (set-buffer (find-file-noselect file))
96 (save-excursion
97 (goto-line line)
98 (point-marker)))))
99 compilation-error-list)))))
100
101 (found-desired nil)
102 g)
103
104 (while (and (not found-desired)
105 ;; We don't just pass LIMIT-SEARCH to re-search-forward
106 ;; because we want to find matches containing LIMIT-SEARCH
107 ;; but which extend past it.
108 (re-search-forward regexp nil t))
109
110 ;; Find which individual regexp matched.
111 (setq g groups)
112 (while (and g (null (match-beginning (car (car g)))))
113 (setq g (cdr g)))
114 (setq g (car g))
115
116 (if (nth 1 g) ;OLD-IDX
117 (funcall new-error diff-old-file (nth 1 g)))
118 (if (nth 2 g) ;NEW-IDX
119 (funcall new-error diff-new-file (nth 2 g)))
120
c540863c
RM
121 (if (or (and find-at-least (>= nfound find-at-least))
122 (and limit-search (>= (point) limit-search)))
123 ;; We have found as many new errors as the user wants,
124 ;; or the user wanted a specific diff, and we're past it.
125 (setq found-desired t)))
646bd331
RM
126 (if found-desired
127 (setq compilation-parsing-end (point))
128 ;; Set to point-max, not point, so we don't perpetually
129 ;; parse the last bit of text when it isn't a diff header.
130 (setq compilation-parsing-end (point-max))
131 (message "Parsing differences...done")))
132 (setq compilation-error-list (nreverse compilation-error-list)))
b10a4379
JB
133
134;;;###autoload
646bd331 135(defun diff (old new &optional switches)
b10a4379 136 "Find and display the differences between OLD and NEW files.
9c50f912 137Interactively the current buffer's file name is the default for for NEW
646bd331
RM
138and a backup file for NEW is the default for OLD.
139With prefix arg, prompt for diff switches."
b10a4379 140 (interactive
646bd331
RM
141 (nconc
142 (let (oldf newf)
143 (nreverse
144 (list
145 (setq newf (buffer-file-name)
146 newf (if (and newf (file-exists-p newf))
147 (read-file-name
148 (concat "Diff new file: ("
149 (file-name-nondirectory newf) ") ")
150 nil newf t)
151 (read-file-name "Diff new file: " nil nil t)))
152 (setq oldf (file-newest-backup newf)
153 oldf (if (and oldf (file-exists-p oldf))
154 (read-file-name
155 (concat "Diff original file: ("
156 (file-name-nondirectory oldf) ") ")
157 (file-name-directory oldf) oldf t)
158 (read-file-name "Diff original file: "
159 (file-name-directory newf) nil t))))))
160 (if current-prefix-arg
161 (list (read-string "Diff switches: "
162 (if (stringp diff-switches)
163 diff-switches
164 (mapconcat 'identity diff-switches " "))))
165 nil)))
b10a4379
JB
166 (message "Comparing files %s %s..." new old)
167 (setq new (expand-file-name new)
168 old (expand-file-name old))
646bd331
RM
169 (let ((buf (compile-internal (mapconcat 'identity
170 (append '("diff")
171 (if (consp diff-switches)
172 diff-switches
173 (list diff-switches))
174 (list old)
175 (list new))
176 " ")
177 "No more differences" "Diff"
178 'diff-parse-differences)))
179 (save-excursion
180 (set-buffer buf)
181 (set (make-local-variable 'diff-old-file) old)
182 (set (make-local-variable 'diff-new-file) new))
183 buf))
aa228418 184
646bd331
RM
185;;;###autoload
186(defun diff-backup (file &optional switches)
37c51f00
RS
187 "Diff this file with its backup file or vice versa.
188Uses the latest backup, if there are several numerical backups.
189If this file is a backup, diff it with its original.
190The backup file is the first file given to `diff'."
646bd331
RM
191 (interactive (list (read-file-name "Diff (file with backup): ")
192 (if current-prefix-arg
193 (read-string "Diff switches: "
194 (if (stringp diff-switches)
195 diff-switches
196 (mapconcat 'identity
197 diff-switches " ")))
198 nil)))
37c51f00
RS
199 (let (bak ori)
200 (if (backup-file-name-p file)
201 (setq bak file
202 ori (file-name-sans-versions file))
203 (setq bak (or (diff-latest-backup-file file)
204 (error "No backup found for %s" file))
205 ori file))
646bd331 206 (diff bak ori switches)))
37c51f00
RS
207
208(defun diff-latest-backup-file (fn) ; actually belongs into files.el
209 "Return the latest existing backup of FILE, or nil."
210 ;; First try simple backup, then the highest numbered of the
211 ;; numbered backups.
212 ;; Ignore the value of version-control because we look for existing
213 ;; backups, which maybe were made earlier or by another user with
214 ;; a different value of version-control.
215 (setq fn (expand-file-name fn))
216 (or
217 (let ((bak (make-backup-file-name fn)))
218 (if (file-exists-p bak) bak))
219 (let* ((dir (file-name-directory fn))
220 (base-versions (concat (file-name-nondirectory fn) ".~"))
221 (bv-length (length base-versions)))
222 (concat dir
223 (car (sort
224 (file-name-all-completions base-versions dir)
225 ;; bv-length is a fluid var for backup-extract-version:
226 (function
227 (lambda (fn1 fn2)
228 (> (backup-extract-version fn1)
229 (backup-extract-version fn2))))))))))
aa228418 230
c0274f38 231;;; diff.el ends here