* pop.c (pop_stat, pop_last): Check validity of string-to-integer
[bpt/emacs.git] / lisp / ediff-diff.el
index 659c4ff..b690bfb 100644 (file)
@@ -1,6 +1,7 @@
 ;;; ediff-diff.el --- diff-related utilities
 
-;; Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02 Free Software Foundation, Inc.
+;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+;;   2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 ;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
 
@@ -8,7 +9,7 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 
 ;;; Code:
 
-(provide 'ediff-diff)
 
 ;; compiler pacifier
 (defvar ediff-default-variant)
 (defvar null-device)
+(defvar longlines-mode)
 
 (eval-when-compile
   (let ((load-path (cons (expand-file-name ".") load-path)))
     (or (featurep 'ediff-init)
-       (load "ediff-init.el" nil nil 'nosuffix))
+       (load "ediff-init.el" nil t 'nosuffix))
     (or (featurep 'ediff-util)
-       (load "ediff-util.el" nil nil 'nosuffix))
+       (load "ediff-util.el" nil t 'nosuffix))
     ))
 ;; end pacifier
 
@@ -47,7 +48,6 @@
   :prefix "ediff-"
   :group 'ediff)
 
-;; these two must be here to prevent ediff-test-utility from barking
 (defcustom ediff-diff-program "diff"
   "*Program to use for generating the differential of the two files."
   :type 'string
@@ -61,41 +61,7 @@ Must produce output compatible with Unix's diff3 program."
 
 ;; The following functions must precede all defcustom-defined variables.
 
-;; The following functions needed for setting diff/diff3 options
-;; test if diff supports the --binary option
-(defsubst ediff-test-utility (diff-util option &optional files)
-  (eq 0 (apply 'call-process
-              (append (list diff-util nil nil nil option) files))))
-
-(defun ediff-diff-mandatory-option (diff-util)
-  (let ((file (if (boundp 'null-device) null-device "/dev/null")))
-    (cond  ((not (memq system-type '(ms-dos windows-nt windows-95)))
-           "")
-          ((and (string= diff-util ediff-diff-program)
-                (ediff-test-utility
-                 ediff-diff-program "--binary" (list file file)))
-           "--binary")
-          ((and (string= diff-util ediff-diff3-program)
-                (ediff-test-utility
-                 ediff-diff3-program "--binary" (list file file file)))
-           "--binary")
-          (t ""))))
-
-;; make sure that mandatory options are added even if the user changes
-;; ediff-diff-options or ediff-diff3-options in the customization widget
-(defun ediff-reset-diff-options (symb val)
-  (let* ((diff-program
-         (if (eq symb 'ediff-diff-options)
-             ediff-diff-program
-           ediff-diff3-program))
-        (mandatory-option (ediff-diff-mandatory-option diff-program))
-        (spacer (if (string-equal mandatory-option "") "" " ")))
-    (set symb
-        (if (string-match mandatory-option val)
-            val
-          (concat mandatory-option spacer val)))
-    ))
-
+(fset 'ediff-set-actual-diff-options '(lambda () nil))
 
 (defcustom ediff-shell
   (cond ((eq system-type 'emx) "cmd") ; OS/2
@@ -125,15 +91,47 @@ are `-I REGEXP', to ignore changes whose lines match the REGEXP."
   :type '(repeat string)
   :group 'ediff-diff)
 
-(defcustom ediff-diff-options ""
+(defun ediff-set-diff-options (symbol value)
+  (set symbol value)
+  (ediff-set-actual-diff-options))
+
+(defcustom ediff-diff-options
+  (if (memq system-type '(ms-dos windows-nt windows-95)) "--binary" "")
   "*Options to pass to `ediff-diff-program'.
-If Unix diff is used as `ediff-diff-program', then the most useful options are
-`-w', to ignore space, and `-i', to ignore case of letters.
-At present, the option `-c' is not allowed."
-  :set 'ediff-reset-diff-options
+If Unix diff is used as `ediff-diff-program',
+then a useful option is `-w', to ignore space.
+Options `-c', `-u', and `-i' are not allowed. Case sensitivity can be
+toggled interactively using \\[ediff-toggle-ignore-case].
+
+Do not remove the default options. If you need to change this variable, add new
+options after the default ones.
+
+This variable is not for customizing the look of the differences produced by
+the command \\[ediff-show-diff-output]. Use the variable
+`ediff-custom-diff-options' for that."
+  :set 'ediff-set-diff-options
+  :type 'string
+  :group 'ediff-diff)
+
+(ediff-defvar-local ediff-ignore-case nil
+  "*If t, skip over difference regions that differ only in letter case.
+This variable can be set either in .emacs or toggled interactively.
+Use `setq-default' if setting it in .emacs")
+
+(defcustom ediff-ignore-case-option "-i"
+  "*Option that causes the diff program to ignore case of letters."
   :type 'string
   :group 'ediff-diff)
 
+(defcustom ediff-ignore-case-option3 ""
+  "*Option that causes the diff3 program to ignore case of letters.
+GNU diff3 doesn't have such an option."
+  :type 'string
+  :group 'ediff-diff)
+
+;; the actual options used in comparison
+(ediff-defvar-local ediff-actual-diff-options ediff-diff-options "")
+
 (defcustom ediff-custom-diff-program ediff-diff-program
   "*Program to use for generating custom diff output for saving it in a file.
 This output is not used by Ediff internally."
@@ -150,9 +148,13 @@ This output is not used by Ediff internally."
   "Pattern to match lines produced by diff3 that describe differences.")
 (defcustom ediff-diff3-options ""
   "*Options to pass to `ediff-diff3-program'."
-  :set 'ediff-reset-diff-options
+  :set 'ediff-set-diff-options
   :type 'string
   :group 'ediff-diff)
+
+;; the actual options used in comparison
+(ediff-defvar-local ediff-actual-diff3-options ediff-diff3-options "")
+
 (defcustom ediff-diff3-ok-lines-regexp
   "^\\([1-3]:\\|====\\|  \\|.*Warning *:\\|.*No newline\\|.*missing newline\\|^\C-m$\\)"
   "*Regexp that matches normal output lines from `ediff-diff3-program'.
@@ -180,7 +182,7 @@ Use `setq-default' if setting it in .emacs")
 This variable can be set either in .emacs or toggled interactively.
 Use `setq-default' if setting it in .emacs")
 
-(ediff-defvar-local ediff-auto-refine-limit 1400
+(ediff-defvar-local ediff-auto-refine-limit 14000
   "*Auto-refine only the regions of this size \(in bytes\) or less.")
 
 ;;; General
@@ -225,9 +227,10 @@ one optional arguments, diff-number to refine.")
 ;; ediff-setup-diff-regions-function, which can also have the value
 ;; ediff-setup-diff-regions3, which takes 4 arguments.
 (defun ediff-setup-diff-regions (file-A file-B file-C)
-  ;; looking either for '-c' or a 'c' in a set of clustered non-long options
-  (if (string-match "^-c\\| -c\\|-[^- ]+c" ediff-diff-options)
-      (error "Option `-c' is not allowed in `ediff-diff-options'"))
+  ;; looking for '-c', '-i', '-u', or 'c', 'i', 'u' among clustered non-long options
+  (if (string-match "^-[ciu]\\| -[ciu]\\|\\(^\\| \\)-[^- ]+[ciu]"
+                   ediff-diff-options)
+      (error "Options `-c', `-u', and `-i' are not allowed in `ediff-diff-options'"))
 
   ;; create, if it doesn't exist
   (or (ediff-buffer-live-p ediff-diff-buffer)
@@ -264,7 +267,7 @@ one optional arguments, diff-number to refine.")
             (ediff-exec-process ediff-diff-program
                                 diff-buffer
                                 'synchronize
-                                ediff-diff-options file1 file2)
+                                ediff-actual-diff-options file1 file2)
             (message "")
             (ediff-with-current-buffer diff-buffer
               (buffer-size))))))
@@ -282,7 +285,9 @@ one optional arguments, diff-number to refine.")
   (let (diff3-job diff-program diff-options ok-regexp diff-list)
     (setq diff3-job ediff-3way-job
          diff-program (if diff3-job ediff-diff3-program ediff-diff-program)
-         diff-options (if diff3-job ediff-diff3-options ediff-diff-options)
+         diff-options (if diff3-job
+                          ediff-actual-diff3-options
+                        ediff-actual-diff-options)
          ok-regexp (if diff3-job
                        ediff-diff3-ok-lines-regexp
                        ediff-diff-ok-lines-regexp))
@@ -312,20 +317,20 @@ one optional arguments, diff-number to refine.")
     ;; fixup diff-list
     (if diff3-job
        (cond ((not file-A)
-              (mapcar (lambda (elt)
-                        (aset elt 0 nil)
-                        (aset elt 1 nil))
-                      (cdr diff-list)))
+              (mapc (lambda (elt)
+                      (aset elt 0 nil)
+                      (aset elt 1 nil))
+                    (cdr diff-list)))
              ((not file-B)
-              (mapcar (lambda (elt)
-                        (aset elt 2 nil)
-                        (aset elt 3 nil))
-                      (cdr diff-list)))
+              (mapc (lambda (elt)
+                      (aset elt 2 nil)
+                      (aset elt 3 nil))
+                    (cdr diff-list)))
              ((not file-C)
-              (mapcar (lambda (elt)
-                        (aset elt 4 nil)
-                        (aset elt 5 nil))
-                      (cdr diff-list)))
+              (mapc (lambda (elt)
+                      (aset elt 4 nil)
+                      (aset elt 5 nil))
+                    (cdr diff-list)))
          ))
 
     (ediff-convert-fine-diffs-to-overlays diff-list reg-num)
@@ -338,6 +343,7 @@ one optional arguments, diff-number to refine.")
            (get-buffer-create (ediff-unique-buffer-name
                                "*ediff-errors" "*"))))
   (ediff-with-current-buffer ediff-error-buffer
+    (setq buffer-undo-list t)
     (erase-buffer)
     (insert (ediff-with-current-buffer diff-buff (buffer-string)))
     (goto-char (point-min))
@@ -364,8 +370,11 @@ one optional arguments, diff-number to refine.")
        (B-buffer ediff-buffer-B)
        (C-buffer ediff-buffer-C)
        (a-prev 1) ; this is needed to set the first diff line correctly
+       (a-prev-pt nil)
        (b-prev 1)
+       (b-prev-pt nil)
        (c-prev 1)
+       (c-prev-pt nil)
        diff-list shift-A shift-B
        )
 
@@ -459,24 +468,52 @@ one optional arguments, diff-number to refine.")
                   c-prev c-end)
           ;; else convert lines to points
           (ediff-with-current-buffer A-buffer
-            (forward-line (- a-begin a-prev))
-            (setq a-begin-pt (point))
-            (forward-line (- a-end a-begin))
-            (setq a-end-pt (point)
-                  a-prev a-end))
+            (let ((longlines-mode-val
+                   (if (and (boundp 'longlines-mode) longlines-mode) 1 0)))
+              ;; we must disable and then restore longlines-mode
+              (if (eq longlines-mode-val 1)
+                  (longlines-mode 0))
+              (goto-char (or a-prev-pt shift-A (point-min)))
+              (forward-line (- a-begin a-prev))
+              (setq a-begin-pt (point))
+              (forward-line (- a-end a-begin))
+              (setq a-end-pt (point)
+                    a-prev a-end
+                    a-prev-pt a-end-pt)
+              (if (eq longlines-mode-val 1)
+                  (longlines-mode longlines-mode-val))
+              ))
           (ediff-with-current-buffer B-buffer
-            (forward-line (- b-begin b-prev))
-            (setq b-begin-pt (point))
-            (forward-line (- b-end b-begin))
-            (setq b-end-pt (point)
-                  b-prev b-end))
+            (let ((longlines-mode-val
+                   (if (and (boundp 'longlines-mode) longlines-mode) 1 0)))
+              (if (eq longlines-mode-val 1)
+                  (longlines-mode 0))
+              (goto-char (or b-prev-pt shift-B (point-min)))
+              (forward-line (- b-begin b-prev))
+              (setq b-begin-pt (point))
+              (forward-line (- b-end b-begin))
+              (setq b-end-pt (point)
+                    b-prev b-end
+                    b-prev-pt b-end-pt)
+              (if (eq longlines-mode-val 1)
+                  (longlines-mode longlines-mode-val))
+              ))
           (if (ediff-buffer-live-p C-buffer)
               (ediff-with-current-buffer C-buffer
-                (forward-line (- c-begin c-prev))
-                (setq c-begin-pt (point))
-                (forward-line (- c-end c-begin))
-                (setq c-end-pt (point)
-                      c-prev c-end)))
+                (let ((longlines-mode-val
+                       (if (and (boundp 'longlines-mode) longlines-mode) 1 0)))
+                  (if (eq longlines-mode-val 1)
+                      (longlines-mode 0))
+                  (goto-char (or c-prev-pt (point-min)))
+                  (forward-line (- c-begin c-prev))
+                  (setq c-begin-pt (point))
+                  (forward-line (- c-end c-begin))
+                  (setq c-end-pt (point)
+                        c-prev c-end
+                        c-prev-pt c-end-pt)
+                  (if (eq longlines-mode-val 1)
+                      (longlines-mode longlines-mode-val))
+                )))
           (setq diff-list
                 (nconc
                  diff-list
@@ -800,7 +837,7 @@ one optional arguments, diff-number to refine.")
             (ediff-make-fine-diffs n 'noforce)
           (ediff-make-fine-diffs n 'skip)))
 
-       ;; highlight iff fine diffs already exist
+       ;; highlight if fine diffs already exist
        ((eq ediff-auto-refine 'off)
         (ediff-make-fine-diffs n 'skip))))
 
@@ -822,9 +859,9 @@ one optional arguments, diff-number to refine.")
   (let ((fine-diff-vector  (ediff-get-fine-diff-vector n buf-type))
        (face (if default
                  'default
-               (face-name
-                (ediff-get-symbol-from-alist
-                 buf-type ediff-fine-diff-face-alist))))
+               (ediff-get-symbol-from-alist
+                buf-type ediff-fine-diff-face-alist)
+               ))
        (priority (if default
                      0
                    (1+ (or (ediff-overlay-get
@@ -911,7 +948,7 @@ delimiter regions"))
       )))
 
 
-(defsubst ediff-convert-fine-diffs-to-overlays (diff-list region-num)
+(defun ediff-convert-fine-diffs-to-overlays (diff-list region-num)
   (ediff-set-fine-overlays-in-one-buffer 'A diff-list region-num)
   (ediff-set-fine-overlays-in-one-buffer 'B diff-list region-num)
   (if ediff-3way-job
@@ -963,8 +1000,11 @@ delimiter regions"))
        (C-buffer ediff-buffer-C)
        (anc-buffer ediff-ancestor-buffer)
        (a-prev 1) ; needed to set the first diff line correctly
+       (a-prev-pt nil)
        (b-prev 1)
+       (b-prev-pt nil)
        (c-prev 1)
+       (c-prev-pt nil)
        (anc-prev 1)
        diff-list shift-A shift-B shift-C
        )
@@ -1060,30 +1100,65 @@ delimiter regions"))
                         c-prev c-end)
                 ;; else convert lines to points
                 (ediff-with-current-buffer A-buffer
-                  (forward-line (- a-begin a-prev))
-                  (setq a-begin-pt (point))
-                  (forward-line (- a-end a-begin))
-                  (setq a-end-pt (point)
-                        a-prev a-end))
+                  (let ((longlines-mode-val
+                         (if (and (boundp 'longlines-mode) longlines-mode) 1 0)))
+                    ;; we must disable and then restore longlines-mode
+                    (if (eq longlines-mode-val 1)
+                        (longlines-mode 0))
+                    (goto-char (or a-prev-pt shift-A (point-min)))
+                    (forward-line (- a-begin a-prev))
+                    (setq a-begin-pt (point))
+                    (forward-line (- a-end a-begin))
+                    (setq a-end-pt (point)
+                          a-prev a-end
+                          a-prev-pt a-end-pt)
+                    (if (eq longlines-mode-val 1)
+                        (longlines-mode longlines-mode-val))
+                    ))
                 (ediff-with-current-buffer B-buffer
-                  (forward-line (- b-begin b-prev))
-                  (setq b-begin-pt (point))
-                  (forward-line (- b-end b-begin))
-                  (setq b-end-pt (point)
-                        b-prev b-end))
+                  (let ((longlines-mode-val
+                         (if (and (boundp 'longlines-mode) longlines-mode) 1 0)))
+                    (if (eq longlines-mode-val 1)
+                        (longlines-mode 0))
+                    (goto-char (or b-prev-pt shift-B (point-min)))
+                    (forward-line (- b-begin b-prev))
+                    (setq b-begin-pt (point))
+                    (forward-line (- b-end b-begin))
+                    (setq b-end-pt (point)
+                          b-prev b-end
+                          b-prev-pt b-end-pt)
+                    (if (eq longlines-mode-val 1)
+                        (longlines-mode longlines-mode-val))
+                    ))
                 (ediff-with-current-buffer C-buffer
-                  (forward-line (- c-begin c-prev))
-                  (setq c-begin-pt (point))
-                  (forward-line (- c-end c-begin))
-                  (setq c-end-pt (point)
-                        c-prev c-end))
+                  (let ((longlines-mode-val
+                         (if (and (boundp 'longlines-mode) longlines-mode) 1 0)))
+                    (if (eq longlines-mode-val 1)
+                        (longlines-mode 0))
+                    (goto-char (or c-prev-pt shift-C (point-min)))
+                    (forward-line (- c-begin c-prev))
+                    (setq c-begin-pt (point))
+                    (forward-line (- c-end c-begin))
+                    (setq c-end-pt (point)
+                          c-prev c-end
+                          c-prev-pt c-end-pt)
+                    (if (eq longlines-mode-val 1)
+                        (longlines-mode longlines-mode-val))
+                    ))
                 (if (ediff-buffer-live-p anc-buffer)
                     (ediff-with-current-buffer anc-buffer
-                      (forward-line (- c-or-anc-begin anc-prev))
-                      (setq anc-begin-pt (point))
-                      (forward-line (- c-or-anc-end c-or-anc-begin))
-                      (setq anc-end-pt (point)
-                            anc-prev c-or-anc-end)))
+                      (let ((longlines-mode-val
+                             (if (and (boundp 'longlines-mode) longlines-mode) 1 0)))
+                        (if (eq longlines-mode-val 1)
+                            (longlines-mode 0))
+                        (forward-line (- c-or-anc-begin anc-prev))
+                        (setq anc-begin-pt (point))
+                        (forward-line (- c-or-anc-end c-or-anc-begin))
+                        (setq anc-end-pt (point)
+                              anc-prev c-or-anc-end)
+                        (if (eq longlines-mode-val 1)
+                            (longlines-mode longlines-mode-val))
+                        )))
                 (setq diff-list
                       (nconc
                        diff-list
@@ -1118,13 +1193,17 @@ delimiter regions"))
 ;; File-C is either the third file to compare (in case of 3-way comparison)
 ;; or it is the ancestor file.
 (defun ediff-setup-diff-regions3 (file-A file-B file-C)
+  ;; looking for '-i' or a 'i' among clustered non-long options
+  (if (string-match "^-i\\| -i\\|\\(^\\| \\)-[^- ]+i" ediff-diff-options)
+      (error "Option `-i' is not allowed in `ediff-diff3-options'"))
+
   (or (ediff-buffer-live-p ediff-diff-buffer)
       (setq ediff-diff-buffer
            (get-buffer-create (ediff-unique-buffer-name "*ediff-diff" "*"))))
 
   (message "Computing differences ...")
   (ediff-exec-process ediff-diff3-program ediff-diff-buffer 'synchronize
-                     ediff-diff3-options file-A file-B file-C)
+                     ediff-actual-diff3-options file-A file-B file-C)
 
   (ediff-prepare-error-list ediff-diff3-ok-lines-regexp ediff-diff-buffer)
   ;;(message "Computing differences ... done")
@@ -1225,7 +1304,8 @@ delimiter regions"))
 Used for splitting difference regions into individual words.")
 (make-variable-buffer-local 'ediff-forward-word-function)
 
-(defvar ediff-whitespace " \n\t\f"
+;; \240 is unicode symbol for nonbreakable whitespace
+(defvar ediff-whitespace " \n\t\f\r\240"
   "*Characters constituting white space.
 These characters are ignored when differing regions are split into words.")
 (make-variable-buffer-local 'ediff-whitespace)
@@ -1341,13 +1421,15 @@ arguments to `skip-chars-forward'."
            (not (file-directory-p f2)))
       (let ((res
             (apply 'call-process ediff-cmp-program nil nil nil
-                   (append ediff-cmp-options (list f1 f2)))))
-       (and (numberp res) (eq res 0))))
-  )
+                   (append ediff-cmp-options (list (expand-file-name f1)
+                                                   (expand-file-name f2))))
+            ))
+       (and (numberp res) (eq res 0)))
+    ))
 
 
 (defun ediff-same-contents (d1 d2 &optional filter-re)
-  "Returns t iff D1 and D2 have the same content.
+  "Return t if D1 and D2 have the same content.
 D1 and D2 can either be both directories or both regular files.
 Symlinks and the likes are not handled.
 If FILTER-RE is non-nil, recursive checking in directories
@@ -1419,12 +1501,49 @@ affects only files whose names match the expression."
     (reverse result)))
 
 
+(defun ediff-set-actual-diff-options ()
+  (if ediff-ignore-case
+      (setq ediff-actual-diff-options
+           (concat ediff-diff-options " " ediff-ignore-case-option)
+           ediff-actual-diff3-options
+           (concat ediff-diff3-options " " ediff-ignore-case-option3))
+    (setq ediff-actual-diff-options ediff-diff-options
+         ediff-actual-diff3-options ediff-diff3-options)
+    )
+  (setq-default ediff-actual-diff-options ediff-actual-diff-options
+               ediff-actual-diff3-options ediff-actual-diff3-options)
+  )
+
+
+;; Ignore case handling - some ideas from drew.adams@@oracle.com
+(defun ediff-toggle-ignore-case ()
+  (interactive)
+  (ediff-barf-if-not-control-buffer)
+  (setq ediff-ignore-case (not ediff-ignore-case))
+  (ediff-set-actual-diff-options)
+  (if ediff-ignore-case
+      (message "Ignoring regions that differ only in case")
+    (message "Ignoring case differences turned OFF"))
+  (cond (ediff-merge-job
+        (message "Ignoring letter case is too dangerous in merge jobs"))
+       ((and ediff-diff3-job (string= ediff-ignore-case-option3 ""))
+        (message "Ignoring letter case is not supported by this diff3 program"))
+       ((and (not ediff-3way-job) (string= ediff-ignore-case-option ""))
+        (message "Ignoring letter case is not supported by this diff program"))
+       (t
+        (sit-for 1)
+        (ediff-update-diffs)))
+  )
+
+
+(provide 'ediff-diff)
+
+
 ;;; Local Variables:
 ;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
 ;;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1)
 ;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body))
 ;;; End:
 
-
 ;;; arch-tag: a86d448e-58d7-4572-a1d9-fdedfa22f648
 ;;; ediff-diff.el ends here