home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / packages / diff.el.z / diff.el
Encoding:
Text File  |  1998-05-21  |  23.9 KB  |  706 lines

  1. ;; -*-Emacs-Lisp-*-
  2. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  3. ;;
  4. ;; File:         diff.el
  5. ;; Version:      $Revision: 3.4 $
  6. ;; Author:       This file is based on diff.el by
  7. ;;               sunpitt!wpmstr!fbresz@Sun.COM 1/27/89.
  8. ;;               It has been completely rewritten in July 1994 by
  9. ;;               Sandy Rutherford <sandy@ibm550.sissa.it>
  10. ;; RCS:          
  11. ;; Description:  diff-mode for handling output from unix diff utility.
  12. ;; Modified:     Wed Jul 17 10:26:57 1996 (Andy Norman) ange@hplb.hpl.hp.com
  13. ;;
  14. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  15.  
  16. ;;; Copyright (C) 1990 Free Software Foundation, Inc.
  17. ;;; Copyright (C) 1994 Sandy Rutherford
  18.  
  19. ;;; This file is based on diff.el by sunpitt!wpmstr!fbresz@Sun.COM 1/27/89.
  20. ;;; It has been completely rewritten in July 1994 by
  21. ;;; Sandy Rutherford <sandy@ibm550.sissa.it>
  22.  
  23. ;;; This program is free software; you can redistribute it and/or modify
  24. ;;; it under the terms of the GNU General Public License as published by
  25. ;;; the Free Software Foundation; either version 1, or (at your option)
  26. ;;; any later version.
  27. ;;;
  28. ;;; This program is distributed in the hope that it will be useful,
  29. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  30. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  31. ;;; GNU General Public License for more details.
  32. ;;;
  33. ;;; A copy of the GNU General Public License can be obtained from this
  34. ;;; program's author (send electronic mail to sandy@ibm550.sissa.it) or
  35. ;;; from the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
  36. ;;; MA 02139, USA.
  37.  
  38. (provide 'diff)
  39.  
  40. ;;; User Variables
  41.  
  42. (defgroup diff nil
  43.   "Handling output from Unix diff utility"
  44.   :group 'tools)
  45.  
  46. ;; should be in to loaddefs.el now.
  47. ;;;###autoload
  48. (defcustom diff-switches "-c"
  49.   "*A list of switches (strings) to pass to the diff program."
  50.   :type '(choice string
  51.          (repeat string))
  52.   :group 'diff)
  53.  
  54. (defcustom diff-do-narrow nil
  55.   "*If non-nil diff buffers are initialized narrowed to each difference."
  56.   :type 'boolean
  57.   :group 'diff)
  58.  
  59. (defcustom diff-load-hooks nil
  60.   "Hooks to run after loading diff.el"
  61.   :type 'hook
  62.   :group 'diff)
  63.  
  64. ;;; Internal variables
  65.  
  66. (defconst diff-emacs-19-p
  67.   (let ((ver (string-to-int (substring emacs-version 0 2))))
  68.     (>= ver 19)))
  69.  
  70. (or diff-emacs-19-p (require 'emacs-19))
  71.  
  72. (defvar diff-old-file nil)
  73. ;; A list whose car is the name of the old file, and whose cdr indicates
  74. ;; whether we should delete the buffer on quit.
  75. (defvar diff-new-file nil)
  76. ;; Same as diff-old-file, except for the new file.
  77. (defvar diff-total-differences "0")
  78. ;; Total number of difference hunks as a string.
  79. (defvar diff-current-difference "0")
  80. ;; Current difference hunk as a string.
  81. (defvar diff-current-hunk 0)
  82. ;; Current difference hunk as an integer.
  83. (defvar diff-total-hunks 0)
  84. ;; Total number of difference hunks as an integer.
  85. (defvar diff-hunk-vector (vector 0))
  86. ;; Vector storing the starting positions of the difference hunks.
  87. (defvar diff-old-file-pattern nil)
  88. (defvar diff-new-file-pattern nil)
  89. (defvar diff-hunk-pattern nil)
  90. ;; Regexps to use when parsing file lines in difference hunks.
  91.  
  92.  
  93. (defvar diff-search-pattern-alist
  94.   (list
  95.     (list ?e "^[0-9]\\(,[0-9]+\\)?[acd]$" "^\\([0-9]+\\)" nil)
  96.     (list ?c "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\n"
  97.       "^\\*+ \\([0-9]+\\)" "^-+ \\([0-9]+\\)")
  98.     (list ?u "^@@ " "^@@ -\\([0-9]+\\)" "^@@ -[0-9,]+ \\+\\([0-9]+\\)")
  99.     (list nil "^[0-9]+" "^\\([0-9]+\\)" "^[0-9,]+[acd]\\([0-9]+\\)")))
  100.  
  101. (defvar diff-keymap-grokked nil)
  102.  
  103. (defvar diff-temp-template "/tmp/diff")
  104.  
  105. ;; Initialize the keymap if it isn't already
  106.  
  107. (defvar diff-mode-map nil
  108.   "Keymap used in diff mode.")
  109.  
  110. (if diff-mode-map
  111.     nil
  112.   (setq diff-mode-map (make-keymap))
  113.   (suppress-keymap diff-mode-map)
  114.   (define-key diff-mode-map "?" 'describe-mode)
  115.   (define-key diff-mode-map "." 'diff-display-file)
  116.   (define-key diff-mode-map "N" 'diff-narrow)
  117.   (define-key diff-mode-map "W" 'widen)
  118.   (define-key diff-mode-map "f" 'diff-find-file)
  119.   (define-key diff-mode-map "h" 'diff-show-header)
  120.   (define-key diff-mode-map "j" 'diff-show-difference)
  121.   (define-key diff-mode-map "n" 'diff-next-difference)
  122.   (define-key diff-mode-map "o" 'diff-find-file-other-window)
  123.   (define-key diff-mode-map "p" 'diff-previous-difference)
  124.   (define-key diff-mode-map "q" 'diff-quit)
  125.   (define-key diff-mode-map "w" 'diff-find-file-other-frame)
  126.   (define-key diff-mode-map "\C-c\C-c" 'diff-find-file-other-window)
  127.   (define-key diff-mode-map " " 'diff-advertised-scroll-up)
  128.   (define-key diff-mode-map 'backspace 'diff-advertised-scroll-down)
  129.   (define-key diff-mode-map 'delete 'diff-advertised-scroll-down)
  130.   (define-key diff-mode-map "\C-n" 'diff-next-line)
  131.   (define-key diff-mode-map "\C-p" 'diff-previous-line)
  132.   (define-key diff-mode-map "\M->" 'diff-end-of-buffer)
  133.   (define-key diff-mode-map "\M-<" 'diff-beginning-of-buffer))
  134.  
  135. ;;; Internal functions
  136.  
  137. (defun diff-grok-keys (to-command from-command)
  138.   ;; Assigns to TO-COMMAND the keys for the global binding of FROM-COMMAND.
  139.   ;; Does not clobber anything in the local keymap.
  140.   (let ((keys (where-is-internal from-command)))
  141.     (while keys
  142.       (condition-case nil
  143.       (if (eq (global-key-binding (car keys)) (key-binding (car keys)))
  144.           (local-set-key (car keys) to-command))
  145.     (error nil))
  146.       (setq keys (cdr keys)))))
  147.  
  148. (defun diff-grok-keymap ()
  149.   (if diff-keymap-grokked
  150.       ()
  151.     (if (and term-setup-hook (boundp 'command-line-args-left))
  152.     (progn
  153.       (if diff-emacs-19-p
  154.           (run-hooks term-setup-hook)
  155.         (funcall term-setup-hook))
  156.       (setq term-setup-hook nil)))
  157.     (setq diff-keymap-grokked t)
  158.     (diff-grok-keys 'diff-next-line 'next-line)
  159.     (diff-grok-keys 'diff-previous-line 'previous-line)
  160.     (diff-grok-keys 'diff-forward-char 'forward-char)
  161.     (diff-grok-keys 'diff-backward-char 'backward-char)
  162.     (diff-grok-keys 'diff-scroll-up 'scroll-up)
  163.     (diff-grok-keys 'diff-scroll-down 'scroll-down)
  164.     (diff-grok-keys 'diff-beginning-of-buffer 'beginning-of-buffer)
  165.     (diff-grok-keys 'diff-end-of-buffer 'end-of-buffer)))
  166.  
  167. (defun diff-buffer-narrowed-p ()
  168.   ;; Returns t if the current buffer is narrowed
  169.   (save-restriction
  170.     (let ((min (point-min))
  171.       (max (point-max)))
  172.       (widen)
  173.       (not (and (= min (point-min)) (= max (point-max)))))))
  174.  
  175. (defun diff-current-hunk ()
  176.   ;; Returns the current diff hunk.
  177.   (let ((pt (point))
  178.     (start 0)
  179.     (end (1+ diff-total-hunks))
  180.     m)
  181.     (while (> (- end start) 1)
  182.       (setq m (/ (+ start end) 2))
  183.       (if (>= pt (aref diff-hunk-vector m))
  184.       (setq start m)
  185.     (setq end m)))
  186.     (if (>= pt (aref diff-hunk-vector end))
  187.     (setq m end)
  188.       (setq m start))
  189.     ;; Don't treat end of buffer as start of next hunk
  190.     (if (eobp) (1- m) m)))
  191.  
  192. (defun diff-hunk-min (n)
  193.   ;; Returns the start of the current diff hunk.
  194.   (aref diff-hunk-vector n))
  195.  
  196. (defun diff-hunk-max (n)
  197.   ;; Returns the end of the current diff hunk.
  198.   (aref diff-hunk-vector (1+ n)))
  199.  
  200. (defun diff-parse-hunks ()
  201.   ;; Parses a buffer of diff output.
  202.   (save-excursion
  203.     (save-restriction
  204.       (message "Parsing differences...")
  205.       (widen)
  206.       (goto-char (point-min))
  207.       (let ((hunks (list 1)))
  208.     (while (re-search-forward diff-hunk-pattern nil t)
  209.       (setq hunks (cons (match-beginning 0) hunks)))
  210.     (setq diff-total-hunks (1- (length hunks))
  211.           diff-hunk-vector (apply 'vector
  212.                       (nreverse (cons (point-max) hunks)))))))
  213.   (message "Parsing differences...done"))
  214.  
  215. (defun diff-update-modeline ()
  216.   ;; Updates the mode line to show current diff hunk.
  217.   (if (or (< (point) (diff-hunk-min diff-current-hunk))
  218.       (>= (point) (diff-hunk-max diff-current-hunk)))
  219.       (progn
  220.     (setq diff-current-hunk (diff-current-hunk)
  221.           diff-current-difference (int-to-string diff-current-hunk))
  222.     (set-buffer-modified-p (buffer-modified-p)))))
  223.  
  224. (defun diff-read-args (oldprompt newprompt switchprompt
  225.                  &optional file-for-backup)
  226.   ;; Grab the args for diff.  OLDPROMPT and NEWPROMPT are the prompts
  227.   ;; for the old & new filenames, SWITCHPROMPT for the list of
  228.   ;; switches.  If FILE_FOR_BACKUP is provided (it must be a string if
  229.   ;; so), then it will be used to try & work out a file & backup to
  230.   ;; diff, & in this case the prompting order is backwards.  %s in a
  231.   ;; prompt has a guess substituted into it.  This is nasty.
  232.   (let (oldf newf)
  233.     (if file-for-backup
  234.     (setq newf file-for-backup
  235.           newf (if (and newf (file-exists-p newf))
  236.                (read-file-name
  237.             (format newprompt (file-name-nondirectory newf))
  238.             nil newf t)
  239.              (read-file-name (format newprompt "") nil nil t))
  240.           oldf (file-newest-backup newf)
  241.           oldf (if (and oldf (file-exists-p oldf))
  242.                (read-file-name
  243.             (format oldprompt (file-name-nondirectory oldf))
  244.             nil oldf t)
  245.              (read-file-name (format oldprompt "")
  246.                      (file-name-directory newf) nil t)))
  247.       ;; Else we aren't trying to be bright...
  248.       (setq oldf (read-file-name (format oldprompt "") nil nil t)
  249.         newf (read-file-name
  250.           (format newprompt (file-name-nondirectory oldf))
  251.           nil (file-name-directory oldf) t)))
  252.     (list oldf newf (diff-read-switches switchprompt))))
  253.  
  254. (defun diff-read-switches (switchprompt)
  255.   ;; Read and return a list of switches
  256.   (if current-prefix-arg
  257.       (let ((default (if (listp diff-switches)
  258.              (mapconcat 'identity diff-switches " ")
  259.                diff-switches)))
  260.     (diff-fix-switches
  261.      (read-string (format switchprompt default) default)))))
  262.  
  263. (defun diff-fix-switches (switch-spec)
  264.   ;; Parse a string into a list of switches or leave it be if it's
  265.   ;; not a string
  266.   (if (stringp switch-spec)
  267.       (let (result (start 0))
  268.     (while (string-match "\\(\\S-+\\)" switch-spec start)
  269.       (setq result (cons (substring switch-spec (match-beginning 1)
  270.                     (match-end 1))
  271.                  result)
  272.         start (match-end 0)))
  273.     (nreverse result))
  274.     switch-spec))
  275.  
  276. (defun diff-get-file-buffer (file)
  277.   ;; Returns \(BUFFER . DEL-P\), where DEL-P is t if diff is expected
  278.   ;; to delete the buffer, and nil otherwise.
  279.   (let* ((buff (get-file-buffer file))
  280.      (del-p (null buff)))
  281.     (if (and buff (buffer-modified-p buff))
  282.     (progn
  283.       (message
  284.        "Buffer %s is modified.  Diffing against buffer contents."
  285.        (buffer-name buff))
  286.       (sit-for 1)))
  287.     ;; Call find-file-noselect even if we already have the buffer,
  288.     ;; as it will run verify-buffer-file-modtime.
  289.     (cons (find-file-noselect file) del-p)))
  290.  
  291. (defun diff-cleanup-buffers ()
  292.   ;; Cleans up diff buffers by deleting buffers that we don't expect
  293.   ;; the user to care about.
  294.   (let ((files (list diff-old-file diff-new-file)))
  295.     (while files
  296.       (let ((ent (car files))
  297.         buff)
  298.     (and (cdr ent)
  299.          (setq buff (get-file-buffer (car ent)))
  300.          (not (buffer-modified-p buff))
  301.          (kill-buffer buff)))
  302.       (setq files (cdr files)))
  303.     (if (get-buffer "*Diff Header*")
  304.     (kill-buffer "*Diff Header*"))))
  305.  
  306. (defun diff-latest-backup-file (file)
  307.   "Return the latest existing backup of FILE, or nil."
  308.   ;; First try simple backup, then the highest numbered of the
  309.   ;; numbered backups.
  310.   ;; Ignore the value of version-control because we look for existing
  311.   ;; backups, which maybe were made earlier or by another user with
  312.   ;; a different value of version-control.
  313.   (let* ((file (expand-file-name file))
  314.      (handler (find-file-name-handler file 'diff-latest-backup-file)))
  315.     (if handler
  316.     (funcall handler 'diff-latest-backup-file file)
  317.       (or
  318.        (let ((bak (make-backup-file-name file)))
  319.      (if (file-exists-p bak) bak))
  320.        (let* ((dir (file-name-directory file))
  321.           (base-versions (concat (file-name-nondirectory file) ".~"))
  322.           (bv-length (length base-versions)))
  323.      (concat dir
  324.          (car (sort
  325.                (file-name-all-completions base-versions dir)
  326.                ;; bv-length is a fluid var for backup-extract-version:
  327.                (function
  328.             (lambda (fn1 fn2)
  329.               (> (backup-extract-version fn1)
  330.                  (backup-extract-version fn2))))))))))))
  331.  
  332. (defun diff-file-line (&optional old-file-p)
  333.   "Return line number of current hunk in `diff-new-file'.
  334. With optional argument OLD-FILE-P, use `diff-old-file' instead."
  335.   (save-excursion
  336.     (let ((min (diff-hunk-min diff-current-hunk))
  337.       (max (diff-hunk-max diff-current-hunk))
  338.       (regexp (if old-file-p diff-old-file-pattern diff-new-file-pattern)))
  339.       (goto-char min)
  340.       (or (and regexp (re-search-forward regexp max t))
  341.       (error "Unable to locate a file line for %s file."
  342.          (if old-file-p "old" "new")))
  343.       (string-to-int (buffer-substring (match-beginning 1) (match-end 1))))))
  344.  
  345. (defun diff-run-diff (switches old old-temp new new-temp)
  346.   ;; Actually run the diff process with SWITCHES on OLD and NEW.
  347.   ;; OLD-TEMP and NEW-TEMP are names of temp files that can be used
  348.   ;; to dump the data out to.
  349.   (insert "diff " (mapconcat 'identity switches " ") " " old
  350.       " " new "\n")
  351.   (apply 'call-process "diff" nil t nil
  352.      (append switches (list old-temp new-temp))))
  353.  
  354.  
  355. (defun diff-fix-file-names (old old-temp new new-temp pattern)
  356.   ;; Replaces any temp file names with the real names of files.
  357.   (save-excursion
  358.     (save-restriction
  359.       (let ((files (list old new))
  360.         (temps (list old-temp new-temp))
  361.         buffer-read-only case-fold-search)
  362.     (goto-char (point-min))
  363.     (if (re-search-forward pattern nil t)
  364.         (narrow-to-region (point-min) (match-beginning 0)))
  365.     (while files
  366.       (let ((regexp (concat "[ \t\n]\\("
  367.                 (regexp-quote (car temps))
  368.                 "\\)[ \t\n]")))
  369.         (goto-char (point-min))
  370.         (forward-line 1)
  371.         (while (re-search-forward regexp nil t)
  372.           (goto-char (match-beginning 1))
  373.           (delete-region (point) (match-end 1))
  374.           (insert (car files))))
  375.       (setq files (cdr files)
  376.         temps (cdr temps)))))))
  377.  
  378. ;;;; User commands     
  379.  
  380. (defun diff-mode ()
  381.   "Diff Mode is used by \\[diff] for perusing the output from the diff program.
  382. All normal editing commands are turned off.  Instead, these are available:
  383. \\<diff-mode-map>
  384. \\[diff-advertised-scroll-up]    Scroll to next screen of this difference.
  385. \\[diff-advertised-scroll-down]    Scroll to previous screen of this difference.
  386. \\[diff-next-difference]    Move to Next Difference.
  387. \\[diff-previous-difference]    Move to Previous Difference.
  388. \\[diff-show-difference]    Jump to difference specified by numeric position.
  389. \\[diff-find-file]    Find current diff in file
  390. \\[diff-find-file-other-window]    Find current diff in file in other window
  391. \\[diff-display-file]    Display file in other window
  392. \\[diff-narrow]    Narrow diff buffer to current difference
  393. \\[widen]    Widen diff buffer
  394. \\[diff-show-header]    Show diff header describing file name etc.
  395. \\[diff-quit]    Quit diff
  396. "
  397.   (interactive)
  398.   (use-local-map diff-mode-map)
  399.   (diff-grok-keymap)
  400.   (setq buffer-read-only t
  401.     major-mode 'diff-mode
  402.     mode-name "Diff"
  403.     mode-line-modified "--- "
  404.     mode-line-process
  405.     '(" " diff-current-difference "/" diff-total-differences))
  406.   (diff-parse-hunks)
  407.   (setq diff-total-differences (int-to-string diff-total-hunks)))
  408.  
  409. ;;; Motion commands
  410.  
  411. (defun diff-next-difference (n)
  412.   "In diff-mode go the the beginning of the next difference hunk."
  413.   (interactive "p")
  414.   (if (zerop n)
  415.       (goto-char (diff-hunk-min diff-current-hunk))
  416.     (let ((narrow (diff-buffer-narrowed-p))
  417.       (max (point-max))
  418.       (min (point-min)))
  419.       (unwind-protect
  420.       (progn
  421.         (widen)
  422.         (setq diff-current-hunk (+ n diff-current-hunk))
  423.         (cond ((> diff-current-hunk diff-total-hunks)
  424.            (setq diff-current-hunk diff-total-hunks)
  425.            (message "No following difference hunks."))
  426.           ((< diff-current-hunk 0)
  427.            (setq diff-current-hunk 0)
  428.            (message "No preceding difference hunks.")))
  429.         (setq diff-current-difference (int-to-string diff-current-hunk)
  430.           min (goto-char (diff-hunk-min diff-current-hunk))
  431.           max (diff-hunk-max diff-current-hunk)))
  432.     (if narrow (narrow-to-region min max))))
  433.     (set-buffer-modified-p (buffer-modified-p))))
  434.  
  435. (defun diff-previous-difference (n)
  436.   "In diff-mode go the the beginning of the previous difference hunk."
  437.   (interactive "p")
  438.   (diff-next-difference (- n)))
  439.  
  440. (defun diff-next-line (n)
  441.   "In diff-mode go to the next line."
  442.   (interactive "p")
  443.   (condition-case nil
  444.       (next-line n)
  445.     (error (if (> n 0) (message "End of difference hunk"))))
  446.   (diff-update-modeline))
  447.  
  448. (defun diff-previous-line (n)
  449.   "In diff-mode go to the previous line."
  450.   (interactive "p")
  451.   (diff-next-line (- n)))
  452.  
  453. (defun diff-forward-char (n)
  454.   "In diff-mode move the point forward."
  455.   (interactive "p")
  456.   (forward-char n)
  457.   (diff-update-modeline))
  458.  
  459. (defun diff-backward-char (n)
  460.   "In diff-mode move the point backward."
  461.   (interactive "p")
  462.   (backward-char n)
  463.   (diff-update-modeline))
  464.  
  465. (defun diff-scroll-up (n)
  466.   "In diff-mode scroll the buffer up."
  467.   (interactive "P")
  468.   (scroll-up n)
  469.   (diff-update-modeline))
  470.  
  471. (fset 'diff-advertised-scroll-up 'diff-scroll-up)
  472.  
  473. (defun diff-scroll-down (n)
  474.   "In diff-mode scroll the buffer down."
  475.   (interactive "P")
  476.   (scroll-down n)
  477.   (diff-update-modeline))
  478.  
  479. (fset 'diff-advertised-scroll-down 'diff-scroll-down)
  480.  
  481. (defun diff-beginning-of-buffer (n)
  482.   "In diff-mode go to the beginning of the buffer."
  483.   (interactive "P")
  484.   (beginning-of-buffer n)
  485.   (diff-update-modeline))
  486.  
  487. (defun diff-end-of-buffer (n)
  488.   "In diff-mode go to the end of the buffer."
  489.   (interactive "P")
  490.   (end-of-buffer n)
  491.   (diff-update-modeline))
  492.  
  493. ;;; The main command.
  494.  
  495. ;;;###autoload
  496. (defun diff (old new &optional switches)
  497.   "Find and display the differences between OLD and NEW files.
  498. Interactively you are prompted with the current buffer's file name for NEW
  499. and what appears to be its backup for OLD."
  500.   ;; Support for diffing directories is rather limited.  It needs work.
  501.   (interactive (diff-read-args "Diff original file (%s) "
  502.                    "Diff new file (%s) "
  503.                    "Switches for diff (%s) "
  504.                    (buffer-file-name)))
  505.   (setq switches (diff-fix-switches (or switches diff-switches))
  506.     old (expand-file-name old)
  507.     new (expand-file-name new))
  508.   (let ((curr-buff (current-buffer))
  509.     doing-dirs old-temp new-temp old-buffer new-buffer flag)
  510.     (let ((fdp-old (file-directory-p old))
  511.       (fdp-new (file-directory-p new)))
  512.       (cond
  513.        ((null (or fdp-new fdp-old)))
  514.        ((null fdp-new)
  515.     (setq old (expand-file-name (file-name-nondirectory new) old)))
  516.        ((null fdp-old)
  517.     (setq new (expand-file-name (file-name-nondirectory old) new)))
  518.        (t (setq doing-dirs t))))
  519. ;;    (message "diff %s %s %s..."
  520. ;;         (mapconcat (function identity) switches " ") new old)
  521.     (message "diff %s %s %s..."
  522.          (mapconcat (function identity) switches " ") old new)
  523.     (if doing-dirs
  524.     (setq diff-old-file nil
  525.           diff-new-file nil)
  526.       (setq old-temp (make-temp-name (concat diff-temp-template "1"))
  527.         new-temp (make-temp-name (concat diff-temp-template "2"))
  528.         old-buffer (diff-get-file-buffer old)
  529.         new-buffer (diff-get-file-buffer new)
  530.         diff-old-file (cons old (cdr old-buffer))
  531.         diff-new-file (cons new (cdr new-buffer))))
  532.     (let (case-fold-search)
  533.       (mapcar (function
  534.            (lambda (x)
  535.          (if (string-match "[ecu]" x)
  536.              (setq flag (aref x (match-beginning 0))))))
  537.           switches))
  538.     (unwind-protect
  539.     (let ((patterns (assq flag diff-search-pattern-alist)))
  540.       (set-buffer (get-buffer-create "*Diff Output*"))
  541.       (setq default-directory (file-name-directory new)
  542.         diff-old-file-pattern (nth 2 patterns)
  543.         diff-new-file-pattern (nth 3 patterns)
  544.         diff-hunk-pattern (nth 1 patterns))
  545.       (let (buffer-read-only)
  546.         (if (fboundp 'buffer-disable-undo)
  547.         (buffer-disable-undo (current-buffer))
  548.           ;; old style (Emacs 18.55 and earlier)
  549.           (buffer-disable-undo (current-buffer)))
  550.         (widen)
  551.         (erase-buffer)
  552.         (if doing-dirs
  553.         (progn
  554.           (diff-run-diff switches old old new new)
  555.           (setq diff-hunk-pattern (concat diff-hunk-pattern
  556.                           "\\|^Only in ")))
  557.           (save-excursion
  558.         (set-buffer (car old-buffer))
  559.         (write-region (point-min) (point-max) old-temp nil 'quiet)
  560.         (set-buffer (car new-buffer))
  561.         (write-region (point-min) (point-max) new-temp nil 'quiet))
  562.           (diff-run-diff switches old old-temp new new-temp))
  563.         ;; Need to replace file names
  564.         (if (and (not doing-dirs) (memq flag '(?c ?u)))
  565.         (diff-fix-file-names old old-temp new new-temp
  566.                      diff-hunk-pattern))
  567.         (diff-mode)
  568.         (goto-char (point-min))
  569.         (setq diff-current-difference "0"
  570.           diff-current-hunk 0)
  571.         (if (zerop diff-total-hunks)
  572.         (progn
  573.           (diff-cleanup-buffers)
  574.           (message "No differences"))
  575.           (if diff-do-narrow (narrow-to-region (point) (diff-hunk-max 0)))
  576.           (display-buffer (current-buffer))
  577.           (message "%s difference hunk%s" diff-total-differences
  578.                (if (= diff-total-hunks 1) "" "s")))))
  579.       (condition-case nil
  580.       (delete-file old-temp)
  581.     (error nil))
  582.       (condition-case nil
  583.       (delete-file new-temp)
  584.     (error nil))
  585.       (set-buffer curr-buff))))
  586.  
  587. ;;;###autoload
  588. (defun diff-backup (file &optional switches)
  589.   "Diff this file with its backup file or vice versa.
  590. Uses the latest backup, if there are several numerical backups.
  591. If this file is a backup, diff it with its original.
  592. The backup file is the first file given to `diff'."
  593.   (interactive (list (read-file-name "Diff (file with backup): ")
  594.              (and current-prefix-arg
  595.               (diff-read-switches "Diff switches: "))))
  596.   (let (bak ori)
  597.     (if (backup-file-name-p file)
  598.     (setq bak file
  599.           ori (file-name-sans-versions file))
  600.       (setq bak (or (diff-latest-backup-file file)
  601.             (error "No backup found for %s" file))
  602.         ori file))
  603.     (diff bak ori switches)))
  604.  
  605. (defun diff-show-difference (n)
  606.   "Show difference number N (prefix argument)."
  607.   (interactive "p")
  608.   (let ((narrowedp (diff-buffer-narrowed-p))
  609.     (min (diff-hunk-min diff-current-hunk))
  610.     (max (diff-hunk-max diff-current-hunk)))
  611.     (unwind-protect
  612.     (progn
  613.       (widen)
  614.       (cond
  615.        ((< n 0)
  616.         (message "No negative hunks.")
  617.         (setq n 0))
  618.        ((> n diff-total-hunks)
  619.         (message "No hunk %d." n)
  620.         (setq n diff-total-hunks)))
  621.       (setq diff-current-hunk n
  622.         diff-current-difference (int-to-string diff-current-hunk)
  623.         min (diff-hunk-min n)
  624.         max (diff-hunk-max n))
  625.       (goto-char min))
  626.       (if narrowedp (narrow-to-region min max))
  627.       (set-buffer-modified-p (buffer-modified-p)))))
  628.  
  629. (defun diff-show-header ()
  630.   "Show `diff-header'."
  631.   (interactive)
  632.   (with-output-to-temp-buffer "*Diff Header*"
  633.     (princ (save-restriction
  634.          (widen)
  635.          (buffer-substring (diff-hunk-min 0) (diff-hunk-max 0))))))
  636.  
  637.  
  638. (defun diff-find-file (old-file-p)
  639.   "Visit diffed file, at the point corresponding to the current hunk.
  640. Default is to visit the new file; prefix means visit old file instead."
  641.   (interactive "P")
  642.   (let ((line (diff-file-line old-file-p)))
  643.     (find-file 
  644.      (if old-file-p
  645.      (car diff-old-file)
  646.        (car diff-new-file)))
  647.     (goto-line line)
  648.     (recenter 0)))
  649.  
  650. (defun diff-find-file-other-window (old-file-p)
  651.   "Visit the diffed file in other window, with the point at the current hunk.
  652. Default is to visit the new file; prefix means visit the old file instead."
  653.   (interactive "P")
  654.   (let ((line (diff-file-line old-file-p)))
  655.     (find-file-other-window
  656.      (if old-file-p
  657.      (car diff-old-file)
  658.        (car diff-new-file)))
  659.     (goto-line line)
  660.     (recenter 0)))
  661.  
  662. (defun diff-find-file-other-frame (old-file-p)
  663.   "Visit the diffed file in other frame, with point at the current hunk.
  664. Default is to visit the new file; prefix means visit the old file instead."
  665.   (interactive "P")
  666.   (let ((line (diff-file-line old-file-p)))
  667.     (find-file-other-frame
  668.      (if old-file-p
  669.      (car diff-old-file)
  670.        (car diff-new-file)))
  671.     (goto-line line)
  672.     (recenter 0)))
  673.  
  674. (defun diff-display-file (old-file-p)
  675.   "Display the diffed file in other window, with point at the current hunk.
  676. Default is to visit the new file; prefix means visit the old file instead."
  677.   (interactive "P")
  678.   (let ((line (diff-file-line old-file-p))
  679.     (wind (display-buffer (find-file-noselect (if old-file-p
  680.                               (car diff-old-file)
  681.                             (car diff-new-file)))))
  682.     (curr-wind (selected-window)))
  683.     (unwind-protect
  684.     (progn
  685.       (select-window wind)
  686.       (goto-line line)
  687.       (recenter 0))
  688.       (select-window curr-wind))))
  689.  
  690. (defun diff-quit ()
  691.   "Quit diff by killing the diff buffer."
  692.   (interactive)
  693.   (kill-buffer "*Diff Output*")
  694.   (diff-cleanup-buffers))
  695.  
  696. (defun diff-narrow ()
  697.   "Narrow diff buffer to current difference hunk."
  698.   (interactive)
  699.   (narrow-to-region (diff-hunk-min diff-current-hunk)
  700.             (diff-hunk-max diff-current-hunk)))
  701.  
  702. ;;; Run any load hooks
  703. (run-hooks 'diff-load-hook)
  704.  
  705. ;;; end of diff.el
  706.