diff options
| -rw-r--r-- | lisp/ediff-ptch.el | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/lisp/ediff-ptch.el b/lisp/ediff-ptch.el new file mode 100644 index 00000000000..401bfc1ef08 --- /dev/null +++ b/lisp/ediff-ptch.el | |||
| @@ -0,0 +1,562 @@ | |||
| 1 | ;;; ediff-ptch.el --- Ediff's patch support | ||
| 2 | |||
| 3 | ;; Copyright (C) 1996 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Michael Kifer <kifer@cs.sunysb.edu> | ||
| 6 | |||
| 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 | ||
| 11 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 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 the | ||
| 21 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 22 | ;; Boston, MA 02111-1307, USA. | ||
| 23 | |||
| 24 | |||
| 25 | ;;; Code: | ||
| 26 | |||
| 27 | (defvar ediff-last-dir-patch nil | ||
| 28 | "Last directory used by an Ediff command for file to patch.") | ||
| 29 | |||
| 30 | (defvar ediff-backup-extension | ||
| 31 | (if (memq system-type '(vax-vms axp-vms emx ms-dos windows-nt windows-95)) | ||
| 32 | "_orig" ".orig") | ||
| 33 | "Default backup extension for the patch program.") | ||
| 34 | |||
| 35 | (defvar ediff-patch-default-directory nil | ||
| 36 | "*Default directory to look for patches.") | ||
| 37 | |||
| 38 | (defvar ediff-context-diff-label-regexp | ||
| 39 | (concat "\\(" ; context diff 2-liner | ||
| 40 | "^\\*\\*\\* \\([^ \t]+\\)[^*]+[\t ]*\n--- \\([^ \t]+\\)" | ||
| 41 | "\\|" ; GNU unified format diff 2-liner | ||
| 42 | "^--- \\([^ \t]+\\)[^-]+[\t ]*\n\\+\\+\\+ \\([^ \t]+\\)" | ||
| 43 | "\\)") | ||
| 44 | "*Regexp matching filename 2-liners at the start of each context diff.") | ||
| 45 | |||
| 46 | (defvar ediff-patch-program "patch" | ||
| 47 | "*Name of the program that applies patches.") | ||
| 48 | (defvar ediff-patch-options "" | ||
| 49 | "*Options to pass to ediff-patch-program.") | ||
| 50 | |||
| 51 | ;; The buffer of the patch file. Local to control buffer. | ||
| 52 | (ediff-defvar-local ediff-patchbufer nil "") | ||
| 53 | |||
| 54 | ;; The buffer where patch displays its diagnostics. | ||
| 55 | (ediff-defvar-local ediff-patch-diagnostics nil "") | ||
| 56 | |||
| 57 | ;; Map of patch buffer. Has the form: | ||
| 58 | ;; ((filename1 marker1 marker2) (filename2 marker1 marker2) ...) | ||
| 59 | ;; where filenames are files to which patch would have applied the patch; | ||
| 60 | ;; marker1 delimits the beginning of the corresponding patch and marker2 does | ||
| 61 | ;; it for the end. | ||
| 62 | (ediff-defvar-local ediff-patch-map nil "") | ||
| 63 | |||
| 64 | ;; strip prefix from filename | ||
| 65 | ;; returns /dev/null, if can't strip prefix | ||
| 66 | (defsubst ediff-file-name-sans-prefix (filename prefix) | ||
| 67 | (save-match-data | ||
| 68 | (if (string-match (concat "^" prefix) filename) | ||
| 69 | (substring filename (match-end 0)) | ||
| 70 | (concat "/null/" filename)))) | ||
| 71 | |||
| 72 | |||
| 73 | |||
| 74 | ;; no longer used | ||
| 75 | ;; return the number of matches of regexp in buf starting from the beginning | ||
| 76 | (defun ediff-count-matches (regexp buf) | ||
| 77 | (ediff-eval-in-buffer buf | ||
| 78 | (let ((count 0) opoint) | ||
| 79 | (save-excursion | ||
| 80 | (goto-char (point-min)) | ||
| 81 | (while (and (not (eobp)) | ||
| 82 | (progn (setq opoint (point)) | ||
| 83 | (re-search-forward regexp nil t))) | ||
| 84 | (if (= opoint (point)) | ||
| 85 | (forward-char 1) | ||
| 86 | (setq count (1+ count))))) | ||
| 87 | count))) | ||
| 88 | |||
| 89 | ;; Scan BUF (which is supposed to contain a patch) and make a list of the form | ||
| 90 | ;; ((filename1 marker1 marker2) (filename2 marker1 marker2) ...) | ||
| 91 | ;; where filenames are files to which patch would have applied the patch; | ||
| 92 | ;; marker1 delimits the beginning of the corresponding patch and marker2 does | ||
| 93 | ;; it for the end. This list is then assigned to ediff-patch-map. | ||
| 94 | ;; Returns the number of elements in the list ediff-patch-map | ||
| 95 | (defun ediff-map-patch-buffer (buf) | ||
| 96 | (ediff-eval-in-buffer buf | ||
| 97 | (let ((count 0) | ||
| 98 | (mark1 (move-marker (make-marker) (point-min))) | ||
| 99 | (mark1-end (point-min)) | ||
| 100 | (possible-file-names '("/dev/null" . "/dev/null")) | ||
| 101 | mark2-end mark2 filenames | ||
| 102 | beg1 beg2 end1 end2 | ||
| 103 | patch-map opoint) | ||
| 104 | (save-excursion | ||
| 105 | (goto-char (point-min)) | ||
| 106 | (setq opoint (point)) | ||
| 107 | (while (and (not (eobp)) | ||
| 108 | (re-search-forward ediff-context-diff-label-regexp nil t)) | ||
| 109 | (if (= opoint (point)) | ||
| 110 | (forward-char 1) ; ensure progress towards the end | ||
| 111 | (setq mark2 (move-marker (make-marker) (match-beginning 0)) | ||
| 112 | mark2-end (match-end 0) | ||
| 113 | beg1 (match-beginning 2) | ||
| 114 | end1 (match-end 2) | ||
| 115 | beg2 (match-beginning 3) | ||
| 116 | end2 (match-end 3)) | ||
| 117 | ;; possible-file-names is holding the new file names until we | ||
| 118 | ;; insert the old file name in the patch map | ||
| 119 | ;; It is a pair (filename from 1st header line . fn from 2nd line) | ||
| 120 | (setq possible-file-names | ||
| 121 | (cons (if (and beg1 end1) | ||
| 122 | (buffer-substring beg1 end1) | ||
| 123 | "/dev/null") | ||
| 124 | (if (and beg2 end2) | ||
| 125 | (buffer-substring beg2 end2) | ||
| 126 | "/dev/null"))) | ||
| 127 | ;; check for any `Index:' or `Prereq:' lines, but don't use them | ||
| 128 | (if (re-search-backward "^Index:" mark1-end 'noerror) | ||
| 129 | (move-marker mark2 (match-beginning 0))) | ||
| 130 | (if (re-search-backward "^Prereq:" mark1-end 'noerror) | ||
| 131 | (move-marker mark2 (match-beginning 0))) | ||
| 132 | |||
| 133 | (goto-char mark2-end) | ||
| 134 | |||
| 135 | (if filenames | ||
| 136 | (setq patch-map (cons (list filenames mark1 mark2) patch-map))) | ||
| 137 | (setq mark1 mark2 | ||
| 138 | mark1-end mark2-end | ||
| 139 | filenames possible-file-names)) | ||
| 140 | (setq opoint (point) | ||
| 141 | count (1+ count)))) | ||
| 142 | (setq mark2 (point-max-marker) | ||
| 143 | patch-map (cons (list possible-file-names mark1 mark2) patch-map)) | ||
| 144 | (setq ediff-patch-map (nreverse patch-map)) | ||
| 145 | count))) | ||
| 146 | |||
| 147 | ;; Fix up the file names in the list using the argument FILENAME | ||
| 148 | ;; Algorithm: find the first file's directory and cut it out from each file | ||
| 149 | ;; name in the patch. Prepend the directory of FILENAME to each file in the | ||
| 150 | ;; patch. In addition, the first file in the patch is replaced by FILENAME. | ||
| 151 | ;; Each file is actually a file-pair of files found in the context diff header | ||
| 152 | ;; In the end, for each pair, we select the shortest existing file. | ||
| 153 | ;; Note: Ediff doesn't recognize multi-file patches that are separated | ||
| 154 | ;; with the `Index:' line. It treats them as a single-file patch. | ||
| 155 | ;; | ||
| 156 | ;; Executes inside the patch buffer | ||
| 157 | (defun ediff-fixup-patch-map (filename) | ||
| 158 | (setq filename (expand-file-name filename)) | ||
| 159 | (let ((actual-dir (if (file-directory-p filename) | ||
| 160 | ;; directory part of filename | ||
| 161 | (file-name-as-directory filename) | ||
| 162 | (file-name-directory filename))) | ||
| 163 | ;; directory part of the first file in the patch | ||
| 164 | (base-dir1 (file-name-directory (car (car (car ediff-patch-map))))) | ||
| 165 | (base-dir2 (file-name-directory (cdr (car (car ediff-patch-map))))) | ||
| 166 | ) | ||
| 167 | |||
| 168 | ;; chop off base-dirs | ||
| 169 | (mapcar (function (lambda (triple) | ||
| 170 | (or (string= (car (car triple)) "/dev/null") | ||
| 171 | (setcar (car triple) | ||
| 172 | (ediff-file-name-sans-prefix | ||
| 173 | (car (car triple)) base-dir1))) | ||
| 174 | (or (string= (cdr (car triple)) "/dev/null") | ||
| 175 | (setcdr (car triple) | ||
| 176 | (ediff-file-name-sans-prefix | ||
| 177 | (cdr (car triple)) base-dir2))) | ||
| 178 | )) | ||
| 179 | ediff-patch-map) | ||
| 180 | |||
| 181 | ;; take the given file name into account | ||
| 182 | (or (file-directory-p filename) | ||
| 183 | (string= "/dev/null" filename) | ||
| 184 | (progn | ||
| 185 | (setcar (car ediff-patch-map) | ||
| 186 | (cons (file-name-nondirectory filename) | ||
| 187 | (file-name-nondirectory filename))))) | ||
| 188 | |||
| 189 | ;; prepend actual-dir | ||
| 190 | (mapcar (function (lambda (triple) | ||
| 191 | (if (and (string-match "^/null/" (car (car triple))) | ||
| 192 | (string-match "^/null/" (cdr (car triple)))) | ||
| 193 | ;; couldn't strip base-dir1 and base-dir2 | ||
| 194 | ;; hence, something wrong | ||
| 195 | (progn | ||
| 196 | (with-output-to-temp-buffer ediff-msg-buffer | ||
| 197 | (princ | ||
| 198 | (format " | ||
| 199 | The patch file contains a context diff for | ||
| 200 | %s | ||
| 201 | %s | ||
| 202 | |||
| 203 | However, Ediff cannot infer the name of the actual file | ||
| 204 | to be patched on your system. If you know the correct file name, | ||
| 205 | please enter it now. | ||
| 206 | |||
| 207 | If you don't know and still would like to apply patches to | ||
| 208 | other files, enter /dev/null | ||
| 209 | " | ||
| 210 | (substring (car (car triple)) 6) | ||
| 211 | (substring (cdr (car triple)) 6)))) | ||
| 212 | (let ((directory t) | ||
| 213 | user-file) | ||
| 214 | (while directory | ||
| 215 | (setq user-file | ||
| 216 | (read-file-name | ||
| 217 | "Please enter file name: " | ||
| 218 | actual-dir actual-dir t)) | ||
| 219 | (if (not (file-directory-p user-file)) | ||
| 220 | (setq directory nil) | ||
| 221 | (setq directory t) | ||
| 222 | (beep) | ||
| 223 | (message "%s is a directory" user-file) | ||
| 224 | (sit-for 2))) | ||
| 225 | (setcar triple (cons user-file user-file)))) | ||
| 226 | (setcar (car triple) | ||
| 227 | (expand-file-name | ||
| 228 | (concat actual-dir (car (car triple))))) | ||
| 229 | (setcdr (car triple) | ||
| 230 | (expand-file-name | ||
| 231 | (concat actual-dir (cdr (car triple)))))) | ||
| 232 | )) | ||
| 233 | ediff-patch-map) | ||
| 234 | ;; check for the shorter existing file in each pair and discard the other | ||
| 235 | ;; one | ||
| 236 | (mapcar (function (lambda (triple) | ||
| 237 | (let* ((file1 (car (car triple))) | ||
| 238 | (file2 (cdr (car triple))) | ||
| 239 | (f1-exists (file-exists-p file1)) | ||
| 240 | (f2-exists (file-exists-p file2))) | ||
| 241 | (cond | ||
| 242 | ((and (< (length file2) (length file1)) | ||
| 243 | f2-exists) | ||
| 244 | (setcar triple file2)) | ||
| 245 | ((and (< (length file1) (length file2)) | ||
| 246 | f1-exists) | ||
| 247 | (setcar triple file1)) | ||
| 248 | ((and f1-exists f2-exists | ||
| 249 | (string= file1 file2)) | ||
| 250 | (setcar triple file1)) | ||
| 251 | ((and f1-exists f2-exists) | ||
| 252 | (with-output-to-temp-buffer ediff-msg-buffer | ||
| 253 | (princ (format " | ||
| 254 | Ediff has inferred that | ||
| 255 | %s | ||
| 256 | %s | ||
| 257 | are possible targets for applying the patch. | ||
| 258 | Both files seem to be plausible alternatives. | ||
| 259 | |||
| 260 | Please advice: | ||
| 261 | Type `y' to use %s as the target; | ||
| 262 | Type `n' to use %s as the target. | ||
| 263 | " | ||
| 264 | file1 file2 file2 file1))) | ||
| 265 | (setcar triple | ||
| 266 | (if (y-or-n-p (format "Use %s ? " file2)) | ||
| 267 | file2 file1))) | ||
| 268 | (f2-exists (setcar triple file2)) | ||
| 269 | (f1-exists (setcar triple file1)) | ||
| 270 | (t | ||
| 271 | (with-output-to-temp-buffer ediff-msg-buffer | ||
| 272 | (princ (format " | ||
| 273 | Ediff inferred that | ||
| 274 | %s | ||
| 275 | %s | ||
| 276 | are possible alternative targets for this patch. | ||
| 277 | |||
| 278 | However, these files do not exist. | ||
| 279 | |||
| 280 | Please enter an alternative patch target ... | ||
| 281 | " | ||
| 282 | file1 file2))) | ||
| 283 | (let ((directory t) | ||
| 284 | target) | ||
| 285 | (while directory | ||
| 286 | (setq target (read-file-name | ||
| 287 | "Please enter a patch target: " | ||
| 288 | actual-dir actual-dir t)) | ||
| 289 | (if (not (file-directory-p target)) | ||
| 290 | (setq directory nil) | ||
| 291 | (beep) | ||
| 292 | (message "%s is a directory" target) | ||
| 293 | (sit-for 2))) | ||
| 294 | (setcar triple target))))))) | ||
| 295 | ediff-patch-map) | ||
| 296 | )) | ||
| 297 | |||
| 298 | (defun ediff-show-patch-diagnostics () | ||
| 299 | (interactive) | ||
| 300 | (cond ((window-live-p ediff-window-A) | ||
| 301 | (set-window-buffer ediff-window-A ediff-patch-diagnostics)) | ||
| 302 | ((window-live-p ediff-window-B) | ||
| 303 | (set-window-buffer ediff-window-B ediff-patch-diagnostics)) | ||
| 304 | (t (display-buffer ediff-patch-diagnostics 'not-this-window)))) | ||
| 305 | |||
| 306 | (defun ediff-get-patch-buffer () | ||
| 307 | "Obtain patch buffer. If patch is already in a buffer---use it. | ||
| 308 | Else, read patch file into a new buffer." | ||
| 309 | (let ((dir (cond (ediff-patch-default-directory) ; try patch default dir | ||
| 310 | (ediff-use-last-dir ediff-last-dir-patch) | ||
| 311 | (t default-directory))) | ||
| 312 | patch-buf) | ||
| 313 | (if (y-or-n-p "Is the patch already in a buffer? ") | ||
| 314 | (setq patch-buf | ||
| 315 | (get-buffer | ||
| 316 | (read-buffer | ||
| 317 | "Which buffer contains the patch? " | ||
| 318 | (current-buffer) 'must-match))) | ||
| 319 | (setq patch-buf | ||
| 320 | (find-file-noselect | ||
| 321 | (read-file-name "Which file contains the patch? " dir)))) | ||
| 322 | |||
| 323 | (ediff-eval-in-buffer patch-buf | ||
| 324 | (goto-char (point-min)) | ||
| 325 | (or (ediff-get-visible-buffer-window patch-buf) | ||
| 326 | (progn | ||
| 327 | (pop-to-buffer patch-buf 'other-window) | ||
| 328 | (select-window (previous-window))))) | ||
| 329 | (ediff-map-patch-buffer patch-buf) | ||
| 330 | patch-buf)) | ||
| 331 | |||
| 332 | ;; Dispatch the right patch file function: regular or meta-level, | ||
| 333 | ;; depending on how many patches are in the patch file. | ||
| 334 | ;; At present, there is no support for meta-level patches. | ||
| 335 | ;; Should return either the ctl buffer or the meta-buffer | ||
| 336 | (defun ediff-dispatch-file-patching-job (patch-buf filename | ||
| 337 | &optional startup-hooks) | ||
| 338 | (ediff-eval-in-buffer patch-buf | ||
| 339 | ;; relativize names in the patch with respect to source-file | ||
| 340 | (ediff-fixup-patch-map filename) | ||
| 341 | (if (< (length ediff-patch-map) 2) | ||
| 342 | (ediff-patch-file-internal | ||
| 343 | patch-buf | ||
| 344 | (if (and (not (string-match "^/dev/null" (car (car ediff-patch-map)))) | ||
| 345 | (> (length (car (car ediff-patch-map))) 1)) | ||
| 346 | (car (car ediff-patch-map)) | ||
| 347 | filename) | ||
| 348 | startup-hooks) | ||
| 349 | (ediff-multi-patch-internal patch-buf startup-hooks)) | ||
| 350 | )) | ||
| 351 | |||
| 352 | |||
| 353 | (defun ediff-patch-buffer-internal (patch-buf buf-to-patch-name | ||
| 354 | &optional startup-hooks) | ||
| 355 | (let* ((buf-to-patch (get-buffer buf-to-patch-name)) | ||
| 356 | (file-name-ok (if buf-to-patch (buffer-file-name buf-to-patch))) | ||
| 357 | (buf-mod-status (buffer-modified-p buf-to-patch)) | ||
| 358 | (multifile-patch-p (> (length (ediff-eval-in-buffer patch-buf | ||
| 359 | ediff-patch-map)) 1)) | ||
| 360 | default-dir file-name ctl-buf) | ||
| 361 | (if file-name-ok | ||
| 362 | (setq file-name file-name-ok) | ||
| 363 | (if multifile-patch-p | ||
| 364 | (error | ||
| 365 | "Can't apply multi-file patches to buffers that visit no files")) | ||
| 366 | (ediff-eval-in-buffer buf-to-patch | ||
| 367 | (setq default-dir default-directory) | ||
| 368 | (setq file-name (ediff-make-temp-file buf-to-patch)) | ||
| 369 | (set-visited-file-name file-name) | ||
| 370 | (setq buffer-auto-save-file-name nil) ; don't create auto-save file | ||
| 371 | ;;don't confuse the user with a new bufname | ||
| 372 | (rename-buffer buf-to-patch-name) | ||
| 373 | (set-buffer-modified-p nil) | ||
| 374 | (set-visited-file-modtime) ; sync buffer and temp file | ||
| 375 | (setq default-directory default-dir) | ||
| 376 | )) | ||
| 377 | |||
| 378 | ;; dispatch a patch function | ||
| 379 | (setq ctl-buf (ediff-dispatch-file-patching-job | ||
| 380 | patch-buf file-name startup-hooks)) | ||
| 381 | |||
| 382 | (if file-name-ok | ||
| 383 | () | ||
| 384 | ;; buffer wasn't visiting any file, | ||
| 385 | ;; so we will not run meta-level ediff here | ||
| 386 | (ediff-eval-in-buffer ctl-buf | ||
| 387 | (delete-file (buffer-file-name ediff-buffer-A)) | ||
| 388 | (delete-file (buffer-file-name ediff-buffer-B)) | ||
| 389 | (ediff-eval-in-buffer ediff-buffer-A | ||
| 390 | (if default-dir (setq default-directory default-dir)) | ||
| 391 | (set-visited-file-name nil) | ||
| 392 | (rename-buffer buf-to-patch-name) | ||
| 393 | (set-buffer-modified-p buf-mod-status)) | ||
| 394 | (ediff-eval-in-buffer ediff-buffer-B | ||
| 395 | (setq buffer-auto-save-file-name nil) ; don't create auto-save file | ||
| 396 | (if default-dir (setq default-directory default-dir)) | ||
| 397 | (set-visited-file-name nil) | ||
| 398 | (rename-buffer (ediff-unique-buffer-name | ||
| 399 | (concat buf-to-patch-name "_patched") "")) | ||
| 400 | (set-buffer-modified-p t)))) | ||
| 401 | )) | ||
| 402 | |||
| 403 | (defun ediff-patch-file-internal (patch-buf source-filename | ||
| 404 | &optional startup-hooks) | ||
| 405 | (setq source-filename (expand-file-name source-filename)) | ||
| 406 | |||
| 407 | (let* ((backup-extension | ||
| 408 | ;; if the user specified a -b option, extract the backup | ||
| 409 | ;; extension from there; else use ediff-backup-extension | ||
| 410 | (substring ediff-patch-options | ||
| 411 | (if (string-match "-b[ \t]+" ediff-patch-options) | ||
| 412 | (match-end 0) 0) | ||
| 413 | (if (string-match "-b[ \t]+[^ \t]+" ediff-patch-options) | ||
| 414 | (match-end 0) 0))) | ||
| 415 | (shell-file-name ediff-shell) | ||
| 416 | (patch-diagnostics (get-buffer-create "*ediff patch diagnostics*")) | ||
| 417 | ;; ediff-find-file may use a temp file to do the patch | ||
| 418 | ;; so, we save source-filename and true-source-filename as a var | ||
| 419 | ;; that initially is source-filename but may be changed to a temp | ||
| 420 | ;; file for the purpose of patching. | ||
| 421 | (true-source-filename source-filename) | ||
| 422 | (target-filename source-filename) | ||
| 423 | target-buf buf-to-patch file-name-magic-p ctl-buf backup-style) | ||
| 424 | |||
| 425 | ;; if the user didn't specify a backup extension, use | ||
| 426 | ;; ediff-backup-extension | ||
| 427 | (if (string= backup-extension "") | ||
| 428 | (setq backup-extension ediff-backup-extension)) | ||
| 429 | (if (string-match "-V" ediff-patch-options) | ||
| 430 | (error | ||
| 431 | "Ediff doesn't take the -V option in `ediff-patch-options'--sorry")) | ||
| 432 | |||
| 433 | ;; Make a temp file, if source-filename has a magic file handler (or if | ||
| 434 | ;; it is handled via auto-mode-alist and similar magic). | ||
| 435 | ;; Check if there is a buffer visiting source-filename and if they are in | ||
| 436 | ;; sync; arrange for the deletion of temp file. | ||
| 437 | (ediff-find-file 'true-source-filename 'buf-to-patch | ||
| 438 | 'ediff-last-dir-patch 'startup-hooks) | ||
| 439 | |||
| 440 | ;; Check if source file name has triggered black magic, such as file name | ||
| 441 | ;; handlers or auto mode alist, and make a note of it. | ||
| 442 | ;; true-source-filename should be either the original name or a | ||
| 443 | ;; temporary file where we put the after-product of the file handler. | ||
| 444 | (setq file-name-magic-p (not (equal (file-truename true-source-filename) | ||
| 445 | (file-truename source-filename)))) | ||
| 446 | |||
| 447 | ;; Checkout orig file, if necessary, so that the patched file could be | ||
| 448 | ;; checked back in. | ||
| 449 | (if (ediff-file-checked-in-p (buffer-file-name buf-to-patch)) | ||
| 450 | (ediff-toggle-read-only buf-to-patch)) | ||
| 451 | |||
| 452 | (ediff-eval-in-buffer patch-diagnostics | ||
| 453 | (insert-buffer patch-buf) | ||
| 454 | (message "Applying patch ... ") | ||
| 455 | ;; fix environment for gnu patch, so it won't make numbered extensions | ||
| 456 | (setq backup-style (getenv "VERSION_CONTROL")) | ||
| 457 | (setenv "VERSION_CONTROL" nil) | ||
| 458 | ;; always pass patch the -f option, so it won't ask any questions | ||
| 459 | (shell-command-on-region | ||
| 460 | (point-min) (point-max) | ||
| 461 | (format "%s -f %s -b %s %s" | ||
| 462 | ediff-patch-program ediff-patch-options | ||
| 463 | backup-extension | ||
| 464 | (expand-file-name true-source-filename)) | ||
| 465 | t) | ||
| 466 | ;; restore environment for gnu patch | ||
| 467 | (setenv "VERSION_CONTROL" backup-style)) | ||
| 468 | |||
| 469 | (message "Applying patch ... done") | ||
| 470 | (message "") | ||
| 471 | |||
| 472 | (switch-to-buffer patch-diagnostics) | ||
| 473 | (sit-for 0) ; synchronize - let the user see diagnostics | ||
| 474 | |||
| 475 | (or (file-exists-p (concat true-source-filename backup-extension)) | ||
| 476 | (error "Patch appears to have failed")) | ||
| 477 | |||
| 478 | ;; If black magic is involved, apply patch to a temp copy of the | ||
| 479 | ;; file. Otherwise, apply patch to the orig copy. If patch is applied | ||
| 480 | ;; to temp copy, we name the result old-name_patched for local files | ||
| 481 | ;; and temp-copy_patched for remote files. The orig file name isn't | ||
| 482 | ;; changed, and the temp copy of the original is later deleted. | ||
| 483 | ;; Without magic, the original file is renamed (usually into | ||
| 484 | ;; old-name_orig) and the result of patching will have the same name as | ||
| 485 | ;; the original. | ||
| 486 | (if (not file-name-magic-p) | ||
| 487 | (ediff-eval-in-buffer buf-to-patch | ||
| 488 | (set-visited-file-name (concat source-filename backup-extension)) | ||
| 489 | (set-buffer-modified-p nil)) | ||
| 490 | |||
| 491 | ;; Black magic in effect. | ||
| 492 | ;; If orig file was remote, put the patched file in the temp directory. | ||
| 493 | ;; If orig file is local, put the patched file in the directory of | ||
| 494 | ;; the orig file. | ||
| 495 | (setq target-filename | ||
| 496 | (concat | ||
| 497 | (if (ediff-file-remote-p (file-truename source-filename)) | ||
| 498 | true-source-filename | ||
| 499 | source-filename) | ||
| 500 | "_patched")) | ||
| 501 | |||
| 502 | (rename-file true-source-filename target-filename t) | ||
| 503 | |||
| 504 | ;; arrange that the temp copy of orig will be deleted | ||
| 505 | (rename-file (concat true-source-filename backup-extension) | ||
| 506 | true-source-filename t)) | ||
| 507 | |||
| 508 | ;; make orig buffer read-only | ||
| 509 | (setq startup-hooks | ||
| 510 | (cons 'ediff-set-read-only-in-buf-A startup-hooks)) | ||
| 511 | |||
| 512 | ;; set up a buf for the patched file | ||
| 513 | (setq target-buf (find-file-noselect target-filename)) | ||
| 514 | |||
| 515 | (setq ctl-buf | ||
| 516 | (ediff-buffers-internal | ||
| 517 | buf-to-patch target-buf nil | ||
| 518 | startup-hooks 'epatch)) | ||
| 519 | (ediff-eval-in-buffer ctl-buf | ||
| 520 | (setq ediff-patchbufer patch-buf | ||
| 521 | ediff-patch-diagnostics patch-diagnostics)) | ||
| 522 | |||
| 523 | (bury-buffer patch-diagnostics) | ||
| 524 | (message "Type `P', if you need to see patch diagnostics") | ||
| 525 | ctl-buf)) | ||
| 526 | |||
| 527 | (defun ediff-multi-patch-internal (patch-buf &optional startup-hooks) | ||
| 528 | (let (meta-buf) | ||
| 529 | (setq startup-hooks | ||
| 530 | ;; this sets various vars in the meta buffer inside | ||
| 531 | ;; ediff-prepare-meta-buffer | ||
| 532 | (cons (` (lambda () | ||
| 533 | ;; tell what to do if the user clicks on a session record | ||
| 534 | (setq ediff-session-action-function | ||
| 535 | 'ediff-patch-file-form-meta | ||
| 536 | ediff-meta-patchbufer patch-buf) | ||
| 537 | )) | ||
| 538 | startup-hooks)) | ||
| 539 | (setq meta-buf (ediff-prepare-meta-buffer | ||
| 540 | 'ediff-filegroup-action | ||
| 541 | (ediff-eval-in-buffer patch-buf | ||
| 542 | ;; nil replaces a regular expression | ||
| 543 | (cons (list nil (format "%S" patch-buf)) | ||
| 544 | ediff-patch-map)) | ||
| 545 | "*Ediff Session Group Panel" | ||
| 546 | 'ediff-redraw-directory-group-buffer | ||
| 547 | 'ediff-multifile-patch | ||
| 548 | startup-hooks)) | ||
| 549 | (ediff-show-meta-buffer meta-buf) | ||
| 550 | )) | ||
| 551 | |||
| 552 | |||
| 553 | |||
| 554 | |||
| 555 | ;;; Local Variables: | ||
| 556 | ;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun) | ||
| 557 | ;;; eval: (put 'ediff-eval-in-buffer 'lisp-indent-hook 1) | ||
| 558 | ;;; End: | ||
| 559 | |||
| 560 | (provide 'ediff-ptch) | ||
| 561 | |||
| 562 | ;;; ediff-ptch.el ends here | ||