+(defun diff-find-approx-text (text)
+ "Return the buffer position (BEG . END) of the nearest occurrence of TEXT.
+Whitespace differences are ignored."
+ (let* ((orig (point))
+ (re (concat "^[ \t\n\f]*"
+ (mapconcat 'regexp-quote (split-string text) "[ \t\n\f]+")
+ "[ \t\n\f]*\n"))
+ (forw (and (re-search-forward re nil t)
+ (cons (match-beginning 0) (match-end 0))))
+ (back (and (goto-char (+ orig (length text)))
+ (re-search-backward re nil t)
+ (cons (match-beginning 0) (match-end 0)))))
+ ;; Choose the closest match.
+ (if (and forw back)
+ (if (> (- (car forw) orig) (- orig (car back))) back forw)
+ (or back forw))))
+
+(defsubst diff-xor (a b) (if a (not b) b))
+
+(defun diff-find-source-location (&optional other-file reverse)
+ "Find out (BUF LINE-OFFSET POS SRC DST SWITCHED).
+BUF is the buffer corresponding to the source file.
+LINE-OFFSET is the offset between the expected and actual positions
+ of the text of the hunk or nil if the text was not found.
+POS is a pair (BEG . END) indicating the position of the text in the buffer.
+SRC and DST are the two variants of text as returned by `diff-hunk-text'.
+ SRC is the variant that was found in the buffer.
+SWITCHED is non-nil if the patch is already applied."
+ (save-excursion
+ (let* ((other (diff-xor other-file diff-jump-to-old-file))
+ (char-offset (- (point) (progn (diff-beginning-of-hunk) (point))))
+ (hunk (buffer-substring (point)
+ (save-excursion (diff-end-of-hunk) (point))))
+ (old (diff-hunk-text hunk reverse char-offset))
+ (new (diff-hunk-text hunk (not reverse) char-offset))
+ ;; Find the location specification.
+ (line (if (not (looking-at "\\(?:\\*\\{15\\}.*\n\\)?[-@* ]*\\([0-9,]+\\)\\([ acd+]+\\([0-9,]+\\)\\)?"))
+ (error "Can't find the hunk header")
+ (if other (match-string 1)
+ (if (match-end 3) (match-string 3)
+ (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t)
+ (error "Can't find the hunk separator"))
+ (match-string 1)))))
+ (file (or (diff-find-file-name other) (error "Can't find the file")))
+ (buf (find-file-noselect file)))
+ ;; Update the user preference if he so wished.
+ (when (> (prefix-numeric-value other-file) 8)
+ (setq diff-jump-to-old-file other))
+ (with-current-buffer buf
+ (goto-line (string-to-number line))
+ (let* ((orig-pos (point))
+ (switched nil)
+ ;; FIXME: Check for case where both OLD and NEW are found.
+ (pos (or (diff-find-text (car old))
+ (progn (setq switched t) (diff-find-text (car new)))
+ (progn (setq switched nil)
+ (condition-case nil
+ (diff-find-approx-text (car old))
+ (invalid-regexp nil))) ;Regex too big.
+ (progn (setq switched t)
+ (condition-case nil
+ (diff-find-approx-text (car new))
+ (invalid-regexp nil))) ;Regex too big.
+ (progn (setq switched nil) nil))))
+ (nconc
+ (list buf)
+ (if pos
+ (list (count-lines orig-pos (car pos)) pos)
+ (list nil (cons orig-pos (+ orig-pos (length (car old))))))
+ (if switched (list new old t) (list old new))))))))
+
+
+(defun diff-hunk-status-msg (line-offset reversed dry-run)
+ (let ((msg (if dry-run
+ (if reversed "already applied" "not yet applied")
+ (if reversed "undone" "applied"))))
+ (message (cond ((null line-offset) "Hunk text not found")
+ ((= line-offset 0) "Hunk %s")
+ ((= line-offset 1) "Hunk %s at offset %d line")
+ (t "Hunk %s at offset %d lines"))
+ msg line-offset)))
+
+
+(defun diff-apply-hunk (&optional reverse)
+ "Apply the current hunk to the source file and go to the next.