emerge.el 120 KB
Newer Older
1
;;; emerge.el --- merge diffs under Emacs control
Richard M. Stallman's avatar
Richard M. Stallman committed
2

3 4
;;; The author has placed this file in the public domain.

5 6
;; This file is part of GNU Emacs.

Richard M. Stallman's avatar
Richard M. Stallman committed
7
;; Author: Dale R. Worley <worley@world.std.com>
8
;; Keywords: unix, vc, tools
Richard M. Stallman's avatar
Richard M. Stallman committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22

;; This software was created by Dale R. Worley and is
;; distributed free of charge.  It is placed in the public domain and
;; permission is granted to anyone to use, duplicate, modify and redistribute
;; it provided that this notice is attached.

;; Dale R. Worley provides absolutely NO WARRANTY OF ANY KIND
;; with respect to this software.  The entire risk as to the quality and
;; performance of this software is with the user.  IN NO EVENT WILL DALE
;; R. WORLEY BE LIABLE TO ANYONE FOR ANY DAMAGES ARISING OUT THE
;; USE OF THIS SOFTWARE, INCLUDING, WITHOUT LIMITATION, DAMAGES RESULTING FROM
;; LOST DATA OR LOST PROFITS, OR FOR ANY SPECIAL, INCIDENTAL OR CONSEQUENTIAL
;; DAMAGES.

23 24
;;; Commentary:

25
;;; Code:
Jim Blandy's avatar
Jim Blandy committed
26

27 28 29 30 31 32 33 34 35
;; There aren't really global variables, just dynamic bindings
(defvar A-begin)
(defvar A-end)
(defvar B-begin)
(defvar B-end)
(defvar diff-vector)
(defvar merge-begin)
(defvar merge-end)
(defvar valid-diff)
36

Jim Blandy's avatar
Jim Blandy committed
37 38
;;; Macros

39 40
(defmacro emerge-defvar-local (var value doc)
  "Defines SYMBOL as an advertised variable.
41
Performs a defvar, then executes `make-variable-buffer-local' on
Glenn Morris's avatar
Glenn Morris committed
42
the variable.  Also sets the `permanent-local' property, so that
43
`kill-all-local-variables' (called by major-mode setting commands)
44
won't destroy Emerge control variables."
45 46 47
  `(progn
    (defvar ,var ,value ,doc)
    (make-variable-buffer-local ',var)
Glenn Morris's avatar
Glenn Morris committed
48
    (put ',var 'permanent-local t)))
Jim Blandy's avatar
Jim Blandy committed
49 50

;; Add entries to minor-mode-alist so that emerge modes show correctly
51 52 53 54 55 56
(defvar emerge-minor-modes-list
  '((emerge-mode " Emerge")
    (emerge-fast-mode " F")
    (emerge-edit-mode " E")
    (emerge-auto-advance " A")
    (emerge-skip-prefers " S")))
Jim Blandy's avatar
Jim Blandy committed
57 58 59 60 61 62
(if (not (assq 'emerge-mode minor-mode-alist))
    (setq minor-mode-alist (append emerge-minor-modes-list
				   minor-mode-alist)))

;; We need to define this function so describe-mode can describe Emerge mode.
(defun emerge-mode ()
63 64 65 66 67 68 69 70 71 72
  "Emerge mode is used by the Emerge file-merging package.
It is entered only through one of the functions:
	`emerge-files'
	`emerge-files-with-ancestor'
	`emerge-buffers'
	`emerge-buffers-with-ancestor'
	`emerge-files-command'
	`emerge-files-with-ancestor-command'
	`emerge-files-remote'
	`emerge-files-with-ancestor-remote'
Jim Blandy's avatar
Jim Blandy committed
73 74 75

Commands:
\\{emerge-basic-keymap}
76 77
Commands must be prefixed by \\<emerge-fast-keymap>\\[emerge-basic-keymap] in `edit' mode,
but can be invoked directly in `fast' mode.")
Jim Blandy's avatar
Jim Blandy committed
78 79 80

;;; Emerge configuration variables

Andreas Schwab's avatar
Andreas Schwab committed
81 82 83 84
(defgroup emerge nil
  "Merge diffs under Emacs control."
  :group 'tools)

Jim Blandy's avatar
Jim Blandy committed
85 86 87 88 89 90
;; Commands that produce difference files
;; All that can be configured is the name of the programs to execute
;; (emerge-diff-program and emerge-diff3-program) and the options
;; to be provided (emerge-diff-options).  The order in which the file names
;; are given is fixed.
;; The file names are always expanded (see expand-file-name) before being
91
;; passed to diff, thus they need not be invoked under a shell that
92
;; understands `~'.
Jim Blandy's avatar
Jim Blandy committed
93 94 95
;; The code which processes the diff/diff3 output depends on all the
;; finicky details of their output, including the somewhat strange
;; way they number lines of a file.
Andreas Schwab's avatar
Andreas Schwab committed
96
(defcustom emerge-diff-program "diff"
Lute Kamstra's avatar
Lute Kamstra committed
97
  "Name of the program which compares two files."
Andreas Schwab's avatar
Andreas Schwab committed
98 99 100
  :type 'string
  :group 'emerge)
(defcustom emerge-diff3-program "diff3"
Lute Kamstra's avatar
Lute Kamstra committed
101
  "Name of the program which compares three files.
Andreas Schwab's avatar
Andreas Schwab committed
102 103 104 105
Its arguments are the ancestor file and the two variant files."
  :type 'string
  :group 'emerge)
(defcustom emerge-diff-options ""
Lute Kamstra's avatar
Lute Kamstra committed
106
  "Options to pass to `emerge-diff-program' and `emerge-diff3-program'."
Andreas Schwab's avatar
Andreas Schwab committed
107 108 109 110 111
  :type 'string
  :group 'emerge)
(defcustom emerge-match-diff-line
  (let ((x "\\([0-9]+\\)\\(\\|,\\([0-9]+\\)\\)"))
    (concat "^" x "\\([acd]\\)" x "$"))
Lute Kamstra's avatar
Lute Kamstra committed
112
  "Pattern to match lines produced by diff that describe differences.
Andreas Schwab's avatar
Andreas Schwab committed
113 114 115 116
This is as opposed to lines from the source files."
  :type 'regexp
  :group 'emerge)
(defcustom emerge-diff-ok-lines-regexp
Jim Blandy's avatar
Jim Blandy committed
117
  "^\\([0-9,]+[acd][0-9,]+$\\|[<>] \\|---\\)"
Lute Kamstra's avatar
Lute Kamstra committed
118
  "Regexp that matches normal output lines from `emerge-diff-program'.
Andreas Schwab's avatar
Andreas Schwab committed
119 120 121 122
Lines that do not match are assumed to be error messages."
  :type 'regexp
  :group 'emerge)
(defcustom emerge-diff3-ok-lines-regexp
Jim Blandy's avatar
Jim Blandy committed
123
  "^\\([1-3]:\\|====\\|  \\)"
Lute Kamstra's avatar
Lute Kamstra committed
124
  "Regexp that matches normal output lines from `emerge-diff3-program'.
Andreas Schwab's avatar
Andreas Schwab committed
125 126 127 128 129
Lines that do not match are assumed to be error messages."
  :type 'regexp
  :group 'emerge)

(defcustom emerge-rcs-ci-program "ci"
Lute Kamstra's avatar
Lute Kamstra committed
130
  "Name of the program that checks in RCS revisions."
Andreas Schwab's avatar
Andreas Schwab committed
131 132 133
  :type 'string
  :group 'emerge)
(defcustom emerge-rcs-co-program "co"
Lute Kamstra's avatar
Lute Kamstra committed
134
  "Name of the program that checks out RCS revisions."
Andreas Schwab's avatar
Andreas Schwab committed
135 136 137 138
  :type 'string
  :group 'emerge)

(defcustom emerge-process-local-variables nil
Lute Kamstra's avatar
Lute Kamstra committed
139
  "Non-nil if Emerge should process local-variables lists in merge buffers.
140
\(You can explicitly request processing the local-variables
Andreas Schwab's avatar
Andreas Schwab committed
141 142 143 144
by executing `(hack-local-variables)'.)"
  :type 'boolean
  :group 'emerge)
(defcustom emerge-execute-line-deletions nil
Lute Kamstra's avatar
Lute Kamstra committed
145
  "If non-nil: `emerge-execute-line' makes no output if an input was deleted.
146 147
It concludes that an input version has been deleted when an ancestor entry
is present, only one A or B entry is present, and an output entry is present.
Richard M. Stallman's avatar
Richard M. Stallman committed
148
If nil: In such circumstances, the A or B file that is present will be
Andreas Schwab's avatar
Andreas Schwab committed
149 150 151
copied to the designated output file."
  :type 'boolean
  :group 'emerge)
Richard M. Stallman's avatar
Richard M. Stallman committed
152

Andreas Schwab's avatar
Andreas Schwab committed
153
(defcustom emerge-before-flag "vvvvvvvvvvvvvvvvvvvv\n"
Lute Kamstra's avatar
Lute Kamstra committed
154
  "Flag placed above the highlighted block of code.  Must end with newline.
155
Must be set before Emerge is loaded, or  emerge-new-flags  must be run
Andreas Schwab's avatar
Andreas Schwab committed
156 157 158 159
after setting."
  :type 'string
  :group 'emerge)
(defcustom emerge-after-flag "^^^^^^^^^^^^^^^^^^^^\n"
Lute Kamstra's avatar
Lute Kamstra committed
160
  "Flag placed below the highlighted block of code.  Must end with newline.
161
Must be set before Emerge is loaded, or  emerge-new-flags  must be run
Andreas Schwab's avatar
Andreas Schwab committed
162 163 164
after setting."
  :type 'string
  :group 'emerge)
165

Richard M. Stallman's avatar
Richard M. Stallman committed
166 167
;; Hook variables

Andreas Schwab's avatar
Andreas Schwab committed
168
(defcustom emerge-startup-hook nil
Lute Kamstra's avatar
Lute Kamstra committed
169
  "Hook to run in the merge buffer after the merge has been set up."
Andreas Schwab's avatar
Andreas Schwab committed
170 171 172
  :type 'hook
  :group 'emerge)
(defcustom emerge-select-hook nil
Lute Kamstra's avatar
Lute Kamstra committed
173
  "Hook to run after a difference has been selected.
Andreas Schwab's avatar
Andreas Schwab committed
174 175 176 177
The variable `n' holds the (internal) number of the difference."
  :type 'hook
  :group 'emerge)
(defcustom emerge-unselect-hook nil
Lute Kamstra's avatar
Lute Kamstra committed
178
  "Hook to run after a difference has been unselected.
Andreas Schwab's avatar
Andreas Schwab committed
179 180 181
The variable `n' holds the (internal) number of the difference."
  :type 'hook
  :group 'emerge)
Richard M. Stallman's avatar
Richard M. Stallman committed
182 183 184 185

;; Variables to control the default directories of the arguments to
;; Emerge commands.

Andreas Schwab's avatar
Andreas Schwab committed
186
(defcustom emerge-default-last-directories nil
Lute Kamstra's avatar
Lute Kamstra committed
187
  "If nil, default dir for filenames in emerge is `default-directory'.
Richard M. Stallman's avatar
Richard M. Stallman committed
188
If non-nil, filenames complete in the directory of the last argument of the
Andreas Schwab's avatar
Andreas Schwab committed
189 190 191
same type to an `emerge-files...' command."
  :type 'boolean
  :group 'emerge)
Richard M. Stallman's avatar
Richard M. Stallman committed
192 193

(defvar emerge-last-dir-A nil
194
  "Last directory for the first file of an `emerge-files...' command.")
Richard M. Stallman's avatar
Richard M. Stallman committed
195
(defvar emerge-last-dir-B nil
196
  "Last directory for the second file of an `emerge-files...' command.")
Richard M. Stallman's avatar
Richard M. Stallman committed
197
(defvar emerge-last-dir-ancestor nil
198
  "Last directory for the ancestor file of an `emerge-files...' command.")
Richard M. Stallman's avatar
Richard M. Stallman committed
199
(defvar emerge-last-dir-output nil
200
  "Last directory for the output file of an `emerge-files...' command.")
Richard M. Stallman's avatar
Richard M. Stallman committed
201
(defvar emerge-last-revision-A nil
202
  "Last RCS revision used for first file of an `emerge-revisions...' command.")
Richard M. Stallman's avatar
Richard M. Stallman committed
203
(defvar emerge-last-revision-B nil
204
  "Last RCS revision used for second file of an `emerge-revisions...' command.")
Richard M. Stallman's avatar
Richard M. Stallman committed
205
(defvar emerge-last-revision-ancestor nil
206
  "Last RCS revision used for ancestor file of an `emerge-revisions...' command.")
Richard M. Stallman's avatar
Richard M. Stallman committed
207

208 209 210 211 212 213 214 215 216 217 218 219 220 221
(defvar emerge-before-flag-length)
(defvar emerge-before-flag-lines)
(defvar emerge-before-flag-match)
(defvar emerge-after-flag-length)
(defvar emerge-after-flag-lines)
(defvar emerge-after-flag-match)
(defvar emerge-diff-buffer)
(defvar emerge-diff-error-buffer)
(defvar emerge-prefix-argument)
(defvar emerge-file-out)
(defvar emerge-exit-func)
(defvar emerge-globalized-difference-list)
(defvar emerge-globalized-number-of-differences)

Jim Blandy's avatar
Jim Blandy committed
222 223 224 225 226
;; The flags used to mark differences in the buffers.

;; These function definitions need to be up here, because they are used
;; during loading.
(defun emerge-new-flags ()
227 228 229
  "Function to be called after `emerge-{before,after}-flag'.
This is called after these functions are changed to compute values that
depend on the flags."
Jim Blandy's avatar
Jim Blandy committed
230 231
  (setq emerge-before-flag-length (length emerge-before-flag))
  (setq emerge-before-flag-lines
232
	(emerge-count-matches-string emerge-before-flag "\n"))
Jim Blandy's avatar
Jim Blandy committed
233 234 235
  (setq emerge-before-flag-match (regexp-quote emerge-before-flag))
  (setq emerge-after-flag-length (length emerge-after-flag))
  (setq emerge-after-flag-lines
236
	(emerge-count-matches-string emerge-after-flag "\n"))
Jim Blandy's avatar
Jim Blandy committed
237
  (setq emerge-after-flag-match (regexp-quote emerge-after-flag)))
238 239

(defun emerge-count-matches-string (string regexp)
Jim Blandy's avatar
Jim Blandy committed
240 241 242 243 244 245 246 247 248 249 250
  "Return the number of matches in STRING for REGEXP."
  (let ((i 0)
	(count 0))
    (while (string-match regexp string i)
      (setq count (1+ count))
      (setq i (match-end 0)))
    count))

;; Calculate dependent variables
(emerge-new-flags)

Andreas Schwab's avatar
Andreas Schwab committed
251
(defcustom emerge-min-visible-lines 3
Lute Kamstra's avatar
Lute Kamstra committed
252
  "Number of lines that we want to show above and below the flags when we are
Andreas Schwab's avatar
Andreas Schwab committed
253 254 255
displaying a difference."
  :type 'integer
  :group 'emerge)
Jim Blandy's avatar
Jim Blandy committed
256

Andreas Schwab's avatar
Andreas Schwab committed
257
(defcustom emerge-temp-file-prefix
258
  (expand-file-name "emerge" temporary-file-directory)
Lute Kamstra's avatar
Lute Kamstra committed
259
  "Prefix to put on Emerge temporary file names.
260
Do not start with `~/' or `~USERNAME/'."
Andreas Schwab's avatar
Andreas Schwab committed
261 262
  :type 'string
  :group 'emerge)
Jim Blandy's avatar
Jim Blandy committed
263

Andreas Schwab's avatar
Andreas Schwab committed
264
(defcustom emerge-temp-file-mode 384	; u=rw only
Lute Kamstra's avatar
Lute Kamstra committed
265
  "Mode for Emerge temporary files."
Andreas Schwab's avatar
Andreas Schwab committed
266 267
  :type 'integer
  :group 'emerge)
Jim Blandy's avatar
Jim Blandy committed
268

Andreas Schwab's avatar
Andreas Schwab committed
269
(defcustom emerge-combine-versions-template
270
  "#ifdef NEW\n%b#else /* not NEW */\n%a#endif /* not NEW */\n"
Lute Kamstra's avatar
Lute Kamstra committed
271
  "Template for `emerge-combine-versions' to combine the two versions.
Jim Blandy's avatar
Jim Blandy committed
272 273 274
The template is inserted as a string, with the following interpolations:
	%a	the A version of the difference
	%b	the B version of the difference
275
	%%	the character `%'
Jim Blandy's avatar
Jim Blandy committed
276 277
Don't forget to end the template with a newline.
Note that this variable can be made local to a particular merge buffer by
Andreas Schwab's avatar
Andreas Schwab committed
278 279 280
giving a prefix argument to `emerge-set-combine-versions-template'."
  :type 'string
  :group 'emerge)
Jim Blandy's avatar
Jim Blandy committed
281 282 283 284 285

;; Build keymaps

(defvar emerge-basic-keymap nil
  "Keymap of Emerge commands.
286 287
Directly available in `fast' mode;
must be prefixed by \\<emerge-fast-keymap>\\[emerge-basic-keymap] in `edit' mode.")
Jim Blandy's avatar
Jim Blandy committed
288 289

(defvar emerge-fast-keymap nil
290
  "Local keymap used in Emerge `fast' mode.
Jim Blandy's avatar
Jim Blandy committed
291 292
Makes Emerge commands directly available.")

293 294 295 296 297 298 299 300 301
(defvar emerge-options-menu
  (make-sparse-keymap "Options"))

(defvar emerge-merge-menu
  (make-sparse-keymap "Merge"))

(defvar emerge-move-menu
  (make-sparse-keymap "Move"))

Andreas Schwab's avatar
Andreas Schwab committed
302
(defcustom emerge-command-prefix "\C-c\C-c"
Lute Kamstra's avatar
Lute Kamstra committed
303
  "Command prefix for Emerge commands in `edit' mode.
Andreas Schwab's avatar
Andreas Schwab committed
304 305 306
Must be set before Emerge is loaded."
  :type 'string
  :group 'emerge)
Jim Blandy's avatar
Jim Blandy committed
307 308 309 310 311 312 313 314 315 316 317 318 319

;; This function sets up the fixed keymaps.  It is executed when the first
;; Emerge is done to allow the user maximum time to set up the global keymap.
(defun emerge-setup-fixed-keymaps ()
  ;; Set up the basic keymap
  (setq emerge-basic-keymap (make-keymap))
  (suppress-keymap emerge-basic-keymap)	; this sets 0..9 to digit-argument and
					; - to negative-argument
  (define-key emerge-basic-keymap "p" 'emerge-previous-difference)
  (define-key emerge-basic-keymap "n" 'emerge-next-difference)
  (define-key emerge-basic-keymap "a" 'emerge-select-A)
  (define-key emerge-basic-keymap "b" 'emerge-select-B)
  (define-key emerge-basic-keymap "j" 'emerge-jump-to-difference)
320
  (define-key emerge-basic-keymap "." 'emerge-find-difference)
Jim Blandy's avatar
Jim Blandy committed
321
  (define-key emerge-basic-keymap "q" 'emerge-quit)
322
  (define-key emerge-basic-keymap "\C-]" 'emerge-abort)
Jim Blandy's avatar
Jim Blandy committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
  (define-key emerge-basic-keymap "f" 'emerge-fast-mode)
  (define-key emerge-basic-keymap "e" 'emerge-edit-mode)
  (define-key emerge-basic-keymap "s" nil)
  (define-key emerge-basic-keymap "sa" 'emerge-auto-advance)
  (define-key emerge-basic-keymap "ss" 'emerge-skip-prefers)
  (define-key emerge-basic-keymap "l" 'emerge-recenter)
  (define-key emerge-basic-keymap "d" nil)
  (define-key emerge-basic-keymap "da" 'emerge-default-A)
  (define-key emerge-basic-keymap "db" 'emerge-default-B)
  (define-key emerge-basic-keymap "c" nil)
  (define-key emerge-basic-keymap "ca" 'emerge-copy-as-kill-A)
  (define-key emerge-basic-keymap "cb" 'emerge-copy-as-kill-B)
  (define-key emerge-basic-keymap "i" nil)
  (define-key emerge-basic-keymap "ia" 'emerge-insert-A)
  (define-key emerge-basic-keymap "ib" 'emerge-insert-B)
  (define-key emerge-basic-keymap "m" 'emerge-mark-difference)
  (define-key emerge-basic-keymap "v" 'emerge-scroll-up)
  (define-key emerge-basic-keymap "^" 'emerge-scroll-down)
  (define-key emerge-basic-keymap "<" 'emerge-scroll-left)
  (define-key emerge-basic-keymap ">" 'emerge-scroll-right)
  (define-key emerge-basic-keymap "|" 'emerge-scroll-reset)
  (define-key emerge-basic-keymap "x" nil)
  (define-key emerge-basic-keymap "x1" 'emerge-one-line-window)
  (define-key emerge-basic-keymap "xc" 'emerge-combine-versions)
  (define-key emerge-basic-keymap "xC" 'emerge-combine-versions-register)
  (define-key emerge-basic-keymap "xf" 'emerge-file-names)
  (define-key emerge-basic-keymap "xj" 'emerge-join-differences)
  (define-key emerge-basic-keymap "xl" 'emerge-line-numbers)
  (define-key emerge-basic-keymap "xm" 'emerge-set-merge-mode)
  (define-key emerge-basic-keymap "xs" 'emerge-split-difference)
  (define-key emerge-basic-keymap "xt" 'emerge-trim-difference)
  (define-key emerge-basic-keymap "xx" 'emerge-set-combine-versions-template)
  ;; Allow emerge-basic-keymap to be referenced indirectly
  (fset 'emerge-basic-keymap emerge-basic-keymap)
  ;; Set up the fast mode keymap
  (setq emerge-fast-keymap (copy-keymap emerge-basic-keymap))
  ;; Allow prefixed commands to work in fast mode
  (define-key emerge-fast-keymap emerge-command-prefix 'emerge-basic-keymap)
  ;; Allow emerge-fast-keymap to be referenced indirectly
  (fset 'emerge-fast-keymap emerge-fast-keymap)
  ;; Suppress write-file and save-buffer
364 365
  (define-key emerge-fast-keymap [remap write-file] 'emerge-query-write-file)
  (define-key emerge-fast-keymap [remap save-buffer] 'emerge-query-save-buffer)
366 367 368

  (define-key emerge-basic-keymap [menu-bar] (make-sparse-keymap))

Glenn Morris's avatar
Glenn Morris committed
369 370
  (define-key emerge-fast-keymap [menu-bar emerge-options]
    (cons "Merge-Options" emerge-options-menu))
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
  (define-key emerge-fast-keymap [menu-bar merge]
    (cons "Merge" emerge-merge-menu))
  (define-key emerge-fast-keymap [menu-bar move]
    (cons "Move" emerge-move-menu))

  (define-key emerge-move-menu [emerge-scroll-reset]
    '("Scroll Reset" . emerge-scroll-reset))
  (define-key emerge-move-menu [emerge-scroll-right]
    '("Scroll Right" . emerge-scroll-right))
  (define-key emerge-move-menu [emerge-scroll-left]
    '("Scroll Left" . emerge-scroll-left))
  (define-key emerge-move-menu [emerge-scroll-down]
    '("Scroll Down" . emerge-scroll-down))
  (define-key emerge-move-menu [emerge-scroll-up]
    '("Scroll Up" . emerge-scroll-up))
  (define-key emerge-move-menu [emerge-recenter]
    '("Recenter" . emerge-recenter))
  (define-key emerge-move-menu [emerge-mark-difference]
    '("Mark Difference" . emerge-mark-difference))
  (define-key emerge-move-menu [emerge-jump-to-difference]
    '("Jump To Difference" . emerge-jump-to-difference))
  (define-key emerge-move-menu [emerge-find-difference]
    '("Find Difference" . emerge-find-difference))
  (define-key emerge-move-menu [emerge-previous-difference]
    '("Previous Difference" . emerge-previous-difference))
  (define-key emerge-move-menu [emerge-next-difference]
    '("Next Difference" . emerge-next-difference))


  (define-key emerge-options-menu [emerge-one-line-window]
    '("One Line Window" . emerge-one-line-window))
  (define-key emerge-options-menu [emerge-set-merge-mode]
Glenn Morris's avatar
Glenn Morris committed
403
    '("Set Merge Mode..." . emerge-set-merge-mode))
404 405 406 407 408 409 410
  (define-key emerge-options-menu [emerge-set-combine-template]
    '("Set Combine Template..." . emerge-set-combine-template))
  (define-key emerge-options-menu [emerge-default-B]
    '("Default B" . emerge-default-B))
  (define-key emerge-options-menu [emerge-default-A]
    '("Default A" . emerge-default-A))
  (define-key emerge-options-menu [emerge-skip-prefers]
Glenn Morris's avatar
Glenn Morris committed
411 412
    '(menu-item "Skip Prefers" emerge-skip-prefers
		:button (:toggle . emerge-skip-prefers)))
413
  (define-key emerge-options-menu [emerge-auto-advance]
Glenn Morris's avatar
Glenn Morris committed
414 415
    '(menu-item "Auto Advance" emerge-auto-advance
		:button (:toggle . emerge-auto-advance)))
416
  (define-key emerge-options-menu [emerge-edit-mode]
Glenn Morris's avatar
Glenn Morris committed
417
    '(menu-item "Edit Mode" emerge-edit-mode :enable (not emerge-edit-mode)))
418
  (define-key emerge-options-menu [emerge-fast-mode]
Glenn Morris's avatar
Glenn Morris committed
419
    '(menu-item "Fast Mode" emerge-fast-mode :enable (not emerge-fast-mode)))
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443

  (define-key emerge-merge-menu [emerge-abort] '("Abort" . emerge-abort))
  (define-key emerge-merge-menu [emerge-quit] '("Quit" . emerge-quit))
  (define-key emerge-merge-menu [emerge-split-difference]
    '("Split Difference" . emerge-split-difference))
  (define-key emerge-merge-menu [emerge-join-differences]
    '("Join Differences" . emerge-join-differences))
  (define-key emerge-merge-menu [emerge-trim-difference]
    '("Trim Difference" . emerge-trim-difference))
  (define-key emerge-merge-menu [emerge-combine-versions]
    '("Combine Versions" . emerge-combine-versions))
  (define-key emerge-merge-menu [emerge-copy-as-kill-B]
    '("Copy B as Kill" . emerge-copy-as-kill-B))
  (define-key emerge-merge-menu [emerge-copy-as-kill-A]
    '("Copy A as Kill" . emerge-copy-as-kill-A))
  (define-key emerge-merge-menu [emerge-insert-B]
    '("Insert B" . emerge-insert-B))
  (define-key emerge-merge-menu [emerge-insert-A]
    '("Insert A" . emerge-insert-A))
  (define-key emerge-merge-menu [emerge-select-B]
    '("Select B" . emerge-select-B))
  (define-key emerge-merge-menu [emerge-select-A]
    '("Select A" . emerge-select-A)))

Jim Blandy's avatar
Jim Blandy committed
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499

;; Variables which control each merge.  They are local to the merge buffer.

;; Mode variables
(emerge-defvar-local emerge-mode nil
  "Indicator for emerge-mode.")
(emerge-defvar-local emerge-fast-mode nil
  "Indicator for emerge-mode fast submode.")
(emerge-defvar-local emerge-edit-mode nil
  "Indicator for emerge-mode edit submode.")
(emerge-defvar-local emerge-A-buffer nil
  "The buffer in which the A variant is stored.")
(emerge-defvar-local emerge-B-buffer nil
  "The buffer in which the B variant is stored.")
(emerge-defvar-local emerge-merge-buffer nil
  "The buffer in which the merged file is manipulated.")
(emerge-defvar-local emerge-ancestor-buffer nil
  "The buffer in which the ancestor variant is stored,
or nil if there is none.")

(defconst emerge-saved-variables
  '((buffer-modified-p set-buffer-modified-p)
    buffer-read-only
    buffer-auto-save-file-name)
  "Variables and properties of a buffer which are saved, modified and restored
during a merge.")
(defconst emerge-merging-values '(nil t nil)
  "Values to be assigned to emerge-saved-variables during a merge.")

(emerge-defvar-local emerge-A-buffer-values nil
  "Remembers emerge-saved-variables for emerge-A-buffer.")
(emerge-defvar-local emerge-B-buffer-values nil
  "Remembers emerge-saved-variables for emerge-B-buffer.")

(emerge-defvar-local emerge-difference-list nil
  "Vector of differences between the variants, and markers in the buffers to
show where they are.  Each difference is represented by a vector of seven
elements.  The first two are markers to the beginning and end of the difference
section in the A buffer, the second two are markers for the B buffer, the third
two are markers for the merge buffer, and the last element is the \"state\" of
that difference in the merge buffer.
  A section of a buffer is described by two markers, one to the beginning of
the first line of the section, and one to the beginning of the first line
after the section.  (If the section is empty, both markers point to the same
point.)  If the section is part of the selected difference, then the markers
are moved into the flags, so the user can edit the section without disturbing
the markers.
  The \"states\" are:
	A		the merge buffer currently contains the A variant
	B		the merge buffer currently contains the B variant
	default-A	the merge buffer contains the A variant by default,
			but this difference hasn't been selected yet, so
			change-default commands can alter it
	default-B	the merge buffer contains the B variant by default,
			but this difference hasn't been selected yet, so
			change-default commands can alter it
500
	prefer-A	in a three-file merge, the A variant is the preferred
Jim Blandy's avatar
Jim Blandy committed
501
			choice
502
	prefer-B	in a three-file merge, the B variant is the preferred
Jim Blandy's avatar
Jim Blandy committed
503 504 505 506 507 508 509 510 511 512 513 514
			choice")
(emerge-defvar-local emerge-current-difference -1
  "The difference that is currently selected.")
(emerge-defvar-local emerge-number-of-differences nil
  "Number of differences found.")
(emerge-defvar-local emerge-edit-keymap nil
  "The local keymap for the merge buffer, with the emerge commands defined in
it.  Used to save the local keymap during fast mode, when the local keymap is
replaced by emerge-fast-keymap.")
(emerge-defvar-local emerge-old-keymap nil
  "The original local keymap for the merge buffer.")
(emerge-defvar-local emerge-auto-advance nil
515
		     "If non-nil, emerge-select-A and emerge-select-B automatically advance to
Jim Blandy's avatar
Jim Blandy committed
516 517
the next difference.")
(emerge-defvar-local emerge-skip-prefers nil
518
		     "If non-nil, differences for which there is a preference are automatically
Jim Blandy's avatar
Jim Blandy committed
519
skipped.")
520
(emerge-defvar-local emerge-quit-hook nil
Jim Blandy's avatar
Jim Blandy committed
521
  "Hooks to run in the merge buffer after the merge has been finished.
522
`emerge-prefix-argument' will hold the prefix argument of the `emerge-quit'
Jim Blandy's avatar
Jim Blandy committed
523
command.
524
This is *not* a user option, since Emerge uses it for its own processing.")
Jim Blandy's avatar
Jim Blandy committed
525
(emerge-defvar-local emerge-output-description nil
526
  "Describes output destination of emerge, for `emerge-file-names'.")
Jim Blandy's avatar
Jim Blandy committed
527 528 529 530

;;; Setup functions for two-file mode.

(defun emerge-files-internal (file-A file-B &optional startup-hooks quit-hooks
531
                              output-file)
Richard M. Stallman's avatar
Richard M. Stallman committed
532
  (if (not (file-readable-p file-A))
533
      (error "File `%s' does not exist or is not readable" file-A))
Richard M. Stallman's avatar
Richard M. Stallman committed
534
  (if (not (file-readable-p file-B))
535
      (error "File `%s' does not exist or is not readable" file-B))
Jim Blandy's avatar
Jim Blandy committed
536 537
  (let ((buffer-A (find-file-noselect file-A))
	(buffer-B (find-file-noselect file-B)))
Richard M. Stallman's avatar
Richard M. Stallman committed
538 539 540 541 542
    ;; Record the directories of the files
    (setq emerge-last-dir-A (file-name-directory file-A))
    (setq emerge-last-dir-B (file-name-directory file-B))
    (if output-file
	(setq emerge-last-dir-output (file-name-directory output-file)))
Jim Blandy's avatar
Jim Blandy committed
543
    ;; Make sure the entire files are seen, and they reflect what is on disk
Glenn Morris's avatar
Glenn Morris committed
544
    (with-current-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
545 546
     buffer-A
     (widen)
547 548 549 550
     (let ((temp (file-local-copy file-A)))
       (if temp
	   (setq file-A temp
		 startup-hooks
551
		 (cons `(lambda () (delete-file ,file-A))
552
		       startup-hooks))
553 554
           ;; Verify that the file matches the buffer
           (emerge-verify-file-buffer))))
Glenn Morris's avatar
Glenn Morris committed
555
    (with-current-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
556 557
     buffer-B
     (widen)
558 559 560 561
     (let ((temp (file-local-copy file-B)))
       (if temp
	   (setq file-B temp
		 startup-hooks
562
		 (cons `(lambda () (delete-file ,file-B))
563
		       startup-hooks))
564 565
           ;; Verify that the file matches the buffer
           (emerge-verify-file-buffer))))
Jim Blandy's avatar
Jim Blandy committed
566 567 568 569 570 571 572 573 574 575 576 577
    (emerge-setup buffer-A file-A buffer-B file-B startup-hooks quit-hooks
		  output-file)))

;; Start up Emerge on two files
(defun emerge-setup (buffer-A file-A buffer-B file-B startup-hooks quit-hooks
			      output-file)
  (setq file-A (expand-file-name file-A))
  (setq file-B (expand-file-name file-B))
  (setq output-file (and output-file (expand-file-name output-file)))
  (let* ((merge-buffer-name (emerge-unique-buffer-name "*merge" "*"))
	 ;; create the merge buffer from buffer A, so it inherits buffer A's
	 ;; default directory, etc.
Glenn Morris's avatar
Glenn Morris committed
578
	 (merge-buffer (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
579 580
			buffer-A
			(get-buffer-create merge-buffer-name))))
Glenn Morris's avatar
Glenn Morris committed
581
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
582 583 584 585 586 587 588 589 590 591 592 593 594
     merge-buffer
     (emerge-copy-modes buffer-A)
     (setq buffer-read-only nil)
     (auto-save-mode 1)
     (setq emerge-mode t)
     (setq emerge-A-buffer buffer-A)
     (setq emerge-B-buffer buffer-B)
     (setq emerge-ancestor-buffer nil)
     (setq emerge-merge-buffer merge-buffer)
     (setq emerge-output-description
	   (if output-file
	       (concat "Output to file: " output-file)
	     (concat "Output to buffer: " (buffer-name merge-buffer))))
595
     (save-excursion (insert-buffer-substring emerge-A-buffer))
Jim Blandy's avatar
Jim Blandy committed
596 597 598 599
     (emerge-set-keys)
     (setq emerge-difference-list (emerge-make-diff-list file-A file-B))
     (setq emerge-number-of-differences (length emerge-difference-list))
     (setq emerge-current-difference -1)
600
     (setq emerge-quit-hook quit-hooks)
Richard M. Stallman's avatar
Richard M. Stallman committed
601 602
     (emerge-remember-buffer-characteristics)
     (emerge-handle-local-variables))
Jim Blandy's avatar
Jim Blandy committed
603
    (emerge-setup-windows buffer-A buffer-B merge-buffer t)
Glenn Morris's avatar
Glenn Morris committed
604
    (with-current-buffer merge-buffer
605
			   (run-hooks 'startup-hooks 'emerge-startup-hook)
Jim Blandy's avatar
Jim Blandy committed
606 607 608 609 610
			   (setq buffer-read-only t))))

;; Generate the Emerge difference list between two files
(defun emerge-make-diff-list (file-A file-B)
  (setq emerge-diff-buffer (get-buffer-create "*emerge-diff*"))
Glenn Morris's avatar
Glenn Morris committed
611
  (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
612 613 614 615
   emerge-diff-buffer
   (erase-buffer)
   (shell-command
    (format "%s %s %s %s"
Richard M. Stallman's avatar
Richard M. Stallman committed
616 617 618
	    emerge-diff-program emerge-diff-options
	    (emerge-protect-metachars file-A)
	    (emerge-protect-metachars file-B))
Jim Blandy's avatar
Jim Blandy committed
619
    t))
620
  (emerge-prepare-error-list emerge-diff-ok-lines-regexp)
Jim Blandy's avatar
Jim Blandy committed
621 622 623 624 625 626
  (emerge-convert-diffs-to-markers
   emerge-A-buffer emerge-B-buffer emerge-merge-buffer
   (emerge-extract-diffs emerge-diff-buffer)))

(defun emerge-extract-diffs (diff-buffer)
  (let (list)
Glenn Morris's avatar
Glenn Morris committed
627
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
628 629 630
     diff-buffer
     (goto-char (point-min))
     (while (re-search-forward emerge-match-diff-line nil t)
631 632
       (let* ((a-begin (string-to-number (buffer-substring (match-beginning 1)
                                                           (match-end 1))))
Jim Blandy's avatar
Jim Blandy committed
633 634 635
	      (a-end  (let ((b (match-beginning 3))
			    (e (match-end 3)))
			(if b
636
			    (string-to-number (buffer-substring b e))
Jim Blandy's avatar
Jim Blandy committed
637 638
			  a-begin)))
	      (diff-type (buffer-substring (match-beginning 4) (match-end 4)))
639 640
	      (b-begin (string-to-number (buffer-substring (match-beginning 5)
                                                           (match-end 5))))
Jim Blandy's avatar
Jim Blandy committed
641 642 643
	      (b-end (let ((b (match-beginning 7))
			   (e (match-end 7)))
		       (if b
644
			   (string-to-number (buffer-substring b e))
Jim Blandy's avatar
Jim Blandy committed
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
			 b-begin))))
	 ;; fix the beginning and end numbers, because diff is somewhat
	 ;; strange about how it numbers lines
	 (if (string-equal diff-type "a")
	     (progn
	       (setq b-end (1+ b-end))
	       (setq a-begin (1+ a-begin))
	       (setq a-end a-begin))
	   (if (string-equal diff-type "d")
	       (progn
		 (setq a-end (1+ a-end))
		 (setq b-begin (1+ b-begin))
		 (setq b-end b-begin))
	     ;; (string-equal diff-type "c")
	     (progn
	       (setq a-end (1+ a-end))
	       (setq b-end (1+ b-end)))))
	 (setq list (cons (vector a-begin a-end
				  b-begin b-end
				  'default-A)
			  list)))))
    (nreverse list)))

;; Set up buffer of diff/diff3 error messages.
(defun emerge-prepare-error-list (ok-regexp)
  (setq emerge-diff-error-buffer (get-buffer-create "*emerge-diff-errors*"))
Glenn Morris's avatar
Glenn Morris committed
671
  (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
672 673
   emerge-diff-error-buffer
   (erase-buffer)
674
   (save-excursion (insert-buffer-substring emerge-diff-buffer))
Jim Blandy's avatar
Jim Blandy committed
675 676 677 678 679
   (delete-matching-lines ok-regexp)))

;;; Top-level and setup functions for three-file mode.

(defun emerge-files-with-ancestor-internal (file-A file-B file-ancestor
680 681
					    &optional startup-hooks quit-hooks
					    output-file)
Richard M. Stallman's avatar
Richard M. Stallman committed
682
  (if (not (file-readable-p file-A))
683
      (error "File `%s' does not exist or is not readable" file-A))
Richard M. Stallman's avatar
Richard M. Stallman committed
684
  (if (not (file-readable-p file-B))
685
      (error "File `%s' does not exist or is not readable" file-B))
Richard M. Stallman's avatar
Richard M. Stallman committed
686
  (if (not (file-readable-p file-ancestor))
687
      (error "File `%s' does not exist or is not readable" file-ancestor))
Jim Blandy's avatar
Jim Blandy committed
688 689 690
  (let ((buffer-A (find-file-noselect file-A))
	(buffer-B (find-file-noselect file-B))
	(buffer-ancestor (find-file-noselect file-ancestor)))
Richard M. Stallman's avatar
Richard M. Stallman committed
691 692 693 694 695 696
    ;; Record the directories of the files
    (setq emerge-last-dir-A (file-name-directory file-A))
    (setq emerge-last-dir-B (file-name-directory file-B))
    (setq emerge-last-dir-ancestor (file-name-directory file-ancestor))
    (if output-file
	(setq emerge-last-dir-output (file-name-directory output-file)))
Jim Blandy's avatar
Jim Blandy committed
697
    ;; Make sure the entire files are seen, and they reflect what is on disk
Glenn Morris's avatar
Glenn Morris committed
698
    (with-current-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
699 700
     buffer-A
     (widen)
701 702 703 704
     (let ((temp (file-local-copy file-A)))
       (if temp
	   (setq file-A temp
		 startup-hooks
705
		 (cons `(lambda () (delete-file ,file-A))
706
		       startup-hooks))
707 708
           ;; Verify that the file matches the buffer
           (emerge-verify-file-buffer))))
Glenn Morris's avatar
Glenn Morris committed
709
    (with-current-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
710 711
     buffer-B
     (widen)
712 713 714 715
     (let ((temp (file-local-copy file-B)))
       (if temp
	   (setq file-B temp
		 startup-hooks
716
		 (cons `(lambda () (delete-file ,file-B))
717
		       startup-hooks))
718 719
           ;; Verify that the file matches the buffer
           (emerge-verify-file-buffer))))
Glenn Morris's avatar
Glenn Morris committed
720
    (with-current-buffer
Richard M. Stallman's avatar
Richard M. Stallman committed
721 722
     buffer-ancestor
     (widen)
723 724 725 726
     (let ((temp (file-local-copy file-ancestor)))
       (if temp
	   (setq file-ancestor temp
		 startup-hooks
727
		 (cons `(lambda () (delete-file ,file-ancestor))
728
		       startup-hooks))
729 730
           ;; Verify that the file matches the buffer
           (emerge-verify-file-buffer))))
Jim Blandy's avatar
Jim Blandy committed
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
    (emerge-setup-with-ancestor buffer-A file-A buffer-B file-B
				buffer-ancestor file-ancestor
				startup-hooks quit-hooks output-file)))

;; Start up Emerge on two files with an ancestor
(defun emerge-setup-with-ancestor (buffer-A file-A buffer-B file-B
					    buffer-ancestor file-ancestor
					    &optional startup-hooks quit-hooks
					    output-file)
  (setq file-A (expand-file-name file-A))
  (setq file-B (expand-file-name file-B))
  (setq file-ancestor (expand-file-name file-ancestor))
  (setq output-file (and output-file (expand-file-name output-file)))
  (let* ((merge-buffer-name (emerge-unique-buffer-name "*merge" "*"))
	 ;; create the merge buffer from buffer A, so it inherits buffer A's
	 ;; default directory, etc.
Glenn Morris's avatar
Glenn Morris committed
747
	 (merge-buffer (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
748 749
			buffer-A
			(get-buffer-create merge-buffer-name))))
Glenn Morris's avatar
Glenn Morris committed
750
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
751 752 753 754 755 756 757 758 759 760 761 762 763
     merge-buffer
     (emerge-copy-modes buffer-A)
     (setq buffer-read-only nil)
     (auto-save-mode 1)
     (setq emerge-mode t)
     (setq emerge-A-buffer buffer-A)
     (setq emerge-B-buffer buffer-B)
     (setq emerge-ancestor-buffer buffer-ancestor)
     (setq emerge-merge-buffer merge-buffer)
     (setq emerge-output-description
	   (if output-file
	       (concat "Output to file: " output-file)
	     (concat "Output to buffer: " (buffer-name merge-buffer))))
764
     (save-excursion (insert-buffer-substring emerge-A-buffer))
Jim Blandy's avatar
Jim Blandy committed
765 766 767 768 769
     (emerge-set-keys)
     (setq emerge-difference-list
	   (emerge-make-diff3-list file-A file-B file-ancestor))
     (setq emerge-number-of-differences (length emerge-difference-list))
     (setq emerge-current-difference -1)
770
     (setq emerge-quit-hook quit-hooks)
Jim Blandy's avatar
Jim Blandy committed
771
     (emerge-remember-buffer-characteristics)
Richard M. Stallman's avatar
Richard M. Stallman committed
772 773
     (emerge-select-prefer-Bs)
     (emerge-handle-local-variables))
Jim Blandy's avatar
Jim Blandy committed
774
    (emerge-setup-windows buffer-A buffer-B merge-buffer t)
Glenn Morris's avatar
Glenn Morris committed
775
    (with-current-buffer merge-buffer
776
			   (run-hooks 'startup-hooks 'emerge-startup-hook)
Jim Blandy's avatar
Jim Blandy committed
777 778 779 780 781
			   (setq buffer-read-only t))))

;; Generate the Emerge difference list between two files with an ancestor
(defun emerge-make-diff3-list (file-A file-B file-ancestor)
  (setq emerge-diff-buffer (get-buffer-create "*emerge-diff*"))
Glenn Morris's avatar
Glenn Morris committed
782
  (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
783 784 785 786 787
   emerge-diff-buffer
   (erase-buffer)
   (shell-command
    (format "%s %s %s %s %s"
	    emerge-diff3-program emerge-diff-options
Richard M. Stallman's avatar
Richard M. Stallman committed
788
	    (emerge-protect-metachars file-A)
789
	    (emerge-protect-metachars file-ancestor)
Richard M. Stallman's avatar
Richard M. Stallman committed
790
	    (emerge-protect-metachars file-B))
Jim Blandy's avatar
Jim Blandy committed
791
    t))
792
  (emerge-prepare-error-list emerge-diff3-ok-lines-regexp)
Jim Blandy's avatar
Jim Blandy committed
793 794 795 796 797 798
  (emerge-convert-diffs-to-markers
   emerge-A-buffer emerge-B-buffer emerge-merge-buffer
   (emerge-extract-diffs3 emerge-diff-buffer)))

(defun emerge-extract-diffs3 (diff-buffer)
  (let (list)
Glenn Morris's avatar
Glenn Morris committed
799
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
800 801 802 803 804 805
     diff-buffer
     (while (re-search-forward "^====\\(.?\\)$" nil t)
       ;; leave point after matched line
       (beginning-of-line 2)
       (let ((agreement (buffer-substring (match-beginning 1) (match-end 1))))
	 ;; if the A and B files are the same, ignore the difference
806
	 (if (not (string-equal agreement "2"))
Jim Blandy's avatar
Jim Blandy committed
807
	     (setq list
808
		   (cons
809
		    (let (group-1 group-3 pos)
810
		      (setq pos (point))
811
		      (setq group-1 (emerge-get-diff3-group "1"))
812 813
		      (goto-char pos)
		      (setq group-3 (emerge-get-diff3-group "3"))
814
		      (vector (car group-1) (car (cdr group-1))
Jim Blandy's avatar
Jim Blandy committed
815
			      (car group-3) (car (cdr group-3))
816
			      (cond ((string-equal agreement "1") 'prefer-A)
Jim Blandy's avatar
Jim Blandy committed
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
				    ((string-equal agreement "3") 'prefer-B)
				    (t 'default-A))))
		    list))))))
    (nreverse list)))

(defun emerge-get-diff3-group (file)
  ;; This save-excursion allows emerge-get-diff3-group to be called for the
  ;; various groups of lines (1, 2, 3) in any order, and for the lines to
  ;; appear in any order.  The reason this is necessary is that Gnu diff3
  ;; can produce the groups in the order 1, 2, 3 or 1, 3, 2.
  (save-excursion
    (re-search-forward
     (concat "^" file ":\\([0-9]+\\)\\(,\\([0-9]+\\)\\)?\\([ac]\\)$"))
    (beginning-of-line 2)
    ;; treatment depends on whether it is an "a" group or a "c" group
    (if (string-equal (buffer-substring (match-beginning 4) (match-end 4)) "c")
	;; it is a "c" group
	(if (match-beginning 2)
	    ;; it has two numbers
836
	    (list (string-to-number
Jim Blandy's avatar
Jim Blandy committed
837
		   (buffer-substring (match-beginning 1) (match-end 1)))
838
		  (1+ (string-to-number
Jim Blandy's avatar
Jim Blandy committed
839 840
		       (buffer-substring (match-beginning 3) (match-end 3)))))
	  ;; it has one number
841
	  (let ((x (string-to-number
Jim Blandy's avatar
Jim Blandy committed
842 843 844
		    (buffer-substring (match-beginning 1) (match-end 1)))))
	    (list x (1+ x))))
      ;; it is an "a" group
845
      (let ((x (1+ (string-to-number
Jim Blandy's avatar
Jim Blandy committed
846 847 848 849 850
		    (buffer-substring (match-beginning 1) (match-end 1))))))
	(list x x)))))

;;; Functions to start Emerge on files

851
;;;###autoload
Jim Blandy's avatar
Jim Blandy committed
852 853 854 855 856 857
(defun emerge-files (arg file-A file-B file-out &optional startup-hooks
		     quit-hooks)
  "Run Emerge on two files."
  (interactive
   (let (f)
     (list current-prefix-arg
Richard M. Stallman's avatar
Richard M. Stallman committed
858
	   (setq f (emerge-read-file-name "File A to merge" emerge-last-dir-A
859 860
					  nil nil t))
	   (emerge-read-file-name "File B to merge" emerge-last-dir-B nil f t)
Jim Blandy's avatar
Jim Blandy committed
861
	   (and current-prefix-arg
Richard M. Stallman's avatar
Richard M. Stallman committed
862
		(emerge-read-file-name "Output file" emerge-last-dir-output
863
				       f f nil)))))
864
  (if file-out
865
      (add-hook 'quit-hooks `(lambda () (emerge-files-exit ,file-out))))
Jim Blandy's avatar
Jim Blandy committed
866 867
  (emerge-files-internal
   file-A file-B startup-hooks
868
   quit-hooks
Jim Blandy's avatar
Jim Blandy committed
869 870
   file-out))

871
;;;###autoload
Jim Blandy's avatar
Jim Blandy committed
872 873 874 875 876 877
(defun emerge-files-with-ancestor (arg file-A file-B file-ancestor file-out
				   &optional startup-hooks quit-hooks)
  "Run Emerge on two files, giving another file as the ancestor."
  (interactive
   (let (f)
     (list current-prefix-arg
Richard M. Stallman's avatar
Richard M. Stallman committed
878
	   (setq f (emerge-read-file-name "File A to merge" emerge-last-dir-A
879 880
					  nil nil t))
	   (emerge-read-file-name "File B to merge" emerge-last-dir-B nil f t)
Richard M. Stallman's avatar
Richard M. Stallman committed
881
	   (emerge-read-file-name "Ancestor file" emerge-last-dir-ancestor
882
				  nil f t)
Jim Blandy's avatar
Jim Blandy committed
883
	   (and current-prefix-arg
Richard M. Stallman's avatar
Richard M. Stallman committed
884
		(emerge-read-file-name "Output file" emerge-last-dir-output
885
				       f f nil)))))
886
  (if file-out
887
      (add-hook 'quit-hooks `(lambda () (emerge-files-exit ,file-out))))
Jim Blandy's avatar
Jim Blandy committed
888 889
  (emerge-files-with-ancestor-internal
   file-A file-B file-ancestor startup-hooks
890
   quit-hooks
Jim Blandy's avatar
Jim Blandy committed
891 892 893 894 895 896 897 898 899 900
   file-out))

;; Write the merge buffer out in place of the file the A buffer is visiting.
(defun emerge-files-exit (file-out)
  ;; if merge was successful was given, save to disk
  (if (not emerge-prefix-argument)
      (emerge-write-and-delete file-out)))

;;; Functions to start Emerge on buffers

901
;;;###autoload
Jim Blandy's avatar
Jim Blandy committed
902 903 904 905 906
(defun emerge-buffers (buffer-A buffer-B &optional startup-hooks quit-hooks)
  "Run Emerge on two buffers."
  (interactive "bBuffer A to merge: \nbBuffer B to merge: ")
  (let ((emerge-file-A (emerge-make-temp-file "A"))
	(emerge-file-B (emerge-make-temp-file "B")))
Glenn Morris's avatar
Glenn Morris committed
907
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
908 909
     buffer-A
     (write-region (point-min) (point-max) emerge-file-A nil 'no-message))
Glenn Morris's avatar
Glenn Morris committed
910
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
911 912 913 914
     buffer-B
     (write-region (point-min) (point-max) emerge-file-B nil 'no-message))
    (emerge-setup (get-buffer buffer-A) emerge-file-A
		  (get-buffer buffer-B) emerge-file-B
915 916 917
		  (cons `(lambda ()
                          (delete-file ,emerge-file-A)
                          (delete-file ,emerge-file-B))
Jim Blandy's avatar
Jim Blandy committed
918 919 920 921
			startup-hooks)
		  quit-hooks
		  nil)))

922
;;;###autoload
Jim Blandy's avatar
Jim Blandy committed
923
(defun emerge-buffers-with-ancestor (buffer-A buffer-B buffer-ancestor
924 925
                                     &optional startup-hooks
                                     quit-hooks)
Jim Blandy's avatar
Jim Blandy committed
926 927 928 929 930 931
  "Run Emerge on two buffers, giving another buffer as the ancestor."
  (interactive
   "bBuffer A to merge: \nbBuffer B to merge: \nbAncestor buffer: ")
  (let ((emerge-file-A (emerge-make-temp-file "A"))
	(emerge-file-B (emerge-make-temp-file "B"))
	(emerge-file-ancestor (emerge-make-temp-file "anc")))
Glenn Morris's avatar
Glenn Morris committed
932
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
933 934
     buffer-A
     (write-region (point-min) (point-max) emerge-file-A nil 'no-message))
Glenn Morris's avatar
Glenn Morris committed
935
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
936 937
     buffer-B
     (write-region (point-min) (point-max) emerge-file-B nil 'no-message))
Glenn Morris's avatar
Glenn Morris committed
938
    (with-current-buffer
Jim Blandy's avatar
Jim Blandy committed
939 940 941 942 943 944 945
     buffer-ancestor
     (write-region (point-min) (point-max) emerge-file-ancestor nil
		   'no-message))
    (emerge-setup-with-ancestor (get-buffer buffer-A) emerge-file-A
				(get-buffer buffer-B) emerge-file-B
				(get-buffer buffer-ancestor)
				emerge-file-ancestor
946 947 948 949 950
				(cons `(lambda ()
                                        (delete-file ,emerge-file-A)
                                        (delete-file ,emerge-file-B)
                                        (delete-file
                                         ,emerge-file-ancestor))
Jim Blandy's avatar
Jim Blandy committed
951 952 953 954 955 956
				      startup-hooks)
				quit-hooks
				nil)))

;;; Functions to start Emerge from the command line

957
;;;###autoload
Jim Blandy's avatar
Jim Blandy committed
958 959 960 961 962 963 964
(defun emerge-files-command ()
  (let ((file-a (nth 0 command-line-args-left))
	(file-b (nth 1 command-line-args-left))
	(file-out (nth 2 command-line-args-left)))
    (setq command-line-args-left (nthcdr 3 command-line-args-left))
    (emerge-files-internal
     file-a file-b nil
965
     (list `(lambda () (emerge-command-exit ,file-out))))))
Jim Blandy's avatar
Jim Blandy committed
966

967
;;;###autoload
Jim Blandy's avatar
Jim Blandy committed
968 969 970 971 972 973 974 975 976 977 978
(defun emerge-files-with-ancestor-command ()
  (let (file-a file-b file-anc file-out)
    ;; check for a -a flag, for filemerge compatibility
    (if (string= (car command-line-args-left) "-a")
	;; arguments are "-a ancestor file-a file-b file-out"
	(progn
	  (setq file-a (nth 2 command-line-args-left))
	  (setq file-b (nth 3 command-line-args-left))
	  (setq file-anc (nth 1 command-line-args-left))
	  (setq file-out (nth 4 command-line-args-left))
	  (setq command-line-args-left (nthcdr 5 command-line-args-left)))
979 980 981 982 983 984
        ;; arguments are "file-a file-b ancestor file-out"
        (setq file-a (nth 0 command-line-args-left))
        (setq file-b (nth 1 command-line-args-left))
        (setq file-anc (nth 2 command-line-args-left))
        (setq file-out (nth 3 command-line-args-left))
        (setq command-line-args-left (nthcdr 4 command-line-args-left)))
Jim Blandy's avatar
Jim Blandy committed
985 986
    (emerge-files-with-ancestor-internal
     file-a file-b file-anc nil
987
     (list `(lambda () (emerge-command-exit ,file-out))))))
988

Jim Blandy's avatar
Jim Blandy committed
989 990 991 992 993 994
(defun emerge-command-exit (file-out)
  (emerge-write-and-delete file-out)
  (kill-emacs (if emerge-prefix-argument 1 0)))

;;; Functions to start Emerge via remote request

995
;;;###autoload
Jim Blandy's avatar
Jim Blandy committed
996 997 998 999
(defun emerge-files-remote (file-a file-b file-out)
  (setq emerge-file-out file-out)
  (emerge-files-internal
   file-a file-b nil
1000
   (list `(lambda () (emerge-remote-exit ,file-out ',emerge-exit-func)))
Jim Blandy's avatar
Jim Blandy committed
1001 1002 1003
   file-out)
  (throw 'client-wait nil))

1004
;;;###autoload