~ubuntu-branches/ubuntu/intrepid/mit-scheme/intrepid-updates

« back to all changes in this revision

Viewing changes to src/edwin/vc.scm

  • Committer: Bazaar Package Importer
  • Author(s): Chris Hanson
  • Date: 2002-03-14 17:04:07 UTC
  • Revision ID: james.westby@ubuntu.com-20020314170407-m5lg1d6bdsl9lv0s
Tags: upstream-7.7.0
ImportĀ upstreamĀ versionĀ 7.7.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
;;; -*-Scheme-*-
 
2
;;;
 
3
;;; $Id: vc.scm,v 1.79 2001/06/07 17:48:19 cph Exp $
 
4
;;;
 
5
;;; Copyright (c) 1994-2001 Massachusetts Institute of Technology
 
6
;;;
 
7
;;; This program is free software; you can redistribute it and/or
 
8
;;; modify it under the terms of the GNU General Public License as
 
9
;;; published by the Free Software Foundation; either version 2 of the
 
10
;;; License, or (at your option) any later version.
 
11
;;;
 
12
;;; This program is distributed in the hope that it will be useful,
 
13
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
;;; General Public License for more details.
 
16
;;;
 
17
;;; You should have received a copy of the GNU General Public License
 
18
;;; along with this program; if not, write to the Free Software
 
19
;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
20
;;; 02111-1307, USA.
 
21
 
 
22
;;;; Version Control
 
23
 
 
24
;;; Adapted from "vc.el" in Emacs 19.22.
 
25
;;; Updated March 2000 from "vc.el" in Emacs 20.6.
 
26
 
 
27
(declare (usual-integrations))
 
28
 
 
29
;;;; Editor Variables
 
30
 
 
31
(define-variable vc-make-backup-files
 
32
  "If true, backups of registered files are made as with other files.
 
33
If false (the default), files covered by version control don't get backups."
 
34
  #f
 
35
  boolean?)
 
36
 
 
37
(define-variable-per-buffer vc-mode-line-status
 
38
  "A mode line string showing the version control status of the buffer.
 
39
Bound to #F if the buffer is not under version control."
 
40
  #f
 
41
  string-or-false?)
 
42
(let ((variable (ref-variable-object vc-mode-line-status)))
 
43
  ;;(variable-permanent-local! variable)
 
44
  (set-variable! minor-mode-alist
 
45
                 (cons (list variable variable)
 
46
                       (ref-variable minor-mode-alist))))
 
47
 
 
48
(define-variable vc-suppress-confirm
 
49
  "If true, treat user as expert; suppress yes-no prompts on some things."
 
50
  #f
 
51
  boolean?)
 
52
 
 
53
(define-variable vc-keep-workfiles
 
54
  "If true, don't delete working files after registering changes.
 
55
If the back-end is CVS, workfiles are always kept, regardless of the
 
56
value of this flag."
 
57
  #t
 
58
  boolean?)
 
59
 
 
60
(define-variable vc-delete-logbuf-window
 
61
  "If true, delete the *VC-log* buffer and window after each logical action.
 
62
If false, bury that buffer instead.
 
63
This is most useful if you have multiple windows on a frame and would like to
 
64
preserve the setting."
 
65
  #t
 
66
  boolean?)
 
67
 
 
68
(define-variable vc-initial-comment
 
69
  "Prompt for initial comment when a file is registered."
 
70
  #f
 
71
  boolean?)
 
72
 
 
73
(define-variable vc-default-init-version
 
74
  "A string used as the default version number when a new file is registered.
 
75
This can be overriden by giving a prefix argument to \\[vc-register]."
 
76
  "1.1"
 
77
  string?)
 
78
 
 
79
(define-variable vc-command-messages
 
80
  "If true, display run messages from back-end commands."
 
81
  #f
 
82
  boolean?)
 
83
 
 
84
(define-variable diff-switches
 
85
  "A list of strings specifying switches to be be passed to diff."
 
86
  '("-c")
 
87
  list-of-strings?)
 
88
 
 
89
(define-variable vc-checkin-hooks
 
90
  "An event distributor that is invoked after a checkin is done."
 
91
  (make-event-distributor))
 
92
 
 
93
(define-variable vc-checkout-carefully
 
94
  "True means be extra-careful in checkout.
 
95
Verify that the file really is not locked
 
96
and that its contents match what the master file says."
 
97
  ;; Default is to be extra careful for super-user.
 
98
  (lambda () (= (unix/current-uid) 0))
 
99
  (lambda (object)
 
100
    (or (boolean? object)
 
101
        (and (procedure? object)
 
102
             (procedure-arity-valid? object 0)))))
 
103
 
 
104
(define-variable vc-log-mode-hook
 
105
  "An event distributor that is invoked when entering VC-log mode."
 
106
  (make-event-distributor))
 
107
 
 
108
(define-variable vc-follow-symlinks
 
109
  "Indicates what to do if you visit a symbolic link to a file
 
110
that is under version control.  Editing such a file through the
 
111
link bypasses the version control system, which is dangerous and
 
112
probably not what you want.  
 
113
  If this variable is #t, VC follows the link and visits the real file,
 
114
telling you about it in the echo area.  If it is `ask', VC asks for
 
115
confirmation whether it should follow the link.  If #f, the link is
 
116
visited and a warning displayed."
 
117
  'ASK
 
118
  (lambda (object) (or (boolean? object) (eq? 'ASK object))))
 
119
 
 
120
(define-variable vc-display-status
 
121
  "If true, display revision number and lock status in modeline.
 
122
Otherwise, not displayed."
 
123
  #t
 
124
  boolean?)
 
125
 
 
126
(define-variable vc-rcs-preserve-mod-times
 
127
  "If true, files checked out from RCS use checkin time for mod time.
 
128
Otherwise, the mod time of the file is the checkout time."
 
129
  #t
 
130
  boolean?)
 
131
 
 
132
;;;; VC-TYPE datatype
 
133
 
 
134
(define-structure (vc-type (constructor %make-vc-type
 
135
                                        (name display-name header-keyword))
 
136
                           safe-accessors)
 
137
  (name #f read-only #t)                ;a symbol
 
138
  (display-name #f read-only #t)        ;a string
 
139
  (header-keyword #f read-only #t)      ;a string
 
140
  (operations (make-1d-table) read-only #t)
 
141
  (properties (make-1d-table) read-only #t))
 
142
 
 
143
(define (vc-type-get type key default)
 
144
  (1d-table/get (vc-type-properties type) key default))
 
145
 
 
146
(define (vc-type-put! type key value)
 
147
  (1d-table/put! (vc-type-properties type) key value))
 
148
 
 
149
(define (vc-type-remove! type key)
 
150
  (1d-table/remove! (vc-type-properties type) key))
 
151
 
 
152
(define (make-vc-type name display-name header-keyword)
 
153
  (let ((type (%make-vc-type name display-name header-keyword)))
 
154
    (let loop ((types vc-types))
 
155
      (if (pair? types)
 
156
          (if (eq? name (vc-type-name (car types)))
 
157
              (set-car! types type)
 
158
              (loop (cdr types)))
 
159
          (set! vc-types (cons type vc-types))))
 
160
    type))
 
161
 
 
162
(define vc-types '())
 
163
 
 
164
(define (define-vc-type-operation name type procedure)
 
165
  (1d-table/put! (vc-type-operations type) name procedure))
 
166
 
 
167
(define (vc-type-operation type name)
 
168
  (or (1d-table/get (vc-type-operations type) name #f)
 
169
      (error:bad-range-argument name 'VC-TYPE-OPERATION)))
 
170
 
 
171
(define (vc-call name master . arguments)
 
172
  (apply (vc-type-operation (vc-master-type master) name) master arguments))
 
173
 
 
174
;;;; VC-MASTER datatype
 
175
 
 
176
(define-structure (vc-master (constructor make-vc-master
 
177
                                          (type pathname workfile))
 
178
                             safe-accessors)
 
179
  (type #f read-only #t)                ;a VC-TYPE object
 
180
  (pathname #f read-only #t)            ;a PATHNAME object
 
181
  (workfile #f read-only #t)            ;a PATHNAME object
 
182
  (properties (make-1d-table) read-only #t))
 
183
 
 
184
(define (vc-master-get master key default)
 
185
  (1d-table/get (vc-master-properties master) key default))
 
186
 
 
187
(define (vc-master-put! master key value)
 
188
  (1d-table/put! (vc-master-properties master) key value))
 
189
 
 
190
(define (vc-master-remove! master key)
 
191
  (1d-table/remove! (vc-master-properties master) key))
 
192
 
 
193
(define (read-cached-value-1 master key pathname read-value)
 
194
  (let loop ()
 
195
    (let ((v.t (vc-master-get master key #f))
 
196
          (time (file-modification-time pathname)))
 
197
      (if (and v.t (eqv? time (cdr v.t)))
 
198
          (car v.t)
 
199
          (begin
 
200
            (vc-master-put! master key (cons (read-value time) time))
 
201
            (loop))))))
 
202
#|
 
203
(define (cache-value-1! master key pathname read-value)
 
204
  (let ((time (file-modification-time pathname)))
 
205
    (let ((value (read-value)))
 
206
      (vc-master-put! master key (cons value time))
 
207
      value)))
 
208
|#
 
209
(define (read-cached-value-2 master key p1 p2 read-value)
 
210
  (let loop ()
 
211
    (let ((vtt (vc-master-get master key #f))
 
212
          (t1 (file-modification-time p1))
 
213
          (t2 (file-modification-time p2)))
 
214
      (if (and vtt
 
215
               (eqv? t1 (vector-ref vtt 1))
 
216
               (eqv? t2 (vector-ref vtt 2)))
 
217
          (vector-ref vtt 0)
 
218
          (begin
 
219
            (vc-master-put! master key (vector (read-value t1 t2) t1 t2))
 
220
            (loop))))))
 
221
 
 
222
(define (cache-value-2! master key p1 p2 read-value)
 
223
  (let ((t1 (file-modification-time p1))
 
224
        (t2 (file-modification-time p2)))
 
225
    (let ((value (read-value t1 t2)))
 
226
      (vc-master-put! master key (vector value t1 t2))
 
227
      value)))
 
228
 
 
229
;;;; Editor Hooks
 
230
 
 
231
(set-variable! find-file-hooks
 
232
               (append! (ref-variable find-file-hooks)
 
233
                        (list (lambda (buffer) (vc-hook:find-file buffer)))))
 
234
 
 
235
(define (vc-hook:find-file buffer)
 
236
  (cond ((buffer-vc-master buffer #f)
 
237
         => (lambda (master)
 
238
              (vc-mode-line master buffer)
 
239
              (if (not (ref-variable vc-make-backup-files buffer))
 
240
                  (local-set-variable! make-backup-files #f buffer))
 
241
              buffer))
 
242
        ((let ((pathname (buffer-pathname buffer)))
 
243
           (and (file-symbolic-link? pathname)
 
244
                (file-vc-master (file-chase-links pathname) #f)))
 
245
         => (lambda (master)
 
246
              (let ((workfile (vc-master-workfile master))
 
247
                    (type (vc-type-display-name (vc-master-type master))))
 
248
                (let ((follow
 
249
                       (lambda ()
 
250
                         (kill-buffer buffer)
 
251
                         (let ((buffer*
 
252
                                (or (pathname->buffer workfile)
 
253
                                    (find-file-noselect workfile #f))))
 
254
                           (message "Followed link to "
 
255
                                    (->namestring workfile))
 
256
                           buffer*))))
 
257
                  (case (ref-variable vc-follow-symlinks buffer)
 
258
                    ((#F)
 
259
                     (message "Warning: symbolic link to "
 
260
                              type
 
261
                              "-controlled source file"))
 
262
                    ((ASK)
 
263
                     (if (or (pathname->buffer workfile)
 
264
                             (prompt-for-yes-or-no?
 
265
                              (string-append
 
266
                               "Symbolic link to "
 
267
                               type
 
268
                               "-controlled source file; follow link")))
 
269
                         (follow)
 
270
                         (begin
 
271
                           (message
 
272
                            "Warning: editing through the link bypasses version control.")
 
273
                           buffer)))
 
274
                    (else (follow)))))))
 
275
        (else buffer)))
 
276
 
 
277
(set-variable!
 
278
 find-file-not-found-hooks
 
279
 (append! (ref-variable find-file-not-found-hooks)
 
280
          (list (lambda (buffer) (vc-hook:find-file-not-found buffer)))))
 
281
 
 
282
(define (vc-hook:find-file-not-found buffer)
 
283
  (let ((master (buffer-vc-master buffer #f)))
 
284
    (and master
 
285
         (call-with-current-continuation
 
286
          (lambda (k)
 
287
            (bind-condition-handler (list condition-type:error)
 
288
                (lambda (condition) condition (k #f))
 
289
              (lambda ()
 
290
                (vc-checkout master #f)
 
291
                #t)))))))
 
292
 
 
293
(add-event-receiver! event:after-buffer-save
 
294
                     (lambda (buffer) (vc-hook:after-buffer-save buffer)))
 
295
 
 
296
(define (vc-hook:after-buffer-save buffer)
 
297
  (let ((master (buffer-vc-master buffer #f)))
 
298
    (if master
 
299
        (vc-mode-line master buffer))))
 
300
 
 
301
(add-event-receiver! event:set-buffer-pathname
 
302
                     (lambda (buffer) (vc-hook:set-buffer-pathname buffer)))
 
303
 
 
304
(define (vc-hook:set-buffer-pathname buffer)
 
305
  (buffer-remove! buffer 'VC-MASTER))
 
306
 
 
307
(add-event-receiver! event:set-buffer-major-mode
 
308
                     (lambda (buffer) (vc-hook:set-buffer-major-mode buffer)))
 
309
 
 
310
(define (vc-hook:set-buffer-major-mode buffer)
 
311
  (let ((master (buffer-vc-master buffer #f)))
 
312
    (if master
 
313
        (begin
 
314
          (vc-mode-line master buffer)
 
315
          (if (not (ref-variable vc-make-backup-files buffer))
 
316
              (local-set-variable! make-backup-files #f buffer))))))
 
317
 
 
318
;;;; Mode line
 
319
 
 
320
(define (vc-mode-line master buffer)
 
321
  (let ((workfile-buffer (vc-workfile-buffer master #f)))
 
322
    (let ((buffer (or buffer workfile-buffer))
 
323
          (revision
 
324
           (or (vc-backend-workfile-revision master)
 
325
               (vc-backend-default-revision master #f))))
 
326
      (let ((locker (vc-backend-locking-user master revision))
 
327
            (user-name (current-user-name)))
 
328
        (set-variable!
 
329
         vc-mode-line-status
 
330
         (string-append
 
331
          " "
 
332
          (vc-type-display-name (vc-master-type master))
 
333
          (if (ref-variable vc-display-status buffer)
 
334
              (if revision
 
335
                  (let ()
 
336
                    (string-append
 
337
                     (cond ((not locker) "-")
 
338
                           ((string=? locker user-name) ":")
 
339
                           (else (string-append ":" locker ":")))
 
340
                     revision))
 
341
                  " @@")
 
342
              ""))
 
343
         buffer)
 
344
        (buffer-modeline-event! buffer 'VC-MODE-LINE-STATUS)
 
345
        (if (and (buffer-writeable? buffer)
 
346
                 (eq? buffer workfile-buffer)
 
347
                 ;; If the file is locked by some other user, make the
 
348
                 ;; buffer read-only.  Like this, even root cannot modify a
 
349
                 ;; file that someone else has locked.
 
350
                 (or (and locker (not (string=? locker user-name)))
 
351
                     ;; If the user is root, and the file is not
 
352
                     ;; owner-writeable, then pretend that we can't write it
 
353
                     ;; even though we can (because root can write
 
354
                     ;; anything).  This way, even root cannot modify a file
 
355
                     ;; that isn't locked.
 
356
                     (and (= 0 (unix/current-uid))
 
357
                          (fix:= 0
 
358
                                 (fix:and #o200
 
359
                                          (file-modes
 
360
                                           (vc-master-workfile master)))))))
 
361
            (set-buffer-read-only! buffer))))))
 
362
 
 
363
;;;; VC-MASTER association
 
364
 
 
365
(define (current-vc-master error?)
 
366
  (buffer-vc-master (selected-buffer) error?))
 
367
 
 
368
(define (buffer-vc-master buffer error?)
 
369
  (let ((buffer (chase-parent-buffer buffer)))
 
370
    (let ((master (buffer-get buffer 'VC-MASTER #f)))
 
371
      (if (and master (vc-backend-master-valid? master))
 
372
          master
 
373
          (begin
 
374
            (buffer-remove! buffer 'VC-MASTER)
 
375
            (if (vc-dired-buffer? buffer)
 
376
                (let ((workfile (dired-this-file buffer error?)))
 
377
                  (and workfile
 
378
                       (file-vc-master workfile error?)))
 
379
                (let ((workfile (buffer-pathname buffer)))
 
380
                  (if workfile
 
381
                      (let ((master (%file-vc-master workfile error?)))
 
382
                        (if master (buffer-put! buffer 'VC-MASTER master))
 
383
                        master)
 
384
                      (and error? (vc-registration-error buffer))))))))))
 
385
 
 
386
(define (chase-parent-buffer buffer)
 
387
  (let loop ((buffer buffer))
 
388
    (let ((buffer* (buffer-get buffer 'VC-PARENT-BUFFER #f)))
 
389
      (if buffer*
 
390
          (loop buffer*)
 
391
          buffer))))
 
392
 
 
393
(define (file-vc-master workfile error?)
 
394
  (let ((workfile (->pathname workfile)))
 
395
    (let ((buffer (pathname->buffer workfile)))
 
396
      (if buffer
 
397
          (buffer-vc-master buffer error?)
 
398
          (%file-vc-master workfile error?)))))
 
399
 
 
400
(define (%file-vc-master workfile error?)
 
401
  (let ((workfile (->pathname workfile)))
 
402
    (or (vc-backend-find-master workfile)
 
403
        (and error? (vc-registration-error workfile)))))
 
404
 
 
405
(define (guarantee-vc-master-valid master)
 
406
  (if (not (vc-backend-master-valid? master))
 
407
      (error "VC master file disappeared:" (vc-master-pathname master))))
 
408
 
 
409
(define (vc-registration-error object)
 
410
  (if (buffer? object)
 
411
      (editor-error "Buffer " (buffer-name object)
 
412
                    " is not associated with a file.")
 
413
      (editor-error "File " (->namestring object)
 
414
                    " is not under version control.")))
 
415
 
 
416
;;;; Primary Commands
 
417
 
 
418
(define-command vc-toggle-read-only
 
419
  "Change read-only status of current buffer, perhaps via version control.
 
420
If the buffer is visiting a file registered with version control,
 
421
then check the file in or out.  Otherwise, just change the read-only flag
 
422
of the buffer."
 
423
  ()
 
424
  (lambda ()
 
425
    (if (current-vc-master #f)
 
426
        ((ref-command vc-next-action) #f)
 
427
        ((ref-command toggle-read-only)))))
 
428
 
 
429
(define-command vc-next-action
 
430
  "Do the next logical checkin or checkout operation on the current file.
 
431
   If you call this from within a VC dired buffer with no files marked,
 
432
it will operate on the file in the current line.
 
433
   If you call this from within a VC dired buffer, and one or more
 
434
files are marked, it will accept a log message and then operate on
 
435
each one.  The log message will be used as a comment for any register
 
436
or checkin operations, but ignored when doing checkouts.  Attempted
 
437
lock steals will raise an error.
 
438
   A prefix argument lets you specify the version number to use.
 
439
 
 
440
For RCS files:
 
441
   If the file is not already registered, this registers it for version
 
442
control.
 
443
   If the file is registered and not locked by anyone, this checks out
 
444
a writable and locked file ready for editing.
 
445
   If the file is checked out and locked by the calling user, this
 
446
first checks to see if the file has changed since checkout.  If not,
 
447
it performs a revert.
 
448
   If the file has been changed, this pops up a buffer for entry
 
449
of a log message; when the message has been entered, it checks in the
 
450
resulting changes along with the log message as change commentary.  If
 
451
the variable `vc-keep-workfiles' is true (which is its default), a
 
452
read-only copy of the changed file is left in place afterwards.
 
453
   If the file is registered and locked by someone else, you are given
 
454
the option to steal the lock.
 
455
 
 
456
For CVS files:
 
457
   If the file is not already registered, this registers it for version
 
458
control.  This does a \"cvs add\", but no \"cvs commit\".
 
459
   If the file is added but not committed, it is committed.
 
460
   If your working file is changed, but the repository file is
 
461
unchanged, this pops up a buffer for entry of a log message; when the
 
462
message has been entered, it checks in the resulting changes along
 
463
with the logmessage as change commentary.  A writable file is retained.
 
464
   If the repository file is changed, you are asked if you want to
 
465
merge in the changes into your working copy."
 
466
  "P"
 
467
  (lambda (revision?)
 
468
    (let ((buffer (selected-buffer)))
 
469
      (if (vc-dired-buffer? buffer)
 
470
          (vc-next-action-dired buffer)
 
471
          (vc-next-action-on-file (or (buffer-pathname buffer)
 
472
                                      (vc-registration-error buffer))
 
473
                                  #f revision? #f)))))
 
474
 
 
475
(define-command vc-register
 
476
  "Register the current file into your version-control system."
 
477
  "P"
 
478
  (lambda (revision?)
 
479
    (let ((workfile
 
480
           (let ((buffer (selected-buffer)))
 
481
             (or (if (vc-dired-buffer? buffer)
 
482
                     (dired-this-file buffer #t)
 
483
                     (buffer-pathname buffer))
 
484
                 (vc-registration-error buffer)))))
 
485
      (if (file-vc-master workfile #f)
 
486
          (editor-error "This file is already registered."))
 
487
      (vc-register workfile revision? #f #f))))
 
488
 
 
489
(define (vc-next-action-on-file workfile from-dired? revision? comment)
 
490
  (let ((master (file-vc-master workfile #f)))
 
491
    (if master
 
492
        (let ((do-checkin
 
493
               (lambda ()
 
494
                 (let* ((buffer
 
495
                         (let ((buffer (pathname->buffer workfile)))
 
496
                           (and buffer
 
497
                                (find-file-revert buffer))))
 
498
                        (shown? #f)
 
499
                        (show
 
500
                         (lambda ()
 
501
                           (if (not shown?)
 
502
                               (begin
 
503
                                 (if from-dired?
 
504
                                     (pop-up-buffer buffer #f
 
505
                                                    '(NOT-CURRENT-WINDOW))
 
506
                                     (select-buffer buffer))
 
507
                                 (set! shown? #t))))))
 
508
                   ;; If the file on disk is newer, then the user just
 
509
                   ;; said no to rereading it.  So the user probably
 
510
                   ;; wishes to overwrite the file with the buffer's
 
511
                   ;; contents, and check that in.
 
512
                   (cond ((not buffer) unspecific)
 
513
                         ((verify-visited-file-modification-time? buffer)
 
514
                          (vc-save-buffer buffer #t))
 
515
                         ((begin
 
516
                            (show)
 
517
                            (prompt-for-yes-or-no?
 
518
                             "Replace file on disk with buffer contents"))
 
519
                          (save-buffer buffer #f))
 
520
                         (else (editor-error "Aborted")))
 
521
                   ;; Revert if file is unchanged and buffer is too.
 
522
                   ;; If buffer is modified, that means the user just
 
523
                   ;; said no to saving it; in that case, don't
 
524
                   ;; revert, because the user might intend to save
 
525
                   ;; after finishing the log entry.
 
526
                   (cond ((or (and buffer (buffer-modified? buffer))
 
527
                              (vc-workfile-modified? master))
 
528
                          (vc-checkin master revision? comment))
 
529
                         ;; DO NOT revert the file without asking the
 
530
                         ;; user!
 
531
                         ((prompt-for-yes-or-no?
 
532
                           (if buffer
 
533
                               (begin (show) "Revert to master version")
 
534
                               (string-append "Revert "
 
535
                                              (file-namestring workfile)
 
536
                                              " to master version")))
 
537
                          (vc-backend-revert master)
 
538
                          (if buffer (vc-revert-buffer buffer #t)))))))
 
539
              (do-checkout
 
540
               (lambda ()
 
541
                 (vc-save-workfile-buffer workfile)
 
542
                 (vc-checkout master revision?))))
 
543
          (if (cvs-master? master)
 
544
              (case (cvs-status master)
 
545
                ((UP-TO-DATE)
 
546
                 (let ((buffer (vc-workfile-buffer master #f)))
 
547
                   (cond ((or (and buffer (buffer-modified? buffer))
 
548
                              (cvs-file-edited? master))
 
549
                          (do-checkin))
 
550
                         ((or revision? (cvs-workfile-protected? workfile))
 
551
                          (do-checkout))
 
552
                         ((not from-dired?)
 
553
                          (message (buffer-name buffer) " is up to date.")))))
 
554
                ((NEEDS-CHECKOUT NEEDS-MERGE)
 
555
                 (vc-next-action-merge master from-dired?))
 
556
                ((LOCALLY-MODIFIED LOCALLY-ADDED LOCALLY-REMOVED)
 
557
                 (do-checkin))
 
558
                ((UNRESOLVED-CONFLICT)
 
559
                 (message (->namestring workfile)
 
560
                          " has an unresolved conflict."))
 
561
                (else
 
562
                 (error "Unable to determine CVS status of file:" workfile)))
 
563
              (let ((owner (vc-backend-locking-user master #f)))
 
564
                (cond ((not owner) (do-checkout))
 
565
                      ((string=? owner (current-user-name)) (do-checkin))
 
566
                      (else (vc-steal-lock master revision? comment owner))))))
 
567
        (vc-register workfile revision? comment 'LOCK))))
 
568
 
 
569
(define (vc-next-action-dired buffer)
 
570
  (let ((files
 
571
         (let ((files (dired-marked-files buffer)))
 
572
           (if (pair? files)
 
573
               files
 
574
               (dired-next-files 1 buffer)))))
 
575
    (if (pair? files)
 
576
        (if (pair? (cdr files))
 
577
            (vc-start-entry
 
578
             buffer
 
579
             "Enter a change comment for the marked files."
 
580
             (if (there-exists? files
 
581
                   (lambda (file)
 
582
                     (let ((master (file-vc-master (car file) #f)))
 
583
                       (and master
 
584
                            (if (cvs-master? master)
 
585
                                (memq (cvs-status master)
 
586
                                      '(LOCALLY-MODIFIED
 
587
                                        LOCALLY-ADDED
 
588
                                        LOCALLY-REMOVED))
 
589
                                (vc-backend-locking-user master #f))))))
 
590
                 #f
 
591
                 "")
 
592
             (lambda (comment)
 
593
               (for-each-dired-mark buffer
 
594
                 (lambda (file)
 
595
                   (let ((msg
 
596
                          (string-append "Processing "
 
597
                                         (->namestring file)
 
598
                                         "...")))
 
599
                     (message msg)
 
600
                     (vc-next-action-on-file file #t #f comment)
 
601
                     (message msg "done")))))
 
602
             #f)
 
603
            (vc-next-action-on-file (caar files) #t #f #f)))))
 
604
 
 
605
(define (vc-register workfile revision? comment keep?)
 
606
  (let ((buffer (pathname->buffer workfile)))
 
607
    (let ((revision
 
608
           (or (vc-get-revision revision?
 
609
                                (string-append "Initial version level for "
 
610
                                               (->namestring workfile)))
 
611
               (ref-variable vc-default-init-version buffer))))
 
612
      ;; Watch out for new buffers of size 0: the corresponding file
 
613
      ;; does not exist yet, even though buffer-modified? is false.
 
614
      (if (and buffer
 
615
               (not (buffer-modified? buffer))
 
616
               (= 0 (buffer-length buffer))
 
617
               (not (file-exists? workfile)))
 
618
          (buffer-modified! buffer))
 
619
      (vc-save-workfile-buffer workfile)
 
620
      (vc-start-entry workfile "Enter initial comment."
 
621
                      (or comment
 
622
                          (if (ref-variable vc-initial-comment buffer) #f ""))
 
623
                      (let ((keep?
 
624
                             (or keep?
 
625
                                 (ref-variable vc-keep-workfiles buffer))))
 
626
                        (lambda (comment)
 
627
                          (vc-backend-register workfile revision comment keep?)
 
628
                          (vc-resync-workfile-buffer workfile keep?)))
 
629
                      #f))))
 
630
 
 
631
(define (vc-checkout master revision?)
 
632
  (let ((revision (vc-get-revision revision? "Branch or version to move to")))
 
633
    (let ((do-it
 
634
           (lambda ()
 
635
             (vc-backend-checkout master revision #t #f)
 
636
             (vc-revert-workfile-buffer master #t))))
 
637
      (cond ((not (and (let ((value (ref-variable vc-checkout-carefully)))
 
638
                         (if (boolean? value) value (value)))
 
639
                       (vc-workfile-modified? master)))
 
640
             (do-it))
 
641
            ((cleanup-pop-up-buffers
 
642
              (lambda ()
 
643
                (run-diff master #f #f)
 
644
                (insert-string
 
645
                 (string-append "Changes to "
 
646
                                (vc-workfile-string master)
 
647
                                " since last lock:\n\n")
 
648
                 (buffer-start (get-vc-diff-buffer #f)))
 
649
                (pop-up-vc-diff-buffer #f)
 
650
                (editor-beep)
 
651
                (prompt-for-yes-or-no?
 
652
                 "File has unlocked changes, claim lock retaining changes")))
 
653
             (guarantee-vc-master-valid master)
 
654
             (vc-backend-steal master revision)
 
655
             (let ((buffer (vc-workfile-buffer master #f)))
 
656
               (if buffer
 
657
                   (vc-mode-line master buffer))))
 
658
            ((prompt-for-yes-or-no? "Revert to checked-in version, instead")
 
659
             (do-it))
 
660
            (else
 
661
             (editor-error "Checkout aborted."))))))
 
662
 
 
663
(define (vc-checkin master revision? comment)
 
664
  (let ((revision (vc-get-revision revision? "New version level")))
 
665
    (vc-save-workfile-buffer (vc-master-workfile master))
 
666
    (vc-start-entry master "Enter a change comment." comment
 
667
                    (let ((keep?
 
668
                           (or (cvs-master? master)
 
669
                               (ref-variable vc-keep-workfiles
 
670
                                             (vc-workfile-buffer master #f)))))
 
671
                      (lambda (comment)
 
672
                        (vc-backend-checkin master revision
 
673
                                            (if (blank-string? comment)
 
674
                                                "*** empty log message ***"
 
675
                                                comment)
 
676
                                            keep?)
 
677
                        (vc-resync-workfile-buffer (vc-master-workfile master)
 
678
                                                   keep?)))
 
679
                    (lambda ()
 
680
                      (event-distributor/invoke!
 
681
                       (ref-variable vc-checkin-hooks
 
682
                                     (vc-workfile-buffer master #f))
 
683
                       master)))))
 
684
 
 
685
(define (vc-steal-lock master revision? comment owner)
 
686
  (if (and (rcs-master? master)
 
687
           (not (vc-release? vc-type:rcs "5.6.2")))
 
688
      ;; Can't steal locks with old RCS versions.
 
689
      (editor-error "File is locked by " owner "."))
 
690
  (let ((filename (vc-workfile-string master)))
 
691
    (if comment
 
692
        (editor-error "Sorry, you can't steal the lock on "
 
693
                      filename
 
694
                      " this way."))
 
695
    (let ((revision (vc-get-revision revision? "Version level to steal")))
 
696
      (let ((file:rev
 
697
             (if revision
 
698
                 (string-append filename ":" revision)
 
699
                 filename)))
 
700
        (if (not (prompt-for-confirmation?
 
701
                  (string-append "Take the lock on " file:rev " from " owner)))
 
702
            (editor-error "Steal cancelled."))
 
703
        (make-mail-buffer `(("To" ,owner) ("Subject" ,file:rev))
 
704
                          #f
 
705
                          select-buffer-other-window
 
706
                          'DISCARD-PREVIOUS-MAIL)
 
707
        (let ((mail-buffer (selected-buffer)))
 
708
          (insert-string
 
709
           (string-append "I stole the lock on " file:rev ", "
 
710
                          (universal-time->string (get-universal-time))
 
711
                          ".\n")
 
712
           (buffer-end mail-buffer))
 
713
          (set-buffer-point! mail-buffer (buffer-end mail-buffer))
 
714
          (let ((variable (ref-variable-object send-mail-procedure)))
 
715
            (define-variable-local-value! mail-buffer variable
 
716
              (lambda ()
 
717
                (guarantee-vc-master-valid master)
 
718
                (vc-backend-steal master revision)
 
719
                (vc-revert-workfile-buffer master #t)
 
720
                ;; Send the mail after the steal has completed
 
721
                ;; successfully.
 
722
                ((variable-default-value variable)))))))))
 
723
  (message "Please explain why you are stealing the lock."
 
724
           "  Type C-c C-c when done."))
 
725
 
 
726
(define (vc-next-action-merge master from-dired?)
 
727
  (let ((buffer (vc-workfile-buffer master #f)))
 
728
    ;; (NOT FROM-DIRED?) implies (NOT (NOT BUFFER)).
 
729
    (if (or from-dired?
 
730
            (prompt-for-yes-or-no?
 
731
             (string-append
 
732
              (buffer-name buffer)
 
733
              " is not up-to-date.  Merge in changes now")))
 
734
        (begin
 
735
          (if (and buffer (buffer-modified? buffer))
 
736
              (begin
 
737
                (if from-dired?
 
738
                    (select-buffer-other-window buffer)
 
739
                    (select-buffer buffer))
 
740
                (vc-save-buffer buffer #f)))
 
741
          (if (and buffer
 
742
                   (buffer-modified? buffer)
 
743
                   (not
 
744
                    (prompt-for-yes-or-no?
 
745
                     (string-append
 
746
                      "Buffer "
 
747
                      (buffer-name buffer)
 
748
                      " modified; merge file on disc anyhow"))))
 
749
              (editor-error "Merge aborted"))
 
750
          (let ((conflicts? (cvs-backend-merge-news master)))
 
751
            (if buffer
 
752
                (vc-revert-buffer buffer #t))
 
753
            (if (and conflicts?
 
754
                     (prompt-for-confirmation?
 
755
                      "Conflicts detected.  Resolve them now"))
 
756
                (find-file (vc-master-workfile master)))))
 
757
        (editor-error (buffer-name buffer) " needs update."))))
 
758
 
 
759
;;;; Auxiliary Commands
 
760
 
 
761
(define-command vc-diff
 
762
  "Display diffs between file versions.
 
763
Normally this compares the current file and buffer with the most recent 
 
764
checked in version of that file.  This uses no arguments.
 
765
With a prefix argument, it reads the file name to use
 
766
and two version designators specifying which versions to compare."
 
767
  "P"
 
768
  (lambda (revisions?)
 
769
    (if revisions?
 
770
        (dispatch-on-command (ref-command-object vc-version-diff))
 
771
        (vc-diff (current-vc-master #t) #f #f))))
 
772
 
 
773
(define-command vc-version-diff
 
774
  "Report diffs between two stored versions REV1 and REV2 of a file."
 
775
  (lambda ()
 
776
    (let* ((workfile
 
777
           (prompt-for-existing-file
 
778
            "File to diff"
 
779
            (let ((pathname (buffer-pathname (selected-buffer))))
 
780
              (and pathname
 
781
                   (list pathname)))))
 
782
           (master (file-vc-master workfile #t))
 
783
           (revision (vc-backend-workfile-revision master)))
 
784
      (call-with-values
 
785
          (lambda ()
 
786
            (let ((previous
 
787
                   (and (not (vc-workfile-modified? master))
 
788
                        (previous-revision revision))))
 
789
              (if previous
 
790
                  (values previous revision)
 
791
                  (values revision #f))))
 
792
        (lambda (default1 default2)
 
793
          (let* ((rev1 (prompt-for-string "Older version" default1))
 
794
                 (rev2
 
795
                  (prompt-for-string "Newer version" default2
 
796
                                     'DEFAULT-TYPE 'NULL-DEFAULT)))
 
797
            (list workfile rev1 rev2))))))
 
798
  (lambda (workfile rev1 rev2)
 
799
    (if (file-directory? workfile)
 
800
        (editor-error "Directory diffs not yet supported.")
 
801
        (vc-diff (file-vc-master workfile #t) rev1 rev2))))
 
802
 
 
803
(define (vc-diff master rev1 rev2)
 
804
  (vc-save-workfile-buffer (vc-master-workfile master))
 
805
  (let ((rev1 (vc-normalize-revision rev1))
 
806
        (rev2 (vc-normalize-revision rev2)))
 
807
    (if (and (or rev1 rev2 (vc-workfile-modified? master))
 
808
             (run-diff master rev1 rev2))
 
809
        (begin
 
810
          (pop-up-vc-diff-buffer #t)
 
811
          #f)
 
812
        (begin
 
813
          (message "No changes to "
 
814
                   (vc-workfile-string master)
 
815
                   (if (and rev1 rev2)
 
816
                       (string-append " between " rev1 " and " rev2)
 
817
                       (string-append " since "
 
818
                                      (or rev1 rev2 "latest version")))
 
819
                   ".")
 
820
          #t))))
 
821
 
 
822
(define (run-diff master rev1 rev2)
 
823
  (if (and (not rev1) (not rev2))
 
824
      (cache-value-2! master 'MODIFIED?
 
825
                      (vc-master-pathname master)
 
826
                      (vc-master-workfile master)
 
827
        (lambda (tm tw)
 
828
          (let ((modified? (vc-backend-diff master rev1 rev2 #f)))
 
829
            (set-vc-cvs-workfile-mtime-string! master tm tw modified?)
 
830
            modified?)))
 
831
      (vc-backend-diff master rev1 rev2 #f)))
 
832
 
 
833
(define-command vc-version-other-window
 
834
  "Visit version REV of the current buffer in another window.
 
835
If the current buffer is named `F', the version is named `F.~REV~'.
 
836
If `F.~REV~' already exists, it is used instead of being re-created."
 
837
  "sVersion to visit (default is latest version)"
 
838
  (lambda (revision)
 
839
    (let* ((master (current-vc-master #t))
 
840
           (revision
 
841
            (or (vc-normalize-revision revision)
 
842
                (vc-backend-workfile-revision master)
 
843
                (vc-backend-default-revision master #f)))
 
844
           (workfile
 
845
            (string-append (vc-workfile-string master) ".~" revision "~")))
 
846
      (if (not (file-exists? workfile))
 
847
          (vc-backend-checkout master revision #f workfile))
 
848
      (find-file-other-window workfile))))
 
849
 
 
850
(define-command vc-insert-headers
 
851
  "Insert headers in a file for use with your version-control system.
 
852
Headers are inserted at the start of the buffer."
 
853
  ()
 
854
  (lambda ()
 
855
    (let* ((master (current-vc-master #t))
 
856
           (buffer (vc-workfile-buffer master #t)))
 
857
      (without-group-clipped! (buffer-group buffer)
 
858
        (lambda ()
 
859
          (if (or (not (vc-backend-check-headers master buffer))
 
860
                  (prompt-for-confirmation?
 
861
                   "Version headers already exist.  Insert another set"))
 
862
              (insert-string
 
863
               (string-append
 
864
                (or (ref-variable comment-start buffer) "#")
 
865
                "\t"
 
866
                (vc-type-header-keyword (vc-master-type master))
 
867
                (let ((end (or (ref-variable comment-end buffer) "")))
 
868
                  (if (string-null? end)
 
869
                      end
 
870
                      (string-append "\t" end)))
 
871
                "\n")
 
872
               (buffer-start buffer))))))))
 
873
 
 
874
(define-command vc-print-log
 
875
  "List the change log of the current buffer in a window."
 
876
  ()
 
877
  (lambda ()
 
878
    (vc-backend-print-log (current-vc-master #t))
 
879
    (pop-up-vc-command-buffer #f)))
 
880
 
 
881
(define-command vc-revert-buffer
 
882
  "Revert the current buffer's file back to the latest checked-in version.
 
883
This asks for confirmation if the buffer contents are not identical
 
884
to that version."
 
885
  ()
 
886
  (lambda ()
 
887
    (let* ((master (current-vc-master #t))
 
888
           (buffer (vc-workfile-buffer master #t)))
 
889
      (if (or (and (vc-workfile-modified? master)
 
890
                   (or (ref-variable vc-suppress-confirm)
 
891
                       (cleanup-pop-up-buffers
 
892
                        (lambda ()
 
893
                          (run-diff master #f #f)
 
894
                          (pop-up-vc-diff-buffer #f)
 
895
                          (prompt-for-yes-or-no? "Discard changes")))))
 
896
              (and (cvs-master? master)
 
897
                   (cvs-file-edited? master)))
 
898
          (begin
 
899
            (vc-backend-revert master)
 
900
            (vc-revert-buffer buffer #t))
 
901
          (editor-error "Revert cancelled.")))))
 
902
 
 
903
;;;; VC Dired
 
904
 
 
905
(define-command vc-directory
 
906
  "Show version-control status of files under a directory.
 
907
Normally shows only locked files; prefix arg says to show all files."
 
908
  "DDired under VC (directory)\nP"
 
909
  (lambda (directory all-files?)
 
910
    (let ((buffer (vc-dired directory all-files?)))
 
911
      (if (group-end? (line-start (buffer-start buffer) 1 'LIMIT))
 
912
          (begin
 
913
            (if (not (buffer-visible? buffer))
 
914
                (kill-buffer buffer))
 
915
            (message "No files are currently "
 
916
                     (if all-files? "registered" "locked")
 
917
                     " in "
 
918
                     (->namestring directory)))
 
919
          (pop-up-buffer buffer #t)))))
 
920
 
 
921
(define-command vc-dired
 
922
  "Show version-control status of files under a directory.
 
923
Normally shows only locked files; prefix arg says to show all files."
 
924
  "DVC-Dired (directory)\nP"
 
925
  (lambda (directory all-files?)
 
926
    (select-buffer (vc-dired directory all-files?))))
 
927
 
 
928
(define (vc-dired directory all-files?)
 
929
  (let ((buffer (get-vc-dired-buffer directory)))
 
930
    (fill-vc-dired-buffer! buffer directory all-files?)
 
931
    buffer))
 
932
 
 
933
(define (get-vc-dired-buffer directory)
 
934
  (or (list-search-positive (buffer-list)
 
935
        (lambda (buffer)
 
936
          (let ((spec (buffer-get buffer 'VC-DIRECTORY-SPEC #f)))
 
937
            (and spec
 
938
                 (pathname=? (car spec) directory)))))
 
939
      (new-buffer (pathname->buffer-name directory))))
 
940
 
 
941
(define (fill-vc-dired-buffer! buffer directory all-files?)
 
942
  (let ((msg
 
943
         (string-append "Reading directory " (->namestring directory) "...")))
 
944
    (buffer-reset! buffer)
 
945
    (set-buffer-major-mode! buffer (ref-mode-object vc-dired))
 
946
    (set-buffer-default-directory! buffer (directory-pathname directory))
 
947
    (buffer-put! buffer 'VC-DIRECTORY-SPEC (cons directory all-files?))
 
948
    (buffer-put! buffer 'REVERT-BUFFER-METHOD revert-vc-dired-buffer)
 
949
    (message msg)
 
950
    (let ((mark (mark-left-inserting-copy (buffer-start buffer))))
 
951
      (insert-string (string-append "  Files currently "
 
952
                                    (if all-files? "registered" "locked")
 
953
                                    " under "
 
954
                                    (->namestring directory)
 
955
                                    ":\n")
 
956
                     mark)
 
957
      (generate-vc-dired-lines directory all-files? mark)
 
958
      (mark-temporary! mark))
 
959
    (message msg "done"))
 
960
  (set-buffer-point! buffer (buffer-start buffer))
 
961
  (buffer-not-modified! buffer)
 
962
  (set-buffer-read-only! buffer))
 
963
 
 
964
(define (revert-vc-dired-buffer buffer dont-use-auto-save? dont-confirm?)
 
965
  (let ((spec (buffer-get buffer 'VC-DIRECTORY-SPEC #f)))
 
966
    (if spec
 
967
        (begin
 
968
          (fill-vc-dired-buffer! buffer (car spec) (cdr spec))
 
969
          buffer)
 
970
        (revert-buffer-default buffer dont-use-auto-save? dont-confirm?))))
 
971
 
 
972
(define (generate-vc-dired-lines directory all-files? mark)
 
973
  (for-each
 
974
   (lambda (file)
 
975
     (let ((attr (file-attributes-direct file)))
 
976
       (if (and attr (not (file-attributes/type attr)))
 
977
           (let ((status
 
978
                  (let ((master (file-vc-master file #f)))
 
979
                    (cond ((not master)
 
980
                           #f)
 
981
                          ((cvs-master? master)
 
982
                           (and (vc-workfile-modified? master)
 
983
                                (case (cvs-status master)
 
984
                                  ((LOCALLY-MODIFIED) "modified")
 
985
                                  ((LOCALLY-ADDED) "added")
 
986
                                  ((NEEDS-CHECKOUT) "patch")
 
987
                                  ((NEEDS-MERGE) "merge")
 
988
                                  ((UNRESOLVED-CONFLICT) "conflict")
 
989
                                  (else #f))))
 
990
                          (else
 
991
                           (vc-backend-locking-user master #f))))))
 
992
             (if (or status all-files?)
 
993
                 (generate-vc-dired-line file attr status mark))))))
 
994
   (directory-read directory)))
 
995
 
 
996
(define (generate-vc-dired-line file attr status mark)
 
997
  (insert-string
 
998
   (string-append
 
999
    "  "
 
1000
    (file-attributes/mode-string attr)
 
1001
    " "
 
1002
    (pad-on-right-to (if status (string-append "(" status ")") "") 10)
 
1003
    " "
 
1004
    (file-time->ls-string (file-attributes/modification-time attr))
 
1005
    " "
 
1006
    (file-namestring file)
 
1007
    "\n")
 
1008
   mark))
 
1009
 
 
1010
(define-major-mode vc-dired dired "VC-Dired"
 
1011
  "The major mode used in VC directory buffers.  It works like Dired,
 
1012
but lists only files under version control, with the current VC state of 
 
1013
each file being indicated in the place of the file's link count, owner, 
 
1014
group and size.  Subdirectories are also listed, and you may insert them 
 
1015
into the buffer as desired, as in Dired.
 
1016
  All Dired commands operate normally, with the exception of `v', which
 
1017
is redefined as the version control prefix, so that you can type 
 
1018
`vl', `v=' etc. to invoke `vc-print-log', `vc-diff', and the like on
 
1019
the file named in the current Dired buffer line.  `vv' invokes
 
1020
`vc-next-action' on this file, or on all files currently marked.
 
1021
There is a special command, `*l', to mark all files currently locked.
 
1022
 
 
1023
\\{vc-dired}"
 
1024
  (lambda (buffer)
 
1025
    buffer
 
1026
    unspecific))
 
1027
 
 
1028
(define (vc-dired-buffer? buffer)
 
1029
  (eq? (ref-mode-object vc-dired) (buffer-major-mode buffer)))
 
1030
 
 
1031
(define-key 'vc-dired '(#\v #\h) 'vc-insert-headers)
 
1032
(define-key 'vc-dired '(#\v #\i) 'vc-register)
 
1033
(define-key 'vc-dired '(#\v #\l) 'vc-print-log)
 
1034
;;(define-key 'vc-dired '(#\v #\m) 'vc-merge)
 
1035
;;(define-key 'vc-dired '(#\v #\r) 'vc-retrieve-snapshot)
 
1036
;;(define-key 'vc-dired '(#\v #\s) 'vc-create-snapshot)
 
1037
(define-key 'vc-dired '(#\v #\u) 'vc-revert-buffer)
 
1038
(define-key 'vc-dired '(#\v #\v) 'vc-next-action)
 
1039
(define-key 'vc-dired '(#\v #\=) 'vc-diff)
 
1040
(define-key 'vc-dired '(#\v #\~) 'vc-version-other-window)
 
1041
(define-key 'vc-dired '(#\* #\l) 'vc-dired-mark-locked)
 
1042
 
 
1043
(define-command vc-dired-mark-locked
 
1044
  "Mark all files currently locked."
 
1045
  ()
 
1046
  (lambda ()
 
1047
    (dired-mark-files! (selected-buffer)
 
1048
      (lambda (file)
 
1049
        (let ((master (file-vc-master file #f)))
 
1050
          (and master
 
1051
               (vc-backend-locking-user master #f)))))))
 
1052
 
 
1053
;;;; Log Entries
 
1054
 
 
1055
(define (vc-start-entry reference msg comment finish-entry after)
 
1056
  (if comment
 
1057
      (begin
 
1058
        (finish-entry comment)
 
1059
        (if after (after)))
 
1060
      (let ((log-buffer (new-buffer "*VC-log*")))
 
1061
        (set-buffer-major-mode! log-buffer (ref-mode-object vc-log))
 
1062
        (if (vc-master? reference)
 
1063
            (vc-mode-line reference log-buffer))
 
1064
        (let ((buffer
 
1065
               (and reference
 
1066
                    (if (buffer? reference)
 
1067
                        reference
 
1068
                        (pathname->buffer (->workfile reference))))))
 
1069
          (if buffer
 
1070
              (buffer-put! log-buffer 'VC-PARENT-BUFFER buffer)
 
1071
              (buffer-remove! log-buffer 'VC-PARENT-BUFFER)))
 
1072
        (let ((window (selected-window))
 
1073
              (buffer (selected-buffer)))
 
1074
          (let ((log-window (pop-up-buffer log-buffer #t)))
 
1075
            (buffer-put! log-buffer
 
1076
                         'VC-LOG-FINISH-ENTRY
 
1077
                         (vc-finish-entry reference
 
1078
                                          finish-entry
 
1079
                                          after
 
1080
                                          (weak-cons log-window #f)
 
1081
                                          (weak-cons window #f)
 
1082
                                          (weak-cons buffer #f)))))
 
1083
        (message msg "  Type C-c C-c when done."))))
 
1084
 
 
1085
(define (vc-finish-entry reference finish-entry after log-window window buffer)
 
1086
  (lambda (log-buffer)
 
1087
    (if (vc-master? reference)
 
1088
        (begin
 
1089
          (guarantee-vc-master-valid reference)
 
1090
          (vc-backend-check-log-entry reference log-buffer)))
 
1091
    (guarantee-newline (buffer-end log-buffer))
 
1092
    (let ((comment (buffer-string log-buffer))
 
1093
          (parent-buffer (chase-parent-buffer log-buffer)))
 
1094
      (comint-record-input vc-comment-ring comment)
 
1095
      ;; Save any changes the user might have made while editing the
 
1096
      ;; comment.
 
1097
      (if (and (not (eq? parent-buffer log-buffer))
 
1098
               (buffer-alive? parent-buffer)
 
1099
               (not (vc-dired-buffer? parent-buffer)))
 
1100
          (vc-save-buffer parent-buffer #t))
 
1101
      ;; If a new window was created to hold the log buffer, and the log
 
1102
      ;; buffer is still selected in that window, delete it.
 
1103
      (let ((log-window (weak-car log-window)))
 
1104
        (if (and log-window
 
1105
                 (window-live? log-window)
 
1106
                 (eq? log-buffer (window-buffer log-window))
 
1107
                 (not (window-has-no-neighbors? log-window)))
 
1108
            (window-delete! log-window)))
 
1109
      ;; Either kill or bury the log buffer.
 
1110
      (if (buffer-alive? log-buffer)
 
1111
          (if (ref-variable vc-delete-logbuf-window log-buffer)
 
1112
              (kill-buffer log-buffer)
 
1113
              (begin
 
1114
                (make-buffer-invisible log-buffer)
 
1115
                (bury-buffer log-buffer))))
 
1116
      (let ((window (weak-car window))
 
1117
            (buffer (weak-car buffer)))
 
1118
        (if (and window (window-live? window))
 
1119
            (select-window window))
 
1120
        (if (and buffer (buffer-alive? buffer))
 
1121
            (if (and window (window-live? window))
 
1122
                (select-buffer-no-record buffer window)
 
1123
                (select-buffer buffer))))
 
1124
      ;; Do the log operation.
 
1125
      (finish-entry comment))
 
1126
    (if after (after))))
 
1127
 
 
1128
(define vc-comment-ring
 
1129
  (make-ring 32))
 
1130
 
 
1131
(define-major-mode vc-log text "VC-Log"
 
1132
  "Major mode for entering a version-control change log message.
 
1133
In this mode, the following additional bindings will be in effect.
 
1134
 
 
1135
\\[vc-finish-logentry]  proceed with check in, ending log message entry
 
1136
 
 
1137
Whenever you do a checkin, your log comment is added to a ring of
 
1138
saved comments.  These can be recalled as follows:
 
1139
 
 
1140
\\[comint-previous-input]       replace region with previous message in comment ring
 
1141
\\[comint-next-input]   replace region with next message in comment ring
 
1142
\\[comint-history-search-reverse]       search backward for regexp in the comment ring
 
1143
\\[comint-history-search-forward]       search forward for regexp in the comment ring
 
1144
 
 
1145
Entry to the vc-log submode calls the value of text-mode-hook, then
 
1146
the value of vc-log-mode-hook."
 
1147
  (lambda (buffer)
 
1148
    (local-set-variable! comint-input-ring vc-comment-ring buffer)
 
1149
    (local-set-variable! comint-last-input-match #f buffer)
 
1150
    (event-distributor/invoke! (ref-variable vc-log-mode-hook buffer) buffer)))
 
1151
 
 
1152
(define-key 'vc-log '(#\C-c #\C-c) 'vc-finish-logentry)
 
1153
(define-key 'vc-log #\M-p 'comint-previous-input)
 
1154
(define-key 'vc-log #\M-n 'comint-next-input)
 
1155
(define-key 'vc-log #\M-r 'comint-history-search-backward)
 
1156
(define-key 'vc-log #\M-s 'comint-history-search-forward)
 
1157
 
 
1158
(define-command vc-finish-logentry
 
1159
  "Complete the operation implied by the current log entry."
 
1160
  ()
 
1161
  (lambda ()
 
1162
    (let ((buffer (selected-buffer)))
 
1163
      (let ((finish-entry (buffer-get buffer 'VC-LOG-FINISH-ENTRY #f)))
 
1164
        (if (not finish-entry)
 
1165
            (error "No log operation is pending."))
 
1166
        (finish-entry buffer)))))
 
1167
 
 
1168
;;;; Back-End Calls
 
1169
 
 
1170
;;; In what follows, a "revision string" has the following definition:
 
1171
;;; A revision string of #F usually refers to the head of the
 
1172
;;;   branch on which the workfile resides, but in some cases it has a
 
1173
;;;   different meaning.
 
1174
;;; A revision string with an odd number of elements specifies a branch,
 
1175
;;;   and the string refers to the head of the specified branch.
 
1176
;;; A revision string with an even number of elements specifies a
 
1177
;;;   particular revision.  When checking in, this revision must not
 
1178
;;;   exist, and must be greater than any existing revision on the
 
1179
;;;   associated trunk or branch.  When checking out, this revision
 
1180
;;;   must exist.
 
1181
;;; A revision string may be symbolic, in which case it is treated as
 
1182
;;;   the numeric string that it is bound to.
 
1183
 
 
1184
(define (vc-backend-release type)
 
1185
  ;; TYPE is a VC-TYPE object.
 
1186
  ;; The return value is either a release string or #F.
 
1187
  ;; A release string matches "[0-9.]+ *.*".
 
1188
  (let ((release (vc-type-get type 'RELEASE 'UNKNOWN)))
 
1189
    (if (eq? 'UNKNOWN release)
 
1190
        (let ((release ((vc-type-operation type 'RELEASE))))
 
1191
          (vc-type-put! type 'RELEASE release)
 
1192
          release)
 
1193
        release)))
 
1194
 
 
1195
(define (vc-backend-find-master workfile)
 
1196
  (let loop ((types vc-types))
 
1197
    (and (pair? types)
 
1198
         (or ((vc-type-operation (car types) 'FIND-MASTER) workfile)
 
1199
             (loop (cdr types))))))
 
1200
 
 
1201
(define (vc-backend-master-valid? master)
 
1202
  ;; MASTER is a VC-MASTER object.
 
1203
  ;; The return value is a boolean indicating that MASTER is valid.
 
1204
  (vc-call 'VALID? master))
 
1205
 
 
1206
(define (vc-backend-default-revision master error?)
 
1207
  ;; MASTER is a valid VC-MASTER object.
 
1208
  ;; ERROR? is a boolean.
 
1209
  ;; The default revision (usually the head of the trunk) is returned.
 
1210
  ;; If there is no such revision, then if ERROR? is true, an error is
 
1211
  ;; signalled.  Otherwise #F is returned.
 
1212
  (vc-call 'DEFAULT-REVISION master error?))
 
1213
 
 
1214
(define (vc-backend-workfile-revision master)
 
1215
  ;; MASTER is a valid VC-MASTER object.
 
1216
  ;; The last checked-in revision of the file is returned.
 
1217
  ;; If this can't be determined, #F is returned.
 
1218
  (vc-call 'WORKFILE-REVISION master))
 
1219
 
 
1220
(define (vc-backend-locking-user master revision)
 
1221
  ;; MASTER is a valid VC-MASTER object.
 
1222
  ;; REVISION is a revision string or #F.
 
1223
  ;;   A REVISION of #F refers to the last checked-in revision of the
 
1224
  ;;   workfile.
 
1225
  ;; The user holding the lock on that revision is returned.  If there
 
1226
  ;;   is no lock, or if the lock cannot be determined, #F is returned.
 
1227
  (vc-call 'LOCKING-USER master revision))
 
1228
 
 
1229
(define (vc-backend-register workfile revision comment keep?)
 
1230
  ;; WORKFILE is an absolute pathname to an existing file.
 
1231
  ;; REVISION is either a revision string or #F.
 
1232
  ;; COMMENT is a comment string.
 
1233
  ;; KEEP? is either #F, #T, or LOCK.
 
1234
  ;;   #F means don't keep a copy of WORKFILE after registration.
 
1235
  ;;   #T means keep an unlocked copy.
 
1236
  ;;   LOCK means keep a locked copy.
 
1237
  ;; On return, WORKFILE must be registered.
 
1238
  ((vc-type-operation
 
1239
    (if (and (pair? vc-types)
 
1240
             (null? (cdr vc-types)))
 
1241
        (car vc-types)
 
1242
        (let ((likely-types
 
1243
               (list-transform-positive vc-types
 
1244
                 (lambda (type)
 
1245
                   ((vc-type-operation type 'LIKELY-CONTROL-TYPE?)
 
1246
                    workfile)))))
 
1247
          (if (and (pair? likely-types)
 
1248
                   (null? (cdr likely-types)))
 
1249
              (car likely-types)
 
1250
              (cleanup-pop-up-buffers
 
1251
               (lambda ()
 
1252
                 (call-with-output-to-temporary-buffer " *VC-types*"
 
1253
                                                       '(SHRINK-WINDOW)
 
1254
                   (lambda (port)
 
1255
                     (for-each
 
1256
                      (lambda (type)
 
1257
                        (write-string (vc-type-display-name type) port)
 
1258
                        (newline port))
 
1259
                      vc-types)))
 
1260
                 (prompt-for-alist-value
 
1261
                  "Version control type"
 
1262
                  (map (lambda (type)
 
1263
                         (cons (vc-type-display-name type)
 
1264
                               type))
 
1265
                       vc-types)
 
1266
                  #f
 
1267
                  #f))))))
 
1268
    'REGISTER)
 
1269
   workfile revision comment keep?))
 
1270
 
 
1271
(define (vc-backend-checkout master revision lock? workfile)
 
1272
  ;; MASTER is a valid VC-MASTER object.
 
1273
  ;; REVISION is either a revision string or #F.
 
1274
  ;; LOCK? is a boolean saying whether to lock the specified revision.
 
1275
  ;;   This has effect only with backends that do locking.
 
1276
  ;; WORKFILE is either an absolute pathname or #F.
 
1277
  ;;   If #F, the file is checked out into the workfile pathname of MASTER.
 
1278
  ;;   Otherwise, the file is checked out into WORKFILE.
 
1279
  (vc-call 'CHECKOUT master revision lock?
 
1280
           (and workfile
 
1281
                (not (pathname=? workfile (vc-master-workfile master)))
 
1282
                workfile)))
 
1283
 
 
1284
(define (vc-backend-checkin master revision comment keep?)
 
1285
  ;; MASTER is a valid VC-MASTER object.
 
1286
  ;; REVISION is either a revision string or #F.
 
1287
  ;; COMMENT is a comment string.
 
1288
  ;; KEEP? is a boolean specifying that the workfile should be kept
 
1289
  ;;   after checking in.  If #F, the workfile is deleted.
 
1290
  ;; The workfile is checked in.
 
1291
  (vc-call 'CHECKIN master revision comment keep?))
 
1292
 
 
1293
(define (vc-backend-revert master)
 
1294
  ;; MASTER is a valid VC-MASTER object.
 
1295
  ;; The workfile is checked out, discarding the existing workfile.
 
1296
  (vc-call 'REVERT master))
 
1297
 
 
1298
(define (vc-backend-steal master revision)
 
1299
  ;; MASTER is a valid VC-MASTER object.
 
1300
  ;; REVISION is either a revision string or #F.
 
1301
  ;; The lock is stolen from the owner without notification.
 
1302
  (vc-call 'STEAL master revision))
 
1303
 
 
1304
(define (vc-backend-diff master rev1 rev2 simple?)
 
1305
  ;; MASTER is a valid VC-MASTER object.
 
1306
  ;; REV1 is either a revision string or #F.
 
1307
  ;; REV2 is either a revision string or #F.
 
1308
  ;;   If REV1 and REV2 are both #F, the workfile is compared to its
 
1309
  ;;     most recent checked-in revision.
 
1310
  ;;   If REV1 nor REV2 is #F, the specified revisions are compared.
 
1311
  ;;   Otherwise, the workfile is compared to the specified revision.
 
1312
  ;; SIMPLE? is a boolean specifying how the comparison is performed.
 
1313
  ;;   If #T, only the result of the comparison is interesting.
 
1314
  ;;   If #F, the differences are to be shown to the user.
 
1315
  (vc-call 'DIFF master rev1 rev2 simple?))
 
1316
 
 
1317
(define (vc-backend-print-log master)
 
1318
  ;; MASTER is a valid VC-MASTER object.
 
1319
  ;; The log associated with that file is popped up in another buffer.
 
1320
  (vc-call 'PRINT-LOG master))
 
1321
 
 
1322
(define (vc-backend-check-log-entry master log-buffer)
 
1323
  ;; MASTER is a valid VC-MASTER object.
 
1324
  ;; LOG-BUFFER is a buffer containing a log message.
 
1325
  ;; The buffer's contents is checked for compatibility with the
 
1326
  ;;   backend, and an error is signalled if it is incompatible.
 
1327
  (vc-call 'CHECK-LOG-ENTRY master log-buffer))
 
1328
 
 
1329
(define (vc-backend-check-headers master buffer)
 
1330
  ;; MASTER is a valid VC-MASTER object.
 
1331
  ;; BUFFER is the workfile buffer.
 
1332
  ;; Examines the buffer contents to determine if they contain
 
1333
  ;; appropriate revision-control header strings.  Returns #t iff the
 
1334
  ;; header strings are present.
 
1335
  (vc-call 'CHECK-HEADERS master buffer))
 
1336
 
 
1337
;;;; RCS Commands
 
1338
 
 
1339
(define vc-type:rcs
 
1340
  ;; Splitting up string constant prevents RCS from expanding this
 
1341
  ;; keyword.
 
1342
  (make-vc-type 'RCS "RCS" "\$Id\$"))
 
1343
 
 
1344
(define (rcs-master? master)
 
1345
  (eq? vc-type:rcs (vc-master-type master)))
 
1346
 
 
1347
(define (rcs-directory workfile)
 
1348
  (subdirectory-pathname workfile "RCS"))
 
1349
 
 
1350
(define (get-rcs-admin master)
 
1351
  (let ((pathname (vc-master-pathname master)))
 
1352
    (read-cached-value-1 master 'RCS-ADMIN pathname
 
1353
                         (lambda (time) time (parse-rcs-admin pathname)))))
 
1354
 
 
1355
(define (check-rcs-headers buffer)
 
1356
  (re-search-forward (string-append "\\$[A-Za-z\300-\326\330-\366\370-\377]+"
 
1357
                                    "\\(: [\t -#%-\176\240-\377]*\\)?\\$")
 
1358
                     (buffer-start buffer)
 
1359
                     (buffer-end buffer)))
 
1360
 
 
1361
(define (rcs-rev-switch switch revision)
 
1362
  (if revision
 
1363
      (string-append switch revision)
 
1364
      switch))
 
1365
 
 
1366
(define (rcs-mtime-switch master)
 
1367
  (and (ref-variable vc-rcs-preserve-mod-times
 
1368
                     (pathname->buffer (->workfile master)))
 
1369
       "-M"))
 
1370
 
 
1371
(define-vc-type-operation 'RELEASE vc-type:rcs
 
1372
  (lambda ()
 
1373
    (and (= 0 (vc-run-command #f '() "rcs" "-V"))
 
1374
         (re-search-forward "^RCS version \\([0-9.]+ *.*\\)"
 
1375
                            (buffer-start (get-vc-command-buffer)))
 
1376
         (extract-string (re-match-start 1) (re-match-end 1)))))
 
1377
 
 
1378
(define-vc-type-operation 'FIND-MASTER vc-type:rcs
 
1379
  (lambda (workfile)
 
1380
    (let ((try
 
1381
           (lambda (transform)
 
1382
             (let ((master-file (transform workfile)))
 
1383
               (and (file-exists? master-file)
 
1384
                    (make-vc-master vc-type:rcs master-file workfile)))))
 
1385
          (in-rcs-directory
 
1386
           (lambda (pathname)
 
1387
             (merge-pathnames (file-pathname pathname)
 
1388
                              (rcs-directory pathname))))
 
1389
          (rcs-file
 
1390
           (lambda (pathname)
 
1391
             (merge-pathnames (string-append (file-namestring pathname) ",v")
 
1392
                              (directory-pathname pathname)))))
 
1393
      (or (try (lambda (workfile) (rcs-file (in-rcs-directory workfile))))
 
1394
          (try in-rcs-directory)
 
1395
          (try rcs-file)))))
 
1396
 
 
1397
(define-vc-type-operation 'VALID? vc-type:rcs
 
1398
  (lambda (master)
 
1399
    (file-exists? (vc-master-pathname master))))
 
1400
 
 
1401
(define-vc-type-operation 'DEFAULT-REVISION vc-type:rcs
 
1402
  (lambda (master error?)
 
1403
    (let ((delta (rcs-find-delta (get-rcs-admin master) #f error?)))
 
1404
      (and delta
 
1405
           (rcs-delta/number delta)))))
 
1406
 
 
1407
(define-vc-type-operation 'WORKFILE-REVISION vc-type:rcs
 
1408
  (lambda (master)
 
1409
    (let ((workfile (vc-master-workfile master)))
 
1410
      (read-cached-value-1 master 'RCS-WORKFILE-REVISION workfile
 
1411
        (lambda (time)
 
1412
          time
 
1413
          (let ((parse-buffer
 
1414
                 (lambda (buffer)
 
1415
                   (let ((start (buffer-start buffer))
 
1416
                         (end (buffer-end buffer)))
 
1417
                     (let ((find-keyword
 
1418
                            (lambda (keyword)
 
1419
                              (let ((mark
 
1420
                                     (search-forward
 
1421
                                      (string-append "$" keyword ":")
 
1422
                                      start end #f)))
 
1423
                                (and mark
 
1424
                                     (skip-chars-forward " " mark end #f)))))
 
1425
                           (get-revision
 
1426
                            (lambda (start)
 
1427
                              (let ((end
 
1428
                                     (skip-chars-forward "0-9." start end)))
 
1429
                                (and (mark< start end)
 
1430
                                     (let ((revision
 
1431
                                            (extract-string start end)))
 
1432
                                       (let ((length
 
1433
                                              (rcs-number-length revision)))
 
1434
                                         (and (> length 2)
 
1435
                                              (even? length)
 
1436
                                              (rcs-number-head revision
 
1437
                                                               (- length 1)
 
1438
                                                               #f)))))))))
 
1439
                       (cond ((or (find-keyword "Id") (find-keyword "Header"))
 
1440
                              => (lambda (mark)
 
1441
                                   (get-revision
 
1442
                                    (skip-chars-forward
 
1443
                                     " "
 
1444
                                     (skip-chars-forward "^ " mark end)
 
1445
                                     end))))
 
1446
                             ((find-keyword "Revision") => get-revision)
 
1447
                             (else #f)))))))
 
1448
            (let ((buffer (pathname->buffer workfile)))
 
1449
              (if buffer
 
1450
                  (parse-buffer buffer)
 
1451
                  (call-with-temporary-buffer " *VC-temp*"
 
1452
                    (lambda (buffer)
 
1453
                      (catch-file-errors (lambda (condition) condition #f)
 
1454
                        (lambda ()
 
1455
                          (read-buffer buffer workfile #f)
 
1456
                          (parse-buffer buffer)))))))))))))
 
1457
 
 
1458
(define-vc-type-operation 'LOCKING-USER vc-type:rcs
 
1459
  (lambda (master revision)
 
1460
    (let ((admin (get-rcs-admin master)))
 
1461
      (let ((delta
 
1462
             (rcs-find-delta admin
 
1463
                             (or revision
 
1464
                                 (vc-backend-workfile-revision master))
 
1465
                             #f)))
 
1466
        (and delta
 
1467
             (let loop ((locks (rcs-admin/locks admin)))
 
1468
               (and (not (null? locks))
 
1469
                    (if (eq? delta (cdar locks))
 
1470
                        (caar locks)
 
1471
                        (loop (cdr locks))))))))))
 
1472
 
 
1473
(define-vc-type-operation 'LIKELY-CONTROL-TYPE? vc-type:rcs
 
1474
  (lambda (workfile)
 
1475
    (file-directory? (rcs-directory workfile))))
 
1476
 
 
1477
(define-vc-type-operation 'REGISTER vc-type:rcs
 
1478
  (lambda (workfile revision comment keep?)
 
1479
    (with-vc-command-message workfile "Registering"
 
1480
      (lambda ()
 
1481
        (vc-run-command workfile '() "ci"
 
1482
                        (and (vc-release? vc-type:rcs "5.6.4") "-i")
 
1483
                        (rcs-rev-switch (cond ((not keep?) "-r")
 
1484
                                              ((eq? 'LOCK keep?) "-l")
 
1485
                                              (else "-u"))
 
1486
                                        revision)
 
1487
                        (rcs-mtime-switch workfile)
 
1488
                        (string-append "-t-" comment)
 
1489
                        workfile)))))
 
1490
 
 
1491
(define-vc-type-operation 'CHECKOUT vc-type:rcs
 
1492
  (lambda (master revision lock? workfile)
 
1493
    (let ((revision (or revision (vc-backend-workfile-revision master))))
 
1494
      (with-vc-command-message master "Checking out"
 
1495
        (lambda ()
 
1496
          (if workfile
 
1497
              ;; RCS makes it difficult to check a file out into anything
 
1498
              ;; but the working file.
 
1499
              (begin
 
1500
                (delete-file-no-errors workfile)
 
1501
                (vc-run-shell-command master '() "co"
 
1502
                                      (rcs-rev-switch "-p" revision)
 
1503
                                      (vc-master-workfile master)
 
1504
                                      ">"
 
1505
                                      workfile)
 
1506
                (set-file-modes! workfile (if lock? #o644 #o444)))
 
1507
              (vc-run-command master '() "co"
 
1508
                              (rcs-rev-switch (if lock? "-l" "-r") revision)
 
1509
                              (rcs-mtime-switch master)
 
1510
                              (vc-master-workfile master))))))))
 
1511
 
 
1512
(define-vc-type-operation 'CHECKIN vc-type:rcs
 
1513
  (lambda (master revision comment keep?)
 
1514
    (with-vc-command-message master "Checking in"
 
1515
      (lambda ()
 
1516
        (vc-run-command master '() "ci"
 
1517
                        ;; If available, use the secure check-in option.
 
1518
                        (and (vc-release? vc-type:rcs "5.6.4") "-j")
 
1519
                        (rcs-rev-switch (if keep? "-u" "-r") revision)
 
1520
                        (rcs-mtime-switch master)
 
1521
                        (string-append "-m" comment)
 
1522
                        (vc-master-workfile master))))))
 
1523
 
 
1524
(define-vc-type-operation 'REVERT vc-type:rcs
 
1525
  (lambda (master)
 
1526
    (with-vc-command-message master "Reverting"
 
1527
      (lambda ()
 
1528
        (vc-run-command master '() "co"
 
1529
                        "-f" "-u"
 
1530
                        (rcs-mtime-switch master)
 
1531
                        (vc-master-workfile master))))))
 
1532
 
 
1533
(define-vc-type-operation 'STEAL vc-type:rcs
 
1534
  (lambda (master revision)
 
1535
    (if (not (vc-release? vc-type:rcs "5.6.2"))
 
1536
        (error "Unable to steal locks with this version of RCS."))
 
1537
    (let ((revision (or revision (vc-backend-workfile-revision master))))
 
1538
      (with-vc-command-message master "Stealing lock on"
 
1539
        (lambda ()
 
1540
          (vc-run-command master '() "rcs"
 
1541
                          "-M"
 
1542
                          (rcs-rev-switch "-u" revision)
 
1543
                          (rcs-rev-switch "-l" revision)
 
1544
                          (vc-master-workfile master)))))))
 
1545
 
 
1546
(define-vc-type-operation 'DIFF vc-type:rcs
 
1547
  (lambda (master rev1 rev2 simple?)
 
1548
    (= 1
 
1549
       (vc-run-command master
 
1550
                       `((STATUS 1)
 
1551
                         (BUFFER ,(get-vc-diff-buffer simple?)))
 
1552
                       "rcsdiff"
 
1553
                       "-q"
 
1554
                       (if (and rev1 rev2)
 
1555
                           (list (string-append "-r" rev1)
 
1556
                                 (string-append "-r" rev2))
 
1557
                           (let ((rev
 
1558
                                  (or rev1 rev2
 
1559
                                      (vc-backend-workfile-revision master))))
 
1560
                             (and rev
 
1561
                                  (string-append "-r" rev))))
 
1562
                       (if simple?
 
1563
                           (and (diff-brief-available?) "--brief")
 
1564
                           (ref-variable diff-switches
 
1565
                                         (vc-workfile-buffer master #f)))
 
1566
                       (vc-master-workfile master)))))
 
1567
 
 
1568
(define-vc-type-operation 'PRINT-LOG vc-type:rcs
 
1569
  (lambda (master)
 
1570
    (vc-run-command master '() "rlog" (vc-master-workfile master))))
 
1571
 
 
1572
(define-vc-type-operation 'CHECK-LOG-ENTRY vc-type:rcs
 
1573
  (lambda (master log-buffer)
 
1574
    master log-buffer
 
1575
    unspecific))
 
1576
 
 
1577
(define-vc-type-operation 'CHECK-HEADERS vc-type:rcs
 
1578
  (lambda (master buffer)
 
1579
    master
 
1580
    (check-rcs-headers buffer)))
 
1581
 
 
1582
;;;; CVS Commands
 
1583
 
 
1584
(define vc-type:cvs
 
1585
  (make-vc-type 'CVS "CVS" "\$Id\$"))
 
1586
 
 
1587
(define (cvs-master? master)
 
1588
  (eq? vc-type:cvs (vc-master-type master)))
 
1589
 
 
1590
(define (find-cvs-master workfile)
 
1591
  (let ((entries-file (merge-pathnames "Entries" (cvs-directory workfile))))
 
1592
    (and (%find-cvs-entry entries-file workfile)
 
1593
         (make-vc-master vc-type:cvs entries-file workfile))))
 
1594
 
 
1595
(define (cvs-directory workfile)
 
1596
  (subdirectory-pathname workfile "CVS"))
 
1597
 
 
1598
(define (get-cvs-workfile-revision master error?)
 
1599
  (let ((tokens (find-cvs-entry master)))
 
1600
    (if tokens
 
1601
        (cadr tokens)
 
1602
        (and error?
 
1603
             (error "Workfile has no version:" (vc-master-workfile master))))))
 
1604
 
 
1605
(define (find-cvs-entry master)
 
1606
  (let ((pathname (vc-master-pathname master)))
 
1607
    (read-cached-value-1 master 'CVS-ENTRY pathname
 
1608
      (lambda (time)
 
1609
        time
 
1610
        (%find-cvs-entry pathname (vc-master-workfile master))))))
 
1611
 
 
1612
(define (%find-cvs-entry pathname workfile)
 
1613
  (let ((line
 
1614
         (find-cvs-line pathname
 
1615
                        (string-append "/" (file-namestring workfile) "/"))))
 
1616
    (and line
 
1617
         (let ((tokens (cdr (burst-string line #\/ #f))))
 
1618
           (and (fix:= 5 (length tokens))
 
1619
                tokens)))))
 
1620
 
 
1621
(define (cvs-workfile-protected? workfile)
 
1622
  (string-prefix? "-r-"
 
1623
                  (file-attributes/mode-string (file-attributes workfile))))
 
1624
 
 
1625
(define (cvs-file-edited? master)
 
1626
  (let ((pathname
 
1627
         (merge-pathnames "Baserev"
 
1628
                          (directory-pathname (vc-master-pathname master)))))
 
1629
    (read-cached-value-1 master 'CVS-FILE-EDITED? pathname
 
1630
      (lambda (time)
 
1631
        time
 
1632
        (find-cvs-line pathname
 
1633
                       (string-append
 
1634
                        "B"
 
1635
                        (file-namestring (vc-master-workfile master))
 
1636
                        "/"))))))
 
1637
 
 
1638
(define (find-cvs-line pathname prefix)
 
1639
  (and (file-readable? pathname)
 
1640
       (call-with-input-file pathname
 
1641
         (lambda (port)
 
1642
           (let loop ()
 
1643
             (let ((line (read-line port)))
 
1644
               (and (not (eof-object? line))
 
1645
                    (if (string-prefix? prefix line)
 
1646
                        line
 
1647
                        (loop)))))))))
 
1648
 
 
1649
(define (cvs-status master)
 
1650
  (get-cvs-status master
 
1651
    (lambda (m)
 
1652
      (if (re-search-forward "^File: [^ \t]+[ \t]+Status: \\(.*\\)" m)
 
1653
          (convert-cvs-status
 
1654
           (extract-string (re-match-start 1) (re-match-end 1)))
 
1655
          'UNKNOWN))))
 
1656
 
 
1657
(define (cvs-default-revision master)
 
1658
  (get-cvs-status master
 
1659
    (lambda (m)
 
1660
      (and (re-search-forward
 
1661
            "\\(RCS Version\\|RCS Revision\\|Repository revision\\):[ \t]+\\([0-9.]+\\)"
 
1662
            m)
 
1663
           (extract-string (re-match-start 2) (re-match-end 2))))))
 
1664
 
 
1665
(define (get-cvs-status master parse-output)
 
1666
  (vc-run-command master
 
1667
                  `((BUFFER " *vc-status*"))
 
1668
                  "cvs" "status"
 
1669
                  (file-pathname (vc-master-workfile master)))
 
1670
  (parse-output (buffer-start (find-or-create-buffer " *vc-status*"))))
 
1671
 
 
1672
(define (convert-cvs-status status)
 
1673
  (cond ((string-ci=? status "Up-to-date")
 
1674
         'UP-TO-DATE)
 
1675
        ((string-ci=? status "Locally Modified")
 
1676
         'LOCALLY-MODIFIED)
 
1677
        ((or (string-ci=? status "Locally Added")
 
1678
             (string-ci=? status "New file!"))
 
1679
         'LOCALLY-ADDED)
 
1680
        ((string-ci=? status "Locally Removed")
 
1681
         'LOCALLY-REMOVED)
 
1682
        ((or (string-ci=? status "Needs Checkout")
 
1683
             (string-ci=? status "Needs Patch"))
 
1684
         'NEEDS-CHECKOUT)
 
1685
        ((string-ci=? status "Needs Merge")
 
1686
         'NEEDS-MERGE)
 
1687
        ((or (string-ci=? status "File had conflicts on merge")
 
1688
             (string-ci=? status "Unresolved Conflict"))
 
1689
         'UNRESOLVED-CONFLICT)
 
1690
        (else
 
1691
         'UNKNOWN)))
 
1692
 
 
1693
(define (cvs-rev-switch revision)
 
1694
  (and revision
 
1695
       (list "-r" revision)))
 
1696
 
 
1697
(define-vc-type-operation 'RELEASE vc-type:cvs
 
1698
  (lambda ()
 
1699
    (and (= 0 (vc-run-command #f '() "cvs" "-v"))
 
1700
         (re-search-forward "^Concurrent Versions System (CVS) \\([0-9.]+\\)"
 
1701
                            (buffer-start (get-vc-command-buffer)))
 
1702
         (extract-string (re-match-start 1) (re-match-end 1)))))
 
1703
 
 
1704
(define-vc-type-operation 'FIND-MASTER vc-type:cvs
 
1705
  (lambda (workfile)
 
1706
    (find-cvs-master workfile)))
 
1707
 
 
1708
(define-vc-type-operation 'VALID? vc-type:cvs
 
1709
  (lambda (master)
 
1710
    (get-cvs-workfile-revision master #f)))
 
1711
 
 
1712
(define-vc-type-operation 'DEFAULT-REVISION vc-type:cvs
 
1713
  (lambda (master error?)
 
1714
    (or (cvs-default-revision master)
 
1715
        (and error?
 
1716
             (error "Unable to determine default CVS version:"
 
1717
                    (vc-master-workfile master))))))
 
1718
 
 
1719
(define-vc-type-operation 'WORKFILE-REVISION vc-type:cvs
 
1720
  (lambda (master)
 
1721
    (get-cvs-workfile-revision master #t)))
 
1722
 
 
1723
(define-vc-type-operation 'LOCKING-USER vc-type:cvs
 
1724
  (lambda (master revision)
 
1725
    ;; The workfile is "locked" if it is modified.
 
1726
    ;; We consider the workfile's owner to be the locker.
 
1727
    (and (or (not revision)
 
1728
             (equal? revision (vc-backend-workfile-revision master)))
 
1729
         (or (not
 
1730
              (let ((t1 (file-modification-time (vc-master-workfile master)))
 
1731
                    (t2 (vc-cvs-workfile-mtime-string master)))
 
1732
                (and t1 t2
 
1733
                     (string=? (file-time->global-ctime-string t1) t2))))
 
1734
             (cvs-file-edited? master))
 
1735
         (unix/uid->string
 
1736
          (file-attributes/uid
 
1737
           (file-attributes (vc-master-workfile master)))))))
 
1738
 
 
1739
(define-vc-type-operation 'LIKELY-CONTROL-TYPE? vc-type:cvs
 
1740
  (lambda (workfile)
 
1741
    (file-directory? (cvs-directory workfile))))
 
1742
 
 
1743
(define-vc-type-operation 'REGISTER vc-type:cvs
 
1744
  (lambda (workfile revision comment keep?)
 
1745
    revision keep?                      ;always keep file.
 
1746
    (with-vc-command-message workfile "Registering"
 
1747
      (lambda ()
 
1748
        (vc-run-command workfile '() "cvs" "add"
 
1749
                        "-m" comment
 
1750
                        (file-pathname workfile))))))
 
1751
 
 
1752
(define-vc-type-operation 'CHECKOUT vc-type:cvs
 
1753
  (lambda (master revision lock? workfile)
 
1754
    (let ((workfile* (file-pathname (vc-master-workfile master))))
 
1755
      (with-vc-command-message master "Checking out"
 
1756
        (lambda ()
 
1757
          (cond (workfile
 
1758
                 ;; CVS makes it difficult to check a file out into
 
1759
                 ;; anything but the working file.
 
1760
                 (delete-file-no-errors workfile)
 
1761
                 (vc-run-shell-command master '() "cvs" "update" "-p"
 
1762
                                       (cvs-rev-switch revision)
 
1763
                                       workfile*
 
1764
                                       ">"
 
1765
                                       workfile))
 
1766
                (revision
 
1767
                 (vc-run-command master '() "cvs" (and lock? "-w") "update"
 
1768
                                 (cvs-rev-switch revision)
 
1769
                                 workfile*))
 
1770
                (else
 
1771
                 (vc-run-command master '() "cvs" "edit" workfile*))))))))
 
1772
 
 
1773
(define-vc-type-operation 'CHECKIN vc-type:cvs
 
1774
  (lambda (master revision comment keep?)
 
1775
    keep?
 
1776
    (with-vc-command-message master "Checking in"
 
1777
      (lambda ()
 
1778
        (bind-condition-handler (list condition-type:editor-error)
 
1779
            (lambda (condition)
 
1780
              condition
 
1781
              (if (eq? 'NEEDS-MERGE (cvs-status master))
 
1782
                  ;; The CVS output will be on top of this message.
 
1783
                  (error "Type C-x 0 C-x C-q to merge in changes.")))
 
1784
          (lambda ()
 
1785
            ;; Explicit check-in to the trunk requires a double check-in
 
1786
            ;; (first unexplicit) (CVS-1.3).  [This is copied from Emacs
 
1787
            ;; 20.6, but I don't understand it. -- CPH]
 
1788
            (if (and revision
 
1789
                     (not (equal? revision
 
1790
                                  (vc-backend-workfile-revision master)))
 
1791
                     (trunk-revision? revision))
 
1792
                (vc-run-command master '() "cvs" "commit"
 
1793
                                "-m" "#intermediate"
 
1794
                                (file-pathname (vc-master-workfile master))))
 
1795
            (vc-run-command master '() "cvs" "commit"
 
1796
                            (cvs-rev-switch revision)
 
1797
                            "-m" comment
 
1798
                            (file-pathname (vc-master-workfile master)))))
 
1799
        ;; If this was an explicit check-in, remove the sticky tag.
 
1800
        (if revision
 
1801
            (vc-run-command master '() "cvs" "update" "-A"
 
1802
                            (file-pathname (vc-master-workfile master))))))))
 
1803
 
 
1804
(define-vc-type-operation 'REVERT vc-type:cvs
 
1805
  (lambda (master)
 
1806
    (with-vc-command-message master "Reverting"
 
1807
      (lambda ()
 
1808
        (let ((workfile (vc-master-workfile master)))
 
1809
          (if (cvs-file-edited? master)
 
1810
              (vc-run-command master '() "cvs" "unedit"
 
1811
                              (file-pathname workfile))
 
1812
              (begin
 
1813
                (delete-file-no-errors workfile)
 
1814
                (vc-run-command master '() "cvs" "update"
 
1815
                                (file-pathname workfile)))))))))
 
1816
 
 
1817
(define-vc-type-operation 'STEAL vc-type:cvs
 
1818
  (lambda (master revision)
 
1819
    master revision
 
1820
    (error "You cannot steal a CVS lock; there are no CVS locks to steal.")))
 
1821
 
 
1822
(define-vc-type-operation 'DIFF vc-type:cvs
 
1823
  (lambda (master rev1 rev2 simple?)
 
1824
    (let ((options
 
1825
           `((STATUS 1)
 
1826
             (BUFFER ,(get-vc-diff-buffer simple?)))))
 
1827
      (if (equal? "0" (vc-backend-workfile-revision master))
 
1828
          ;; This file is added but not yet committed; there is no
 
1829
          ;; master file.
 
1830
          (begin
 
1831
            (if (or rev1 rev2)
 
1832
                (error "No revisions exist:" (vc-master-workfile master)))
 
1833
            (if simple?
 
1834
                ;; File is added but not committed; we regard this as
 
1835
                ;; "changed".
 
1836
                #t
 
1837
                ;; Diff against /dev/null.
 
1838
                (= 1
 
1839
                   (vc-run-command master options "diff"
 
1840
                                   (ref-variable diff-switches
 
1841
                                                 (vc-workfile-buffer master
 
1842
                                                                     #f))
 
1843
                                   "/dev/null"
 
1844
                                   (file-pathname
 
1845
                                    (vc-master-workfile master))))))
 
1846
          (= 1
 
1847
             (vc-run-command master options "cvs" "diff"
 
1848
                             (if simple?
 
1849
                                 (and (diff-brief-available?) "--brief")
 
1850
                                 (ref-variable diff-switches
 
1851
                                               (vc-workfile-buffer master #f)))
 
1852
                             (and rev1 (string-append "-r" rev1))
 
1853
                             (and rev2 (string-append "-r" rev2))
 
1854
                             (file-pathname (vc-master-workfile master))))))))
 
1855
 
 
1856
(define-vc-type-operation 'PRINT-LOG vc-type:cvs
 
1857
  (lambda (master)
 
1858
    (vc-run-command master '() "cvs" "log"
 
1859
                    (file-pathname (vc-master-workfile master)))))
 
1860
 
 
1861
(define-vc-type-operation 'CHECK-LOG-ENTRY vc-type:cvs
 
1862
  (lambda (master log-buffer)
 
1863
    master log-buffer
 
1864
    unspecific))
 
1865
 
 
1866
(define-vc-type-operation 'CHECK-HEADERS vc-type:cvs
 
1867
  (lambda (master buffer)
 
1868
    master
 
1869
    (check-rcs-headers buffer)))
 
1870
 
 
1871
(define (cvs-backend-merge-news master)
 
1872
  (with-vc-command-message master "Merging changes into"
 
1873
    (lambda ()
 
1874
      (let ((workfile (vc-master-workfile master)))
 
1875
        (vc-run-command master '() "cvs" "update" (file-pathname workfile))
 
1876
        (let ((buffer (get-vc-command-buffer))
 
1877
              (fn (re-quote-string (file-namestring workfile))))
 
1878
          (cond ((re-search-forward
 
1879
                  (string-append "^\\([CMUP]\\) " fn)
 
1880
                  (buffer-start buffer))
 
1881
                 (char=? #\C (extract-right-char (re-match-start 0))))
 
1882
                ((re-search-forward
 
1883
                  (string-append fn
 
1884
                                 " already contains the differences between ")
 
1885
                  (buffer-start buffer))
 
1886
                 ;; Special case: file contents in sync with repository
 
1887
                 ;; anyhow:
 
1888
                 #f)
 
1889
                (else
 
1890
                 (pop-up-buffer buffer #f)
 
1891
                 (error "Couldn't analyze cvs update result."))))))))
 
1892
 
 
1893
;;;; Command Execution
 
1894
 
 
1895
(define (vc-run-command master options command . arguments)
 
1896
  (let ((workfile (and master (->workfile master)))
 
1897
        (option
 
1898
         (lambda (name default)
 
1899
           (let ((option (assq name options)))
 
1900
             (if option
 
1901
                 (cadr option)
 
1902
                 (default))))))
 
1903
    (let ((command-messages?
 
1904
           (ref-variable vc-command-messages
 
1905
                         (and workfile (pathname->buffer workfile))))
 
1906
          (msg
 
1907
           (string-append "Running " command
 
1908
                          (if master
 
1909
                              (string-append " on " (->namestring workfile))
 
1910
                              "")
 
1911
                          "..."))
 
1912
          (status-limit (option 'STATUS (lambda () 0)))
 
1913
          (directory
 
1914
           (option 'DIRECTORY
 
1915
                   (if workfile
 
1916
                       (lambda () (directory-pathname workfile))
 
1917
                       working-directory-pathname)))
 
1918
          (command-buffer
 
1919
           (let ((buffer (option 'BUFFER get-vc-command-buffer)))
 
1920
             (cond ((buffer? buffer) buffer)
 
1921
                   ((string? buffer) (find-or-create-buffer buffer))
 
1922
                   (else (error "Illegal buffer:" buffer))))))
 
1923
      (if command-messages? (message msg))
 
1924
      (buffer-reset! command-buffer)
 
1925
      (bury-buffer command-buffer)
 
1926
      (set-buffer-default-directory! command-buffer directory)
 
1927
      (let ((result
 
1928
             (apply run-synchronous-process
 
1929
                    #f
 
1930
                    (buffer-end command-buffer)
 
1931
                    directory
 
1932
                    #f
 
1933
                    (os/find-program command directory
 
1934
                                     (ref-variable exec-path command-buffer))
 
1935
                    (vc-command-arguments arguments))))
 
1936
        (if (and (eq? 'EXITED (car result))
 
1937
                 (<= 0 (cdr result) status-limit))
 
1938
            (begin
 
1939
              (if command-messages? (message msg "done"))
 
1940
              (cdr result))
 
1941
            (begin
 
1942
              (pop-up-vc-command-buffer #f)
 
1943
              (editor-error msg "...FAILED "
 
1944
                            (list (car result) (cdr result)))))))))
 
1945
 
 
1946
(define (vc-command-arguments arguments)
 
1947
  (append-map (lambda (argument)
 
1948
                (cond ((not argument) '())
 
1949
                      ((string? argument) (list argument))
 
1950
                      ((pathname? argument) (list (->namestring argument)))
 
1951
                      ((list? argument) (vc-command-arguments argument))
 
1952
                      (else (error "Ill-formed command argument:" argument))))
 
1953
              arguments))
 
1954
 
 
1955
(define (vc-run-shell-command master options command . arguments)
 
1956
  (vc-run-command master options "/bin/sh" "-c"
 
1957
                  (reduce string-append-separated
 
1958
                          ""
 
1959
                          (vc-command-arguments (cons command arguments)))))
 
1960
 
 
1961
(define (pop-up-vc-command-buffer select?)
 
1962
  (let ((buffer (get-vc-command-buffer)))
 
1963
    (set-buffer-point! buffer (buffer-start buffer))
 
1964
    (pop-up-buffer buffer select?)))
 
1965
 
 
1966
(define (get-vc-command-buffer)
 
1967
  (find-or-create-buffer "*vc*"))
 
1968
 
 
1969
(define (pop-up-vc-diff-buffer select?)
 
1970
  (let ((buffer (get-vc-diff-buffer #f)))
 
1971
    (set-buffer-point! buffer (buffer-start buffer))
 
1972
    (pop-up-buffer buffer select?)))
 
1973
 
 
1974
(define (get-vc-diff-buffer simple?)
 
1975
  (find-or-create-buffer (if simple? " *vc-diff*" "*vc-diff*")))
 
1976
 
 
1977
(define (with-vc-command-message master operation thunk)
 
1978
  (let ((msg
 
1979
         (string-append operation " " (->namestring (->workfile master))
 
1980
                        "...")))
 
1981
    (message msg)
 
1982
    (thunk)
 
1983
    (message msg "done")))
 
1984
 
 
1985
;;;; Release/Revision numbers
 
1986
 
 
1987
(define (vc-release? type release)
 
1988
  (let ((release* (vc-backend-release type)))
 
1989
    (and release*
 
1990
         (release<=? release release*))))
 
1991
 
 
1992
(define (release<=? r1 r2)
 
1993
  ;; Compare release numbers, represented as strings.
 
1994
  ;; Release components are assumed cardinal numbers, not decimal
 
1995
  ;; fractions (5.10 is a higher release than 5.9).  Omitted fields
 
1996
  ;; are considered lower (5.6.7 is earlier than 5.6.7.1).
 
1997
  ;; Comparison runs till the end of the string is found, or a
 
1998
  ;; non-numeric component shows up (5.6.7 is earlier than "5.6.7 beta",
 
1999
  ;; which is probably not what you want in some cases).
 
2000
  ;;   This code is suitable for existing RCS release numbers.  
 
2001
  ;; CVS releases are handled reasonably, too (1.3 < 1.4* < 1.5).
 
2002
  (let ((t1 (burst-string r1 #\space #t))
 
2003
        (t2 (burst-string r2 #\space #t)))
 
2004
    (let loop
 
2005
        ((ns1 (burst-string (car t1) #\. #f))
 
2006
         (ns2 (burst-string (car t2) #\. #f)))
 
2007
      (if (pair? ns1)
 
2008
          (and (pair? ns2)
 
2009
               (let ((n1 (string->number (car ns1)))
 
2010
                     (n2 (string->number (car ns2))))
 
2011
                 (or (< n1 n2)
 
2012
                     (and (= n1 n2)
 
2013
                          (loop (cdr ns1) (cdr ns2))))))
 
2014
          (or (pair? ns2)
 
2015
              (not (pair? (cdr t1)))
 
2016
              (pair? (cdr t2)))))))
 
2017
 
 
2018
(define (trunk-revision? revision)
 
2019
  (re-string-match "\\`[0-9]+\\.[0-9]+\\'" revision))
 
2020
 
 
2021
(define (branch-revision? revision)
 
2022
  (re-string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" revision))
 
2023
 
 
2024
(define (revision-branch-part revision)
 
2025
  (let ((regs (re-string-search-forward "\\.[0-9]+\\'" revision)))
 
2026
    (if (not regs)
 
2027
        (error:bad-range-argument revision 'BRANCH-PART))
 
2028
    (string-head revision (re-match-start-index 0 regs))))
 
2029
 
 
2030
(define (revision-minor-part revision)
 
2031
  (let ((regs (re-string-search-forward "[0-9]+\\'" revision)))
 
2032
    (if (not regs)
 
2033
        (error:bad-range-argument revision 'BRANCH-PART))
 
2034
    (substring revision
 
2035
               (re-match-start-index 0 regs)
 
2036
               (re-match-end-index 0 regs))))
 
2037
 
 
2038
(define (previous-revision revision)
 
2039
  (let ((branch (revision-branch-part revision))
 
2040
        (minor (string->number (revision-minor-part revision))))
 
2041
    (if (> minor 1)
 
2042
        (string-append branch "." (number->string (- minor 1)))
 
2043
        ;; At the first minor number.  If on trunk, no obvious answer.
 
2044
        (and (branch-revision? revision)
 
2045
             (revision-branch-part branch)))))
 
2046
 
 
2047
(define (vc-get-revision revision? prompt)
 
2048
  (and revision?
 
2049
       (vc-normalize-revision (prompt-for-string prompt #f))))
 
2050
 
 
2051
(define (vc-normalize-revision revision)
 
2052
  (and revision
 
2053
       (not (string-null? revision))
 
2054
       revision))
 
2055
 
 
2056
;;;; Utilities
 
2057
 
 
2058
(define (blank-string? string)
 
2059
  (not (string-find-next-char-in-set string char-set:not-whitespace)))
 
2060
 
 
2061
(define (subdirectory-pathname pathname name)
 
2062
  (let ((directory (directory-pathname pathname)))
 
2063
    (pathname-new-directory directory
 
2064
                            (append (pathname-directory directory)
 
2065
                                    (list name)))))
 
2066
 
 
2067
(define (->workfile object)
 
2068
  (cond ((vc-master? object) (vc-master-workfile object))
 
2069
        ((pathname? object) object)
 
2070
        (else (error:wrong-type-argument object "workfile" '->WORKFILE))))
 
2071
 
 
2072
(define (vc-workfile-buffer master find?)
 
2073
  (let ((pathname (vc-master-workfile master)))
 
2074
    (if find?
 
2075
        (find-file-noselect pathname #f)
 
2076
        (pathname->buffer pathname))))
 
2077
 
 
2078
(define (vc-workfile-string master)
 
2079
  (->namestring (vc-master-workfile master)))
 
2080
 
 
2081
(define (vc-save-workfile-buffer workfile)
 
2082
  (let ((buffer (pathname->buffer workfile)))
 
2083
    (if buffer
 
2084
        (vc-save-buffer buffer #t))))
 
2085
 
 
2086
(define (vc-save-buffer buffer error?)
 
2087
  (if (buffer-modified? buffer)
 
2088
      (if (or (ref-variable vc-suppress-confirm buffer)
 
2089
              (prompt-for-confirmation?
 
2090
               (string-append "Buffer " (buffer-name buffer)
 
2091
                              " modified; save it")))
 
2092
          (save-buffer buffer #f)
 
2093
          (if error? (editor-error "Aborted")))))
 
2094
 
 
2095
(define (vc-resync-workfile-buffer workfile keep?)
 
2096
  (let ((buffer (pathname->buffer workfile)))
 
2097
    (if buffer
 
2098
        (if keep?
 
2099
            (vc-revert-buffer buffer #t)
 
2100
            (kill-buffer buffer)))))
 
2101
 
 
2102
(define diff-brief-available?
 
2103
  (let ((result 'UNKNOWN))
 
2104
    (lambda ()
 
2105
      (if (eq? result 'UNKNOWN)
 
2106
          (set! result
 
2107
                (= 0
 
2108
                   (run-synchronous-subprocess
 
2109
                    "diff" '("--brief" "/dev/null" "/dev/null")
 
2110
                    'OUTPUT #F))))
 
2111
      result)))
 
2112
 
 
2113
(define (vc-workfile-modified? master)
 
2114
  (read-cached-value-2 master 'MODIFIED?
 
2115
                       (vc-master-pathname master)
 
2116
                       (vc-master-workfile master)
 
2117
    (lambda (tm tw)
 
2118
      (if (eq? vc-type:cvs (vc-master-type master))
 
2119
          (if (and tm tw
 
2120
                   (let ((ts (vc-cvs-workfile-mtime-string master)))
 
2121
                     (and ts
 
2122
                          (string=? ts (file-time->global-ctime-string tw)))))
 
2123
              #f
 
2124
              (let ((modified? (vc-backend-diff master #f #f #t)))
 
2125
                (set-vc-cvs-workfile-mtime-string! master tm tw modified?)
 
2126
                modified?))
 
2127
          (vc-backend-diff master #f #f #t)))))
 
2128
 
 
2129
(define (vc-cvs-workfile-mtime-string master)
 
2130
  (read-cached-value-2 master 'CVS-MTIME-STRING
 
2131
                       (vc-master-pathname master)
 
2132
                       (vc-master-workfile master)
 
2133
    (lambda (tm tw)
 
2134
      (and tm tw
 
2135
           (let ((entry (find-cvs-entry master)))
 
2136
             (and entry
 
2137
                  (caddr entry)))))))
 
2138
 
 
2139
(define (set-vc-cvs-workfile-mtime-string! master tm tw modified?)
 
2140
  (if (and (not modified?) tm tw (eq? vc-type:cvs (vc-master-type master)))
 
2141
      (begin
 
2142
        ;; This breaks the READ-CACHED-VALUE-2 abstraction:
 
2143
        (vc-master-put! master 'CVS-MTIME-STRING
 
2144
                        (vector (file-time->global-ctime-string tw) tm tw))
 
2145
        (let ((buffer (pathname->buffer (vc-master-workfile master))))
 
2146
          (if buffer
 
2147
              (vc-mode-line master buffer))))))
 
2148
 
 
2149
(define (vc-revert-workfile-buffer master dont-confirm?)
 
2150
  (let ((buffer (vc-workfile-buffer master #f)))
 
2151
    (if buffer
 
2152
        (vc-revert-buffer buffer dont-confirm?))))
 
2153
 
 
2154
(define (vc-revert-buffer buffer dont-confirm?)
 
2155
  ;; Revert BUFFER, try to keep point and mark where user expects them
 
2156
  ;; in spite of changes due to expanded version-control keywords.
 
2157
  (let ((point-contexts
 
2158
         (map (lambda (window)
 
2159
                (list window
 
2160
                      (vc-mark-context (window-point window))
 
2161
                      (vc-mark-context (window-start-mark window))))
 
2162
              (buffer-windows buffer)))
 
2163
        (point-context (vc-mark-context (buffer-point buffer)))
 
2164
        (mark-context (vc-mark-context (buffer-mark buffer))))
 
2165
    (let ((buffer (revert-buffer buffer #t dont-confirm?)))
 
2166
      (update-screens! '(IGNORE-INPUT NO-SCREEN-OUTPUT))
 
2167
      (if (null? point-contexts)
 
2168
          (let ((m (vc-find-context buffer point-context)))
 
2169
            (if m
 
2170
                (set-buffer-point! buffer m)))
 
2171
          (for-each (lambda (entry)
 
2172
                      (let ((window (car entry)))
 
2173
                        (if (and (window-live? window)
 
2174
                                 (eq? buffer (window-buffer window)))
 
2175
                            (begin
 
2176
                              (let ((m (vc-find-context buffer (caddr entry))))
 
2177
                                (if m
 
2178
                                    (set-window-start-mark! window m #t)))
 
2179
                              (let ((m (vc-find-context buffer (cadr entry))))
 
2180
                                (if m
 
2181
                                    (set-window-point! window m)))))))
 
2182
                    point-contexts))
 
2183
      (let ((m (vc-find-context buffer mark-context)))
 
2184
        (if m
 
2185
            (set-buffer-mark! buffer m)))
 
2186
      buffer)))
 
2187
 
 
2188
(define (vc-mark-context mark)
 
2189
  (let ((group (mark-group mark))
 
2190
        (index (mark-index mark)))
 
2191
    (let ((length (group-length group)))
 
2192
      (vector index
 
2193
              length
 
2194
              (group-extract-string group index (min length (+ index 100)))))))
 
2195
 
 
2196
(define (vc-find-context buffer context)
 
2197
  (let ((group (buffer-group buffer))
 
2198
        (index (vector-ref context 0))
 
2199
        (string (vector-ref context 2)))
 
2200
    (let ((length (group-length group)))
 
2201
      (if (string-null? string)
 
2202
          (group-end-mark group)
 
2203
          (and (or (and (< index length)
 
2204
                        (search-forward string
 
2205
                                        (make-mark group index)
 
2206
                                        (make-mark group length)))
 
2207
                   (let ((index
 
2208
                          (- index
 
2209
                             (abs (- (vector-ref context 1) length))
 
2210
                             (string-length string))))
 
2211
                     (and (<= 0 index length)
 
2212
                          (search-forward string
 
2213
                                          (make-mark group index)
 
2214
                                          (make-mark group length)))))
 
2215
               (let ((mark (re-match-start 0)))
 
2216
                 (cond ((mark< mark (group-start-mark group))
 
2217
                        (group-start-mark group))
 
2218
                       ((mark> mark (group-end-mark group))
 
2219
                        (group-end-mark group))
 
2220
                       (else mark))))))))
 
 
b'\\ No newline at end of file'