1 ;;; clearcase.el --- ClearCase/Emacs integration.
3 ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Kevin Esler
5 ;; Author: Kevin Esler <kaesler@us.ibm.com>
6 ;; Maintainer: Kevin Esler <kaesler@us.ibm.com>
7 ;; Keywords: clearcase tools
8 ;; Web home: http://members.verizon.net/~vze24fr2/EmacsClearCase
10 ;; This file is not part of GNU Emacs.
12 ;; This program is free software; you can redistribute it and/or modify it under
13 ;; the terms of the GNU General Public License as published by the Free Software
14 ;; Foundation; either version 2, or (at your option) any later version.
16 ;; This program is distributed in the hope that it will be useful, but WITHOUT
17 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 ;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 ;; You should have received a copy of the GNU General Public License along with
22 ;; GNU Emacs; see the file COPYING. If not, write to the Free Software
23 ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 ;; This is a ClearCase/Emacs integration.
33 ;; 0. Make sure you're using Gnu Emacs-20.4 or later or a recent XEmacs.
34 ;; In general it seems to work better in Gnu Emacs than in XEmacs,
35 ;; although many XEmacs users have no problems at all with it.
37 ;; 1. Make sure that you DON'T load old versions of vc-hooks.el which contain
38 ;; incompatible versions of the tq package (functions tq-enqueue and
39 ;; friends). In particular, Bill Sommerfeld's VC/CC integration has this
42 ;; 2. Copy the files (or at least the clearcase.elc file) to a directory
43 ;; on your emacs-load-path.
45 ;; 3. Insert this in your emacs startup file: (load "clearcase")
47 ;; When you begin editing in any view-context, a ClearCase menu will appear
48 ;; and ClearCase Minor Mode will be activated for you.
50 ;; Summary of features
51 ;; ===================
53 ;; Keybindings compatible with Emacs' VC (where it makes sense)
54 ;; Richer interface than VC
55 ;; Works on NT and Unix
56 ;; Context sensitive menu (Emacs knows the ClearCase-status of files)
57 ;; Snapshot view support: update, version comparisons
58 ;; Can use Emacs Ediff for version comparison display
60 ;; - en masse checkin/out etc
62 ;; - browse version tree
63 ;; Completion of viewnames, version strings
64 ;; Auto starting of views referenced as /view/TAG/.. (or \\view\TAG\...)
65 ;; Emacs for editing comments, config specs
66 ;; Standard ClearCase GUI tools launchable from Emacs menu
67 ;; - version tree browser
71 ;; Operations directly available from Emacs menu/keymap:
82 ;; snapshot view update: file, directory, view
83 ;; version comparisons using ediff, diff or GUI
91 ;; The help of the following is gratefully acknowledged:
93 ;; XEmacs support and other bugfixes:
98 ;; This was a result of examining earlier versions of VC and VC/ClearCase
99 ;; integrations and borrowing freely therefrom. Accordingly, the following
100 ;; are ackowledged as contributors:
102 ;; VC/ClearCase integration authors:
121 ;; Jonathan Stigelman
124 ;; Other Contributors:
145 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 (defconst clearcase-version-stamp "ClearCase-version: </main/laptop/156>")
152 (defconst clearcase-version (substring clearcase-version-stamp 19))
154 (defun clearcase-maintainer-address ()
157 (concat "kevin.esler.1989"
161 (defun clearcase-submit-bug-report ()
162 "Submit via mail a bug report on ClearCase Mode"
164 (and (y-or-n-p "Do you really want to submit a report on ClearCase Mode ? ")
165 (reporter-submit-bug-report
166 (clearcase-maintainer-address)
167 (concat "clearcase.el " clearcase-version)
172 clearcase-clearcase-version-installed
173 clearcase-cleartool-path
179 clearcase-servers-online
182 clearcase-setview-root
183 clearcase-suppress-vc-within-mvfs
185 w32-quote-process-args
192 (defmacro clearcase-when-debugging (&rest forms)
193 (list 'if 'clearcase-debug (cons 'progn forms)))
195 (defmacro clearcase-with-tempfile (filename-var &rest forms)
196 `(let ((,filename-var (clearcase-utl-tempfile-name)))
202 (if (file-exists-p ,filename-var)
203 (delete-file ,filename-var)))))
209 (defvar clearcase-xemacs-p (string-match "XEmacs" emacs-version))
211 (defvar clearcase-on-mswindows (memq system-type
212 '(windows-nt ms-windows cygwin cygwin32)))
214 (defvar clearcase-on-cygwin (memq system-type '(cygwin cygwin32)))
216 (defvar clearcase-sink-file-name
218 (clearcase-on-cygwin "/dev/null")
219 (clearcase-on-mswindows "NUL")
222 (defun clearcase-view-mode-quit (buf)
223 "Exit from View mode, restoring the previous window configuration."
225 (cond ((frame-property (selected-frame) 'clearcase-view-window-config)
226 (set-window-configuration
227 (frame-property (selected-frame) 'clearcase-view-window-config))
228 (set-frame-property (selected-frame) 'clearcase-view-window-config nil))
229 ((not (one-window-p))
233 (defun clearcase-view-mode (arg &optional camefrom)
234 (if clearcase-xemacs-p
235 (let* ((winconfig (current-window-configuration))
236 (was-one-window (one-window-p))
237 (buffer-name (buffer-name (current-buffer)))
238 (clearcase-view-not-visible
239 (not (and (windows-of-buffer buffer-name) ;shortcut
240 (memq (selected-frame)
241 (mapcar 'window-frame
242 (windows-of-buffer buffer-name)))))))
243 (when clearcase-view-not-visible
244 (set-frame-property (selected-frame)
245 'clearcase-view-window-config winconfig))
246 (view-mode camefrom 'clearcase-view-mode-quit)
247 (setq buffer-read-only nil))
250 (defun clearcase-port-view-buffer-other-window (buffer)
251 (if clearcase-xemacs-p
252 (switch-to-buffer-other-window buffer)
253 (view-buffer-other-window buffer nil 'kill-buffer)))
255 (defun clearcase-dired-sort-by-date ()
256 (if (fboundp 'dired-sort-by-date)
257 (dired-sort-by-date)))
259 ;; Copied from emacs-20
261 (if (not (fboundp 'subst-char-in-string))
262 (defun subst-char-in-string (fromchar tochar string &optional inplace)
263 "Replace FROMCHAR with TOCHAR in STRING each time it occurs.
264 Unless optional argument INPLACE is non-nil, return a new string."
265 (let ((i (length string))
266 (newstr (if inplace string (copy-sequence string))))
269 (if (eq (aref newstr i) fromchar)
270 (aset newstr i tochar)))
277 ;; nyi: we also use these at the moment:
287 (require 'executable)
290 (or clearcase-xemacs-p
293 ;; NT Emacs - doesn't use tq.
295 (if (not clearcase-on-mswindows)
300 ;;{{{ Debugging facilities
302 ;; Setting this to true will enable some debug code.
304 (defvar clearcase-debug nil)
306 (defun clearcase-trace (string)
307 (clearcase-when-debugging
308 (let ((trace-buf (get-buffer "*clearcase-trace*")))
311 (set-buffer trace-buf)
312 (goto-char (point-max))
313 (insert string "\n"))))))
315 (defun clearcase-enable-tracing ()
317 (setq clearcase-debug t)
318 (get-buffer-create "*clearcase-trace*"))
320 (defun clearcase-disable-tracing ()
322 (setq clearcase-debug nil))
324 (defun clearcase-dump ()
326 (clearcase-utl-populate-and-view-buffer
330 (clearcase-fprop-dump-to-current-buffer)
331 (clearcase-vprop-dump-to-current-buffer)))))
333 (defun clearcase-flush-caches ()
335 (clearcase-fprop-clear-all-properties)
336 (clearcase-vprop-clear-all-properties))
340 ;;{{{ Customizable variables
346 (if (and (featurep 'custom)
347 (fboundp 'custom-declare-variable))
348 nil ;; We've got what we needed
349 ;; We have the old custom-library, hack around it!
350 (defmacro defgroup (&rest args)
352 (defmacro defcustom (var value doc &rest args)
353 (` (defvar (, var) (, value) (, doc))))
354 (defmacro defface (face value doc &rest stuff)
356 (defmacro custom-declare-variable (symbol value doc &rest args)
357 (list 'defvar (eval symbol) value doc))))
359 (defgroup clearcase () "ClearCase Options" :group 'tools :prefix "clearcase")
361 (defcustom clearcase-keep-uncheckouts t
362 "When true, the contents of an undone checkout will be kept in a file
363 with a \".keep\" suffix. Otherwise it will be removed."
367 (defcustom clearcase-keep-unhijacks t
368 "When true, the contents of an undone hijack will be kept in a file
369 with a \".keep\" suffix. Otherwise it will be removed."
373 ;; nyi: We could also allow a value of 'prompt here
375 (defcustom clearcase-set-to-new-activity t
376 "*If this variable is non-nil when a new activity is created, that activity
377 will be set as the current activity for the view, otherwise no change is made
378 to the view's current activity setting."
382 (defcustom clearcase-prompt-for-activity-names t
383 "*If this variable is non-nil the user will be prompted for activity names.
384 Otherwise, activity names will be generated automatically and will typically
385 have the form \"activity011112.155233\". If the name entered is empty sucn an
386 internal name will also be generated."
390 (defcustom clearcase-make-backup-files nil
391 "*If non-nil, backups of ClearCase files are made as with other files.
392 If nil (the default), files under ClearCase control don't get backups."
396 (defcustom clearcase-complete-viewtags t
397 "*If non-nil, completion on viewtags is enabled. For sites with thousands of view
398 this should be set to nil."
402 (defcustom clearcase-minimise-menus nil
403 "*If non-nil, menus will hide rather than grey-out inapplicable choices."
407 (defcustom clearcase-auto-dired-mode t
408 "*If non-nil, automatically enter `clearcase-dired-mode' in dired-mode
409 for directories in ClearCase."
413 (defcustom clearcase-dired-highlight t
414 "If non-nil, highlight reserved files in clearcase-dired buffers."
418 (defcustom clearcase-dired-show-view t
419 "If non-nil, show the view tag in dired buffers."
423 (defcustom clearcase-verify-pre-mkelem-dir-checkout nil
424 "*If non-nil, prompt before checking out the containing directory
425 before creating a new ClearCase element."
429 (defcustom clearcase-diff-on-checkin nil
430 "Display diff on checkin to help you compose the checkin comment."
434 ;; General customization
436 (defcustom clearcase-suppress-confirm nil
437 "If non-nil, treat user as expert; suppress yes-no prompts on some things."
441 (defcustom clearcase-initial-mkelem-comment nil
442 "Prompt for initial comment when an element is created."
446 (defcustom clearcase-command-messages nil
447 "Display run messages from back-end commands."
451 (defcustom clearcase-checkin-arguments
452 ;; For backwards compatibility with old name for this variable:
454 (if (and (boundp 'clearcase-checkin-switches)
455 (not (null clearcase-checkin-switches)))
456 (list clearcase-checkin-switches)
458 "A list of extra arguments passed to the checkin command."
460 :type '(repeat (string :tag "Argument")))
462 (defcustom clearcase-checkin-on-mkelem nil
463 "If t, file will be checked-in when first created as an element."
467 (defcustom clearcase-suppress-checkout-comments nil
468 "Suppress prompts for checkout comments for those version control
469 systems which use them."
473 (defcustom clearcase-checkout-arguments
474 ;; For backwards compatibility with old name for this variable:
476 (if (and (boundp 'clearcase-checkout-arguments)
477 (not (null clearcase-checkout-arguments)))
478 (list clearcase-checkout-arguments)
480 "A list of extra arguments passed to the checkout command."
482 :type '(repeat (string :tag "Argument")))
484 (defcustom clearcase-directory-exclusion-list '("lost+found")
485 "Directory names ignored by functions that recursively walk file trees."
487 :type '(repeat (string :tag "Subdirectory")))
489 (defcustom clearcase-use-normal-diff nil
490 "If non-nil, use normal diff instead of cleardiff."
494 (defcustom clearcase-normal-diff-program "diff"
495 "*Program to use for generating the differential of the two files
496 when `clearcase-use-normal-diff' is t."
500 (defcustom clearcase-normal-diff-arguments
501 (if (and (boundp 'clearcase-normal-diff-switches)
502 (not (null clearcase-normal-diff-switches)))
503 (list clearcase-normal-diff-switches)
505 "A list of extra arguments passed to `clearcase-normal-diff-program'
506 when `clearcase-use-normal-diff' is t. Usage of the -u switch is
507 recommended to produce unified diffs, when your
508 `clearcase-normal-diff-program' supports it."
510 :type '(repeat (string :tag "Argument")))
512 (defcustom clearcase-vxpath-glue "@@"
513 "The string used to construct version-extended pathnames."
517 (defcustom clearcase-viewroot (if clearcase-on-mswindows
520 "The ClearCase viewroot directory."
524 (defcustom clearcase-viewroot-drive "m:"
525 "The ClearCase viewroot drive letter for Windows."
529 (defcustom clearcase-suppress-vc-within-mvfs t
530 "Suppresses VC activity within the MVFS."
534 (defcustom clearcase-hide-rebase-activities t
535 "Hide rebase activities from activity selection list."
539 (defcustom clearcase-rebase-id-regexp "^rebase\\."
540 "The regexp used to detect rebase actvities."
546 ;;{{{ Global variables
548 ;; Initialize clearcase-pname-sep-regexp according to
549 ;; directory-sep-char.
550 (defvar clearcase-pname-sep-regexp
552 (char-to-string directory-sep-char)))
554 (defvar clearcase-non-pname-sep-regexp
556 (char-to-string directory-sep-char)))
558 ;; Matches any viewtag (without the trailing "/").
560 (defvar clearcase-viewtag-regexp
563 clearcase-pname-sep-regexp
565 clearcase-non-pname-sep-regexp "*"
570 ;; Matches ANY viewroot-relative path
572 (defvar clearcase-vrpath-regexp
575 clearcase-pname-sep-regexp
577 clearcase-non-pname-sep-regexp "*"
583 ;;{{{ Minor Mode: ClearCase
585 ;; For ClearCase Minor Mode
587 (defvar clearcase-mode nil)
588 (set-default 'clearcase-mode nil)
589 (make-variable-buffer-local 'clearcase-mode)
590 (put 'clearcase-mode 'permanent-local t)
592 ;; Tell Emacs about this new kind of minor mode
594 (if (not (assoc 'clearcase-mode minor-mode-alist))
595 (setq minor-mode-alist (cons '(clearcase-mode clearcase-mode)
598 ;; For now we override the bindings for VC Minor Mode with ClearCase Minor Mode
601 (defvar clearcase-mode-map (make-sparse-keymap))
602 (defvar clearcase-prefix-map (make-sparse-keymap))
603 (define-key clearcase-mode-map "\C-xv" clearcase-prefix-map)
604 (define-key clearcase-mode-map "\C-x\C-q" 'clearcase-toggle-read-only)
606 (define-key clearcase-prefix-map "b" 'clearcase-browse-vtree-current-buffer)
607 (define-key clearcase-prefix-map "c" 'clearcase-uncheckout-current-buffer)
608 (define-key clearcase-prefix-map "e" 'clearcase-edcs-edit)
609 (define-key clearcase-prefix-map "g" 'clearcase-annotate-current-buffer)
610 (define-key clearcase-prefix-map "i" 'clearcase-mkelem-current-buffer)
611 (define-key clearcase-prefix-map "l" 'clearcase-list-history-current-buffer)
612 (define-key clearcase-prefix-map "m" 'clearcase-mkbrtype)
613 (define-key clearcase-prefix-map "u" 'clearcase-uncheckout-current-buffer)
614 (define-key clearcase-prefix-map "v" 'clearcase-next-action-current-buffer)
615 (define-key clearcase-prefix-map "w" 'clearcase-what-rule-current-buffer)
616 (define-key clearcase-prefix-map "=" 'clearcase-diff-pred-current-buffer)
617 (define-key clearcase-prefix-map "?" 'clearcase-describe-current-buffer)
618 (define-key clearcase-prefix-map "~" 'clearcase-version-other-window)
620 ;; To avoid confusion, we prevent VC Mode from being active at all by
621 ;; undefining its keybindings for which ClearCase Mode doesn't yet have an
624 (define-key clearcase-prefix-map "a" 'undefined) ;; vc-update-change-log
625 (define-key clearcase-prefix-map "d" 'undefined) ;; vc-directory
626 (define-key clearcase-prefix-map "h" 'undefined) ;; vc-insert-headers
627 (define-key clearcase-prefix-map "m" 'undefined) ;; vc-merge
628 (define-key clearcase-prefix-map "r" 'undefined) ;; vc-retrieve-snapshot
629 (define-key clearcase-prefix-map "s" 'undefined) ;; vc-create-snapshot
630 (define-key clearcase-prefix-map "t" 'undefined) ;; vc-dired-toggle-terse-mode
632 ;; Associate the map and the minor mode
634 (or (not (boundp 'minor-mode-map-alist))
635 (assq 'clearcase-mode (symbol-value 'minor-mode-map-alist))
636 (setq minor-mode-map-alist
637 (cons (cons 'clearcase-mode clearcase-mode-map)
638 minor-mode-map-alist)))
640 (defun clearcase-mode (&optional arg)
641 "ClearCase Minor Mode"
645 ;; Behave like a proper minor-mode.
652 ;; Check if the numeric arg is positive.
654 (> (prefix-numeric-value arg) 0))
657 ;; Use the car if it's a list.
660 (setq arg (car arg)))
663 (not clearcase-mode) ;; toggle mode switch
664 (not (eq '- arg))) ;; True if symbol is not '-
667 ;; assume it's a number and check that.
672 (easy-menu-add clearcase-menu 'clearcase-mode-map))
677 ;;{{{ Minor Mode: ClearCase Dired
679 ;;{{{ Reformatting the Dired buffer
681 ;; Create a face for highlighting checked out files in clearcase-dired.
683 (if (not (memq 'clearcase-dired-checkedout-face (face-list)))
685 (make-face 'clearcase-dired-checkedout-face)
686 (set-face-foreground 'clearcase-dired-checkedout-face "red")))
688 (defun clearcase-dired-insert-viewtag ()
691 (goto-char (point-min))
693 ;; Only do this if the buffer is not currently narrowed
696 (let ((viewtag (clearcase-fprop-viewtag (file-truename default-directory))))
700 (let ((buffer-read-only nil))
701 (insert (format " [ClearCase View: %s]\n" viewtag))))))))))
703 (defun clearcase-dired-reformat-buffer ()
704 "Reformats the current dired buffer."
705 (let* ((checkout-list nil)
706 (modified-file-info nil)
708 (directory default-directory)
712 ;; Iterate over each line in the buffer.
715 ;; 1. In general, a Dired buffer can contain listings for several
716 ;; directories. We pass though from top to bottom and adjust
718 ;; 2. Since this is called from dired-after-reading-hook, it can get
719 ;; called on a single-line buffer. In this case there is no subdir,
720 ;; and no checkout-list. We need to call clearcase-fprop-checked-out
721 ;; to test for a checkout.
724 (goto-char (point-min))
728 ;; Case 1: Look for directory markers
730 ((setq subdir (dired-get-subdir))
732 ;; We're at a subdirectory line in the dired buffer.
733 ;; Go and list all checkouts and hijacks in this subdirectory.
735 (setq modified-file-info (clearcase-dired-list-modified-files subdir))
736 (setq checkout-list (nth 0 modified-file-info))
737 (setq hijack-list (nth 1 modified-file-info))
739 ;; If no checkouts are found, we don't need to check each file, and
740 ;; it's very slow. The checkout-list should contain something so it
741 ;; doesn't attempt to do this.
743 (if (null checkout-list)
744 (setq checkout-list '(nil)))
745 (if (null hijack-list)
746 (setq hijack-list '(nil)))
747 (message "Reformatting %s..." subdir))
749 ;; Case 2: Look for files (the safest way to get the filename).
751 ((setq fullpath (dired-get-filename nil t))
753 ;; Expand it to get rid of . and .. entries.
755 (setq fullpath (expand-file-name fullpath))
757 (setq fullpath (clearcase-path-canonicalise-slashes fullpath))
759 ;; Only modify directory listings of the correct format.
760 ;; We replace the GID field with a checkout indicator.
764 ;; -rw-rw-rw- 1 esler 5 28 Feb 2 16:02 foo.el
765 "..\\([drwxlts-]+ \\) *\\([0-9]+\\) \\([^ ]+\\) *\\([^ ]+ *\\) +[0-9]+\\( [^ 0-9]+ [0-9 ][0-9] .*\\)")
767 (let* ((replacement-begin (match-beginning 4))
768 (replacement-end (match-end 4))
770 (replacement-length (- replacement-end replacement-begin))
771 (checkout-replacement-text (format "CHECKOUT"))
772 (hijack-replacement-text (format "HIJACK"))
773 (is-checkout (if checkout-list
774 (member fullpath checkout-list)
775 (clearcase-fprop-checked-out fullpath)))
776 (is-hijack (if hijack-list
777 (member fullpath hijack-list)
778 (clearcase-fprop-hijacked fullpath))))
780 ;; Highlight the line if the file is checked-out.
784 ;; Replace the GID field with CHECKOUT.
786 (let ((buffer-read-only nil))
788 ;; Pad with replacement text with trailing spaces if necessary.
790 (if (>= replacement-length (length checkout-replacement-text))
791 (setq checkout-replacement-text
792 (concat checkout-replacement-text
793 (make-string (- replacement-length (length checkout-replacement-text))
795 (goto-char replacement-begin)
796 (delete-char replacement-length)
797 (insert (substring checkout-replacement-text 0 replacement-length)))
799 ;; Highlight the checked out files.
801 (if (fboundp 'put-text-property)
802 (let ((buffer-read-only nil))
803 (put-text-property replacement-begin replacement-end
804 'face 'clearcase-dired-checkedout-face)))
810 ;; Replace the GID field with CHECKOUT.
812 (let ((buffer-read-only nil))
814 ;; Pad with replacement text with trailing spaces if necessary.
816 (if (>= replacement-length (length hijack-replacement-text))
817 (setq hijack-replacement-text
818 (concat hijack-replacement-text
819 (make-string (- replacement-length (length hijack-replacement-text))
821 (goto-char replacement-begin)
822 (delete-char replacement-length)
823 (insert (substring hijack-replacement-text 0 replacement-length)))
825 ;; Highlight the checked out files.
827 (if (fboundp 'put-text-property)
828 (let ((buffer-read-only nil))
829 (put-text-property replacement-begin replacement-end
830 'face 'clearcase-dired-checkedout-face)))
836 (message "Reformatting...Done"))
839 (defun clearcase-path-follow-if-vob-slink (path)
840 (if (clearcase-fprop-file-is-vob-slink-p path)
842 ;; It's a slink so follow it.
844 (let ((slink-text (clearcase-fprop-vob-slink-text path)))
845 (if (file-name-absolute-p slink-text)
847 (concat (file-name-directory path) slink-text)))
853 ;;{{{ Searching for modified files
857 ;; (defun clearcase-dired-list-checkouts (directory)
858 ;; "Returns a list of files checked-out to the current view in DIRECTORY."
860 ;; ;; Don't bother looking for checkouts in
861 ;; ;; - a history-mode branch-qua-directory
862 ;; ;; - a view-private directory
864 ;; ;; NYI: For now don't run lsco in root of a snapshot because it gives errors.
865 ;; ;; We need to make this smarter.
867 ;; ;; NYI: For a pathname which is a slink to a dir, despite the fact that
868 ;; ;; clearcase-fprop-file-is-version-p returns true, lsco fails on it,
869 ;; ;; with "not an element". Sheesh, surely lsco ought to follow links ?
870 ;; ;; Solution: catch the error and check if the dir is a slink then follow
871 ;; ;; the link and retry the lsco on the target.
873 ;; ;; For now just ignore the error.
875 ;; (if (and (not (clearcase-vxpath-p directory))
876 ;; (not (eq 'view-private-object (clearcase-fprop-mtype directory)))
877 ;; (clearcase-fprop-file-is-version-p directory))
880 ;; (let* ((ignore (message "Listing ClearCase checkouts..."))
882 ;; (true-dir-path (file-truename directory))
884 ;; ;; Give the directory as an argument so all names will be
885 ;; ;; fullpaths. For some reason ClearCase adds an extra slash if you
886 ;; ;; leave the trailing slash on the directory, so we need to remove
889 ;; (native-dir-path (clearcase-path-native (directory-file-name true-dir-path)))
891 ;; (followed-dir-path (clearcase-path-follow-if-vob-slink native-dir-path))
893 ;; ;; Form the command:
896 ;; "lsco" "-cview" "-fmt"
897 ;; (if clearcase-on-mswindows
901 ;; followed-dir-path))
903 ;; ;; Capture the output:
905 ;; (string (clearcase-path-canonicalise-slashes
906 ;; (apply 'clearcase-ct-cleartool-cmd cmd)))
908 ;; ;; Split the output at the newlines:
910 ;; (checkout-list (clearcase-utl-split-string-at-char string ?\n)))
912 ;; ;; Add entries for "." and ".." if they're checked-out.
914 ;; (let* ((entry ".")
915 ;; (path (expand-file-name (concat (file-name-as-directory true-dir-path)
917 ;; (if (clearcase-fprop-checked-out path)
918 ;; (setq checkout-list (cons path checkout-list))))
919 ;; (let* ((entry "..")
920 ;; (path (expand-file-name (concat (file-name-as-directory true-dir-path)
922 ;; (if (clearcase-fprop-checked-out path)
923 ;; (setq checkout-list (cons path checkout-list))))
925 ;; ;; If DIRECTORY is a vob-slink, checkout list will contain pathnames
926 ;; ;; relative to the vob-slink target rather than to DIRECTORY. Convert
927 ;; ;; them back here. We're making it appear that lsco works on
928 ;; ;; slinks-to-dirs.
930 ;; (if (clearcase-fprop-file-is-vob-slink-p true-dir-path)
931 ;; (let ((re (regexp-quote (file-name-as-directory followed-dir-path))))
932 ;; (setq checkout-list
936 ;; (replace-regexp-in-string re true-dir-path path)))
939 ;; (message "Listing ClearCase checkouts...done")
941 ;; ;; Return the result.
946 ;; ;; I had believed that this implementation below OUGHT to be faster, having
947 ;; ;; read the code in "ct+lsco". It seemed that "lsco -cview" hit the VOB and
948 ;; ;; listed all checkouts on all elements in the directory, and then filtered by
949 ;; ;; view. I thought it would probably be quicker to run "ct ls -vob_only" and
950 ;; ;; keep the lines that have "[eclipsed by checkout]". However this code
951 ;; ;; actually seemed to run slower. Leave the code here for now so I can test
954 ;; (defun clearcase-dired-list-checkouts-experimental (directory)
955 ;; "Returns a list of files checked-out to the current view in DIRECTORY."
957 ;; ;; Don't bother looking for checkouts in a history-mode listing
958 ;; ;; nor in view-private directories.
960 ;; (if (and (not (clearcase-vxpath-p directory))
961 ;; (not (eq 'view-private-object (clearcase-fprop-mtype directory))))
963 ;; (let* ((ignore (message "Listing ClearCase checkouts..."))
965 ;; (true-directory (file-truename directory))
967 ;; ;; Move temporarily to the directory:
969 ;; (default-directory true-directory)
971 ;; ;; Form the command:
973 ;; (cmd (list "ls" "-vob_only"))
975 ;; ;; Capture the output:
977 ;; (string (clearcase-path-canonicalise-slashes
978 ;; (apply 'clearcase-ct-cleartool-cmd cmd)))
980 ;; ;; Split the output at the newlines:
982 ;; (line-list (clearcase-utl-split-string-at-char string ?\n))
984 ;; (checkout-list nil))
986 ;; ;; Look for lines of the form:
987 ;; ;; FILENAME@@ [eclipsed by checkout]
991 ;; (if (string-match "^\\([^ @]+\\)@@ +\\[eclipsed by checkout\\].*" line)
992 ;; (setq checkout-list (cons (concat
993 ;; ;; Add back directory name to get
998 ;; (match-beginning 1)
1000 ;; checkout-list)))))
1003 ;; ;; Add entries for "." and ".." if they're checked-out.
1005 ;; (let* ((entry ".")
1006 ;; (path (expand-file-name (concat true-directory entry))))
1007 ;; (if (clearcase-fprop-checked-out path)
1008 ;; (setq checkout-list (cons path checkout-list))))
1009 ;; (let* ((entry "..")
1010 ;; (path (expand-file-name (concat true-directory entry))))
1011 ;; (if (clearcase-fprop-checked-out path)
1012 ;; (setq checkout-list (cons path checkout-list))))
1014 ;; (message "Listing ClearCase checkouts...done")
1016 ;; ;; Return the result.
1020 ;; (defun clearcase-dired-list-hijacks (directory)
1021 ;; "Returns a list of files hijacked to the current view in DIRECTORY."
1023 ;; ;; Don't bother looking for hijacks in;
1024 ;; ;; - a history-mode listing
1025 ;; ;; - a in view-private directory
1026 ;; ;; - a dynamic view
1028 ;; (let* ((true-directory (file-truename directory))
1029 ;; (viewtag (clearcase-fprop-viewtag true-directory)))
1032 ;; (not (clearcase-vxpath-p directory))
1033 ;; (not (eq 'view-private-object (clearcase-fprop-mtype directory)))
1034 ;; (clearcase-file-would-be-in-snapshot-p true-directory))
1036 ;; (let* ((ignore (message "Listing ClearCase hijacks..."))
1038 ;; (true-directory (file-truename directory))
1040 ;; ;; Form the command:
1045 ;; ;; Give the directory as an argument so all names will be
1046 ;; ;; fullpaths. For some reason ClearCase adds an extra slash
1047 ;; ;; if you leave the trailing slash on the directory, so we
1048 ;; ;; need to remove it.
1050 ;; (clearcase-path-native (directory-file-name true-directory))))
1052 ;; ;; Capture the output:
1054 ;; (string (clearcase-path-canonicalise-slashes
1055 ;; (apply 'clearcase-ct-cleartool-cmd cmd)))
1057 ;; ;; Split the output at the newlines:
1059 ;; (line-list (clearcase-utl-split-string-at-char string ?\n))
1061 ;; (hijack-list nil))
1063 ;; (mapcar (function
1065 ;; (if (string-match "^\\([^ @]+\\)@@[^ ]+ \\[hijacked\\].*" line)
1066 ;; (setq hijack-list (cons (substring line
1067 ;; (match-beginning 1)
1072 ;; (message "Listing ClearCase hijacks...done")
1074 ;; ;; Return the result.
1080 (defun clearcase-dired-list-modified-files (directory)
1081 "Returns a pair of lists of files (checkouts . hijacks) to the current view in DIRECTORY."
1083 ;; Don't bother looking for hijacks in;
1084 ;; - a history-mode listing
1085 ;; - a in view-private directory
1088 (let* ((true-directory (file-truename directory))
1089 (viewtag (clearcase-fprop-viewtag true-directory))
1090 (snapshot (clearcase-file-would-be-in-snapshot-p true-directory))
1094 (not (clearcase-vxpath-p directory))
1095 (not (eq 'view-private-object (clearcase-fprop-mtype directory))))
1097 (let* ((ignore (message "Listing ClearCase modified files..."))
1099 (true-directory (file-truename directory))
1101 ;; Form the command:
1106 ;; Give the directory as an argument so all names will be
1107 ;; fullpaths. For some reason ClearCase adds an extra slash
1108 ;; if you leave the trailing slash on the directory, so we
1109 ;; need to remove it.
1111 (clearcase-path-native (directory-file-name true-directory))))
1113 ;; Capture the output:
1115 (string (clearcase-path-canonicalise-slashes
1116 (apply 'clearcase-ct-cleartool-cmd cmd)))
1118 ;; Split the output at the newlines:
1120 (line-list (clearcase-utl-split-string-at-char string ?\n))
1123 (checkout-list nil))
1127 (if (string-match "^\\([^ @]+\\)@@[^ ]+ \\[hijacked\\].*" line)
1128 (setq hijack-list (cons (substring line
1132 (if (string-match "^\\([^ @]+\\)@@.+CHECKEDOUT from .*" line)
1133 (setq checkout-list (cons (substring line
1139 (message "Listing ClearCase modified files...done")
1141 ;; Return the result.
1143 (setq result (list checkout-list hijack-list))))
1150 ;; For ClearCase Dired Minor Mode
1152 (defvar clearcase-dired-mode nil)
1153 (set-default 'clearcase-dired-mode nil)
1154 (make-variable-buffer-local 'clearcase-dired-mode)
1156 ;; Tell Emacs about this new kind of minor mode
1158 (if (not (assoc 'clearcase-dired-mode minor-mode-alist))
1159 (setq minor-mode-alist (cons '(clearcase-dired-mode clearcase-dired-mode)
1162 ;; For now we override the bindings for VC Minor Mode with ClearCase Dired
1163 ;; Minor Mode bindings.
1165 (defvar clearcase-dired-mode-map (make-sparse-keymap))
1166 (defvar clearcase-dired-prefix-map (make-sparse-keymap))
1167 (define-key clearcase-dired-mode-map "\C-xv" clearcase-dired-prefix-map)
1169 (define-key clearcase-dired-prefix-map "b" 'clearcase-browse-vtree-dired-file)
1170 (define-key clearcase-dired-prefix-map "c" 'clearcase-uncheckout-dired-files)
1171 (define-key clearcase-dired-prefix-map "e" 'clearcase-edcs-edit)
1172 (define-key clearcase-dired-prefix-map "i" 'clearcase-mkelem-dired-files)
1173 (define-key clearcase-dired-prefix-map "g" 'clearcase-annotate-dired-file)
1174 (define-key clearcase-dired-prefix-map "l" 'clearcase-list-history-dired-file)
1175 (define-key clearcase-dired-prefix-map "m" 'clearcase-mkbrtype)
1176 (define-key clearcase-dired-prefix-map "u" 'clearcase-uncheckout-dired-files)
1177 (define-key clearcase-dired-prefix-map "v" 'clearcase-next-action-dired-files)
1178 (define-key clearcase-dired-prefix-map "w" 'clearcase-what-rule-dired-file)
1179 (define-key clearcase-dired-prefix-map "=" 'clearcase-diff-pred-dired-file)
1180 (define-key clearcase-dired-prefix-map "~" 'clearcase-version-other-window)
1181 (define-key clearcase-dired-prefix-map "?" 'clearcase-describe-dired-file)
1183 ;; To avoid confusion, we prevent VC Mode from being active at all by
1184 ;; undefining its keybindings for which ClearCase Mode doesn't yet have an
1187 (define-key clearcase-dired-prefix-map "a" 'undefined) ;; vc-update-change-log
1188 (define-key clearcase-dired-prefix-map "d" 'undefined) ;; vc-directory
1189 (define-key clearcase-dired-prefix-map "h" 'undefined) ;; vc-insert-headers
1190 (define-key clearcase-dired-prefix-map "m" 'undefined) ;; vc-merge
1191 (define-key clearcase-dired-prefix-map "r" 'undefined) ;; vc-retrieve-snapshot
1192 (define-key clearcase-dired-prefix-map "s" 'undefined) ;; vc-create-snapshot
1193 (define-key clearcase-dired-prefix-map "t" 'undefined) ;; vc-dired-toggle-terse-mode
1195 ;; Associate the map and the minor mode
1197 (or (not (boundp 'minor-mode-map-alist))
1198 (assq 'clearcase-dired-mode (symbol-value 'minor-mode-map-alist))
1199 (setq minor-mode-map-alist
1200 (cons (cons 'clearcase-dired-mode clearcase-dired-mode-map)
1201 minor-mode-map-alist)))
1203 (defun clearcase-dired-mode (&optional arg)
1204 "The augmented Dired minor mode used in ClearCase directory buffers.
1205 All Dired commands operate normally. Users with checked-out files
1206 are listed in place of the file's owner and group. Keystrokes bound to
1207 ClearCase Mode commands will execute as though they had been called
1208 on a buffer attached to the file named in the current Dired buffer line."
1212 ;; Behave like a proper minor-mode.
1214 (setq clearcase-dired-mode
1217 (not clearcase-dired-mode)
1219 ;; Check if the numeric arg is positive.
1221 (> (prefix-numeric-value arg) 0))
1224 ;; Use the car if it's a list.
1227 (setq arg (car arg)))
1231 (not clearcase-dired-mode) ;; toggle mode switch
1232 (not (eq '- arg))) ;; True if symbol is not '-
1235 ;; assume it's a number and check that.
1239 (if (not (eq major-mode 'dired-mode))
1240 (setq clearcase-dired-mode nil))
1242 (if (and clearcase-dired-mode clearcase-dired-highlight)
1243 (clearcase-dired-reformat-buffer))
1245 (if clearcase-dired-mode
1246 (easy-menu-add clearcase-dired-menu 'clearcase-dired-mode-map))
1251 ;;{{{ Major Mode: for editing comments.
1253 ;; The major mode function.
1255 (defun clearcase-comment-mode ()
1256 "Major mode for editing comments for ClearCase.
1258 These bindings are added to the global keymap when you enter this mode:
1260 \\[clearcase-next-action-current-buffer] perform next logical version-control operation on current file
1261 \\[clearcase-mkelem-current-buffer] mkelem the current file
1262 \\[clearcase-toggle-read-only] like next-action, but won't create elements
1263 \\[clearcase-list-history-current-buffer] display change history of current file
1264 \\[clearcase-uncheckout-current-buffer] cancel checkout in buffer
1265 \\[clearcase-diff-pred-current-buffer] show diffs between file versions
1266 \\[clearcase-version-other-window] visit old version in another window
1268 While you are entering a comment for a version, the following
1269 additional bindings will be in effect.
1271 \\[clearcase-comment-finish] proceed with check in, ending comment
1273 Whenever you do a checkin, your comment is added to a ring of
1274 saved comments. These can be recalled as follows:
1276 \\[clearcase-comment-next] replace region with next message in comment ring
1277 \\[clearcase-comment-previous] replace region with previous message in comment ring
1278 \\[clearcase-comment-search-reverse] search backward for regexp in the comment ring
1279 \\[clearcase-comment-search-forward] search backward for regexp in the comment ring
1281 Entry to the clearcase-comment-mode calls the value of text-mode-hook, then
1282 the value of clearcase-comment-mode-hook.
1284 Global user options:
1285 clearcase-initial-mkelem-comment If non-nil, require user to enter a change
1286 comment upon first checkin of the file.
1288 clearcase-suppress-confirm Suppresses some confirmation prompts,
1289 notably for reversions.
1291 clearcase-command-messages If non-nil, display run messages from the
1292 actual version-control utilities (this is
1293 intended primarily for people hacking clearcase.el
1298 ;; Major modes are supposed to just (kill-all-local-variables)
1299 ;; but we rely on clearcase-parent-buffer already having been set
1301 ;;(let ((parent clearcase-parent-buffer))
1302 ;; (kill-all-local-variables)
1303 ;; (set (make-local-variable 'clearcase-parent-buffer) parent))
1305 (setq major-mode 'clearcase-comment-mode)
1306 (setq mode-name "ClearCase/Comment")
1308 (set-syntax-table text-mode-syntax-table)
1309 (use-local-map clearcase-comment-mode-map)
1310 (setq local-abbrev-table text-mode-abbrev-table)
1312 (make-local-variable 'clearcase-comment-operands)
1313 (make-local-variable 'clearcase-comment-ring-index)
1315 (set-buffer-modified-p nil)
1316 (setq buffer-file-name nil)
1317 (run-hooks 'text-mode-hook 'clearcase-comment-mode-hook))
1321 (defvar clearcase-comment-mode-map nil)
1322 (if clearcase-comment-mode-map
1324 (setq clearcase-comment-mode-map (make-sparse-keymap))
1325 (define-key clearcase-comment-mode-map "\M-n" 'clearcase-comment-next)
1326 (define-key clearcase-comment-mode-map "\M-p" 'clearcase-comment-previous)
1327 (define-key clearcase-comment-mode-map "\M-r" 'clearcase-comment-search-reverse)
1328 (define-key clearcase-comment-mode-map "\M-s" 'clearcase-comment-search-forward)
1329 (define-key clearcase-comment-mode-map "\C-c\C-c" 'clearcase-comment-finish)
1330 (define-key clearcase-comment-mode-map "\C-x\C-s" 'clearcase-comment-save)
1331 (define-key clearcase-comment-mode-map "\C-x\C-q" 'clearcase-comment-num-num-error))
1335 (defconst clearcase-comment-maximum-ring-size 32
1336 "Maximum number of saved comments in the comment ring.")
1340 (defvar clearcase-comment-entry-mode nil)
1341 (defvar clearcase-comment-operation nil)
1342 (defvar clearcase-comment-operands)
1343 (defvar clearcase-comment-ring nil)
1344 (defvar clearcase-comment-ring-index nil)
1345 (defvar clearcase-comment-last-match nil)
1346 (defvar clearcase-comment-window-config nil)
1348 ;; In several contexts, this is a local variable that points to the buffer for
1349 ;; which it was made (either a file, or a ClearCase dired buffer).
1351 (defvar clearcase-parent-buffer nil)
1352 (defvar clearcase-parent-buffer-name nil)
1354 ;;{{{ Commands and functions
1356 (defun clearcase-comment-start-entry (uniquifier
1360 &optional parent-buffer comment-seed)
1362 "Accept a comment by popping up a clearcase-comment-mode buffer
1363 with a name derived from UNIQUIFIER, and emitting PROMPT in the minibuffer.
1364 Set the continuation on close to CONTINUATION, which should be apply-ed to a list
1365 formed by appending OPERANDS and the comment-string.
1367 Optional 5th argument specifies a PARENT-BUFFER to return to when the operation
1370 Optional 6th argument specifies a COMMENT-SEED to insert in the comment buffer for
1373 (let ((comment-buffer (get-buffer-create (format "*clearcase-comment-%s*" uniquifier)))
1374 (old-window-config (current-window-configuration))
1375 (parent (or parent-buffer
1377 (pop-to-buffer comment-buffer)
1379 ;; Record in buffer-local variables information sufficient to restore
1382 (set (make-local-variable 'clearcase-comment-window-config) old-window-config)
1383 (set (make-local-variable 'clearcase-parent-buffer) parent)
1385 (clearcase-comment-mode)
1386 (setq clearcase-comment-operation continuation)
1387 (setq clearcase-comment-operands operands)
1389 (insert comment-seed))
1390 (message "%s Type C-c C-c when done." prompt)))
1393 (defun clearcase-comment-cleanup ()
1394 ;; Make sure it ends with newline
1396 (goto-char (point-max))
1400 ;; Remove useless whitespace.
1402 (goto-char (point-min))
1403 (while (re-search-forward "[ \t]+$" nil t)
1406 ;; Remove trailing newlines, whitespace.
1408 (goto-char (point-max))
1409 (skip-chars-backward " \n\t")
1410 (delete-region (point) (point-max)))
1412 (defun clearcase-comment-finish ()
1413 "Complete the operation implied by the current comment."
1416 ;;Clean and record the comment in the ring.
1418 (let ((comment-buffer (current-buffer)))
1419 (clearcase-comment-cleanup)
1421 (if (null clearcase-comment-ring)
1422 (setq clearcase-comment-ring (make-ring clearcase-comment-maximum-ring-size)))
1423 (ring-insert clearcase-comment-ring (buffer-string))
1425 ;; Perform the operation on the operands.
1427 (if clearcase-comment-operation
1429 (apply clearcase-comment-operation
1430 (append clearcase-comment-operands (list (buffer-string)))))
1431 (error "No comment operation is pending"))
1433 ;; Return to "parent" buffer of this operation.
1434 ;; Remove comment window.
1436 (let ((old-window-config clearcase-comment-window-config))
1437 (pop-to-buffer clearcase-parent-buffer)
1438 (delete-windows-on comment-buffer)
1439 (kill-buffer comment-buffer)
1440 (if old-window-config (set-window-configuration old-window-config)))))
1442 (defun clearcase-comment-save-comment-for-buffer (comment buffer)
1445 (let ((file (buffer-file-name)))
1446 (if (clearcase-fprop-checked-out file)
1448 (clearcase-ct-do-cleartool-command "chevent"
1452 (clearcase-fprop-set-comment file comment))
1453 (error "Can't change comment of checked-in version with this interface")))))
1455 (defun clearcase-comment-save ()
1456 "Save the currently entered comment"
1458 (let ((comment-string (buffer-string))
1459 (parent-buffer clearcase-parent-buffer))
1460 (if (not (buffer-modified-p))
1461 (message "(No changes need to be saved)")
1464 (set-buffer parent-buffer)
1465 (clearcase-comment-save-comment-for-buffer comment-string parent-buffer))
1467 (set-buffer-modified-p nil)))))
1469 (defun clearcase-comment-num-num-error ()
1471 (message "Perhaps you wanted to type C-c C-c instead?"))
1473 ;; Code for the comment ring.
1475 (defun clearcase-comment-next (arg)
1476 "Cycle forwards through comment history."
1478 (clearcase-comment-previous (- arg)))
1480 (defun clearcase-comment-previous (arg)
1481 "Cycle backwards through comment history."
1483 (let ((len (ring-length clearcase-comment-ring)))
1484 (cond ((or (not len) (<= len 0))
1485 (message "Empty comment ring")
1490 ;; Initialize the index on the first use of this command so that the
1491 ;; first M-p gets index 0, and the first M-n gets index -1.
1493 (if (null clearcase-comment-ring-index)
1494 (setq clearcase-comment-ring-index
1496 (if (< arg 0) 1 0))))
1497 (setq clearcase-comment-ring-index
1498 (mod (+ clearcase-comment-ring-index arg) len))
1499 (message "%d" (1+ clearcase-comment-ring-index))
1500 (insert (ring-ref clearcase-comment-ring clearcase-comment-ring-index))))))
1502 (defun clearcase-comment-search-forward (str)
1503 "Searches forwards through comment history for substring match."
1504 (interactive "sComment substring: ")
1505 (if (string= str "")
1506 (setq str clearcase-comment-last-match)
1507 (setq clearcase-comment-last-match str))
1508 (if (null clearcase-comment-ring-index)
1509 (setq clearcase-comment-ring-index 0))
1510 (let ((str (regexp-quote str))
1511 (n clearcase-comment-ring-index))
1512 (while (and (>= n 0) (not (string-match str (ring-ref clearcase-comment-ring n))))
1515 (clearcase-comment-next (- n clearcase-comment-ring-index)))
1516 (t (error "Not found")))))
1518 (defun clearcase-comment-search-reverse (str)
1519 "Searches backwards through comment history for substring match."
1520 (interactive "sComment substring: ")
1521 (if (string= str "")
1522 (setq str clearcase-comment-last-match)
1523 (setq clearcase-comment-last-match str))
1524 (if (null clearcase-comment-ring-index)
1525 (setq clearcase-comment-ring-index -1))
1526 (let ((str (regexp-quote str))
1527 (len (ring-length clearcase-comment-ring))
1528 (n (1+ clearcase-comment-ring-index)))
1529 (while (and (< n len)
1530 (not (string-match str (ring-ref clearcase-comment-ring n))))
1533 (clearcase-comment-previous (- n clearcase-comment-ring-index)))
1534 (t (error "Not found")))))
1540 ;;{{{ Major Mode: for editing config-specs.
1542 ;; The major mode function.
1544 (defun clearcase-edcs-mode ()
1546 (set-syntax-table text-mode-syntax-table)
1547 (use-local-map clearcase-edcs-mode-map)
1548 (setq major-mode 'clearcase-edcs-mode)
1549 (setq mode-name "ClearCase/edcs")
1550 (make-variable-buffer-local 'clearcase-parent-buffer)
1551 (set-buffer-modified-p nil)
1552 (setq buffer-file-name nil)
1553 (run-hooks 'text-mode-hook 'clearcase-edcs-mode-hook))
1557 (defvar clearcase-edcs-mode-map nil)
1558 (if clearcase-edcs-mode-map
1560 (setq clearcase-edcs-mode-map (make-sparse-keymap))
1561 (define-key clearcase-edcs-mode-map "\C-c\C-c" 'clearcase-edcs-finish)
1562 (define-key clearcase-edcs-mode-map "\C-x\C-s" 'clearcase-edcs-save))
1566 (defvar clearcase-edcs-tag-name nil
1567 "Name of view tag which is currently being edited")
1569 (defvar clearcase-edcs-tag-history ()
1570 "History of view tags used in clearcase-edcs-edit")
1574 (defun clearcase-edcs-edit (tag-name)
1575 "Edit a ClearCase configuration specification"
1578 (let ((vxname (clearcase-fprop-viewtag default-directory)))
1579 (if clearcase-complete-viewtags
1580 (list (directory-file-name
1581 (completing-read "View Tag: "
1582 (clearcase-viewtag-all-viewtags-obarray)
1587 'clearcase-edcs-tag-history)))
1588 (read-string "View Tag: "))))
1590 (let ((start (current-buffer))
1591 (buffer-name (format "*clearcase-config-spec-%s*" tag-name)))
1592 (kill-buffer (get-buffer-create buffer-name))
1593 (pop-to-buffer (get-buffer-create buffer-name))
1594 (auto-save-mode auto-save-default)
1596 (insert (clearcase-ct-cleartool-cmd "catcs" "-tag" tag-name))
1597 (goto-char (point-min))
1598 (re-search-forward "^[^#\n]" nil 'end)
1600 (clearcase-edcs-mode)
1601 (setq clearcase-parent-buffer start)
1602 (make-local-variable 'clearcase-edcs-tag-name)
1603 (setq clearcase-edcs-tag-name tag-name)))
1605 (defun clearcase-edcs-save ()
1607 (if (not (buffer-modified-p))
1608 (message "Configuration not changed since last saved")
1610 (message "Setting configuration for %s..." clearcase-edcs-tag-name)
1611 (clearcase-with-tempfile
1613 (write-region (point-min) (point-max) cspec-text nil 'dont-mention-it)
1614 (let ((ret (clearcase-ct-cleartool-cmd "setcs"
1616 clearcase-edcs-tag-name
1617 (clearcase-path-native cspec-text))))
1619 ;; nyi: we could be smarter and retain viewtag info and perhaps some
1620 ;; other info. For now invalidate all cached file property info.
1622 (clearcase-fprop-clear-all-properties)
1624 (set-buffer-modified-p nil)
1625 (message "Setting configuration for %s...done"
1626 clearcase-edcs-tag-name)))))
1628 (defun clearcase-edcs-finish ()
1630 (let ((old-buffer (current-buffer)))
1631 (clearcase-edcs-save)
1633 (kill-buffer old-buffer)))
1641 ;; nyi: Just an idea now.
1642 ;; Be able to present a selection of views at various times
1643 ;; - show me current file in other view
1644 ;; - top-level browse operation
1646 ;; clearcase-viewtag-started-viewtags gives us the dynamic views that are mounted.
1648 ;; How to find local snapshots ?
1650 ;; How to find drive-letter mount points for view on NT ?
1651 ;; - parse "subst" output
1657 ;;{{{ Hijack/unhijack
1659 (defun clearcase-hijack-current-buffer ()
1660 "Hijack the file in the current buffer."
1662 (clearcase-hijack buffer-file-name))
1664 (defun clearcase-hijack-dired-files ()
1665 "Hijack the selected files."
1667 (clearcase-hijack-seq (dired-get-marked-files)))
1669 (defun clearcase-unhijack-current-buffer ()
1670 "Unhijack the file in the current buffer."
1672 (clearcase-unhijack buffer-file-name))
1674 (defun clearcase-unhijack-dired-files ()
1675 "Hijack the selected files."
1677 (clearcase-unhijack-seq (dired-get-marked-files)))
1683 (defun clearcase-annotate-file (file)
1684 (let ((relative-name (file-relative-name file)))
1685 (message "Annotating %s ..." relative-name)
1686 (clearcase-with-tempfile
1688 (clearcase-ct-do-cleartool-command "annotate"
1694 (clearcase-utl-populate-and-view-buffer
1695 "*clearcase-annotate*"
1699 (insert-file-contents annotation-file)))))
1700 (message "Annotating %s ...done" relative-name)))
1702 (defun clearcase-annotate-current-buffer ()
1704 (clearcase-annotate-file buffer-file-name))
1706 (defun clearcase-annotate-dired-file ()
1707 "Annotate the selected file."
1709 (clearcase-annotate-file (dired-get-filename)))
1713 ;;{{{ nyi: Find checkouts
1715 ;; NYI: Enhance this:
1718 ;; - checkout comment
1719 ;; - permit unco/checkin
1721 (defun clearcase-find-checkouts-in-current-view ()
1722 "Find the checkouts in all vobs in the current view."
1724 (let ((viewtag (clearcase-fprop-viewtag default-directory))
1725 (dir default-directory))
1727 (let* ((ignore (message "Finding checkouts..."))
1728 (text (clearcase-ct-blocking-call "lsco"
1732 (if (zerop (length text))
1733 (message "No checkouts found")
1735 (message "Finding checkouts...done")
1737 (clearcase-utl-populate-and-view-buffer
1740 (function (lambda (s)
1745 ;;{{{ UCM operations
1749 (defun clearcase-read-new-activity-name ()
1750 "Read the name of a new activity from the minibuffer.
1751 Return nil if the empty string is entered."
1753 ;; nyi: Probably should check that the activity doesn't already exist.
1755 (let ((entered-name (read-string "Activity name (optional): " )))
1756 (if (not (zerop (length entered-name)))
1760 (defun clearcase-read-mkact-args ()
1761 "Read the name and headline arguments for clearcase-ucm-mkact-current-dir
1762 from the minibuffer."
1766 (if clearcase-prompt-for-activity-names
1767 (setq name (clearcase-read-new-activity-name)))
1768 (setq headline (read-string "Activity headline: " ))
1769 (list name headline)))
1771 (defun clearcase-make-internally-named-activity (stream-name comment-file)
1772 "Make a new activity in STREAM-NAME with creation comment in COMMENT-FILE,
1773 and use an internally-generated name for the activity."
1776 (if clearcase-set-to-new-activity
1777 (clearcase-ct-blocking-call "mkact"
1778 "-cfile" (clearcase-path-native comment-file)
1781 (clearcase-ct-blocking-call "mkact"
1783 "-cfile" (clearcase-path-native comment-file)
1787 (if (string-match "Created activity \"\\([^\"]+\\)\"" ret)
1788 (substring ret (match-beginning 1) (match-end 1))
1789 (error "Failed to create activity: %s" ret))))
1791 (defun clearcase-ucm-mkact-current-dir (name headline &optional comment)
1793 "Make an activity with NAME and HEADLINE and optional COMMENT, in the stream
1794 associated with the view associated with the current directory."
1796 (interactive (clearcase-read-mkact-args))
1797 (let* ((viewtag (clearcase-fprop-viewtag default-directory))
1798 (stream (clearcase-vprop-stream viewtag))
1799 (pvob (clearcase-vprop-pvob viewtag)))
1800 (if (not (clearcase-vprop-ucm viewtag))
1801 (error "View %s is not a UCM view" viewtag))
1803 (error "View %s has no stream" viewtag))
1805 (error "View %s has no PVOB" viewtag))
1808 ;; If no comment supplied, go and get one..
1811 (clearcase-comment-start-entry (format "new-activity-%d" (random))
1812 "Enter comment for new activity."
1813 'clearcase-ucm-mkact-current-dir
1814 (list name headline)))
1815 ;; ...else do the operation.
1817 (message "Making activity...")
1818 (clearcase-with-tempfile
1820 (write-region comment nil comment-file nil 'noprint)
1821 (let ((qualified-stream (format "%s@%s" stream pvob)))
1823 (if clearcase-set-to-new-activity
1824 (clearcase-ct-blocking-call "mkact"
1825 "-cfile" (clearcase-path-native comment-file)
1826 "-headline" headline
1827 "-in" qualified-stream
1830 (clearcase-ct-blocking-call "mkact"
1832 "-cfile" (clearcase-path-native comment-file)
1833 "-headline" headline
1834 "-in" qualified-stream
1838 ;; If no name was provided we do the creation in two steps:
1841 ;; to make sure we get preferred internally generated activity
1842 ;; name of the form "activityNNN.MMM" rather than some horrible
1843 ;; concoction based on the headline.
1845 (let ((name (clearcase-make-internally-named-activity qualified-stream comment-file)))
1846 (clearcase-ct-blocking-call "chact"
1847 "-headline" headline
1850 ;; Flush the activities for this view so they'll get refreshed when needed.
1852 (clearcase-vprop-flush-activities viewtag)
1854 (message "Making activity...done"))))
1860 (defun clearcase-ucm-filter-out-rebases (activities)
1861 (if (not clearcase-hide-rebase-activities)
1863 (clearcase-utl-list-filter
1866 (let ((id (car activity)))
1867 (not (string-match clearcase-rebase-id-regexp id)))))
1870 (defun clearcase-ucm-set-activity-current-dir ()
1872 (let* ((viewtag (clearcase-fprop-viewtag default-directory)))
1873 (if (not (clearcase-vprop-ucm viewtag))
1874 (error "View %s is not a UCM view" viewtag))
1875 ;; Filter out the rebases here if the user doesn't want to see them.
1877 (let ((activities (clearcase-ucm-filter-out-rebases (clearcase-vprop-activities viewtag))))
1878 (if (null activities)
1879 (error "View %s has no activities" viewtag))
1880 (clearcase-ucm-make-selection-window (format "*clearcase-activity-select-%s*" viewtag)
1884 (let ((id (car activity))
1885 (title (cdr activity)))
1886 (format "%s\t%s" id title))))
1889 'clearcase-ucm-activity-selection-interpreter
1890 'clearcase-ucm-set-activity
1893 (defun clearcase-ucm-activity-selection-interpreter ()
1894 "Extract the activity name from the buffer at point"
1895 (if (looking-at "^\\(.*\\)\t")
1896 (let ((activity-name (buffer-substring (match-beginning 1)
1899 (error "No activity on this line")))
1901 (defun clearcase-ucm-set-activity-none-current-dir ()
1903 (let* ((viewtag (clearcase-fprop-viewtag default-directory)))
1904 (if (not (clearcase-vprop-ucm viewtag))
1905 (error "View %s is not a UCM view" viewtag))
1906 (clearcase-ucm-set-activity viewtag nil)))
1908 (defun clearcase-ucm-set-activity (viewtag activity-name)
1913 (message "Setting activity...")
1914 (let ((qualified-activity-name (if (string-match "@" activity-name)
1916 (concat activity-name "@" (clearcase-vprop-pvob viewtag)))))
1917 (clearcase-ct-blocking-call "setactivity" "-nc" "-view"
1919 (if qualified-activity-name
1920 qualified-activity-name
1924 (clearcase-vprop-set-current-activity viewtag activity-name)
1925 (message "Setting activity...done"))
1929 (message "Unsetting activity...")
1930 (clearcase-ct-blocking-call "setactivity"
1936 (clearcase-vprop-set-current-activity viewtag nil)
1937 (message "Unsetting activity...done")))
1941 ;;{{{ Show current activity
1943 (defun clearcase-ucm-describe-current-activity ()
1945 (let* ((viewtag (clearcase-fprop-viewtag default-directory)))
1947 (error "Not in a view"))
1948 (if (not (clearcase-vprop-ucm viewtag))
1949 (error "View %s is not a UCM view" viewtag))
1950 (let ((pvob (clearcase-vprop-pvob viewtag))
1951 (current-activity (clearcase-vprop-current-activity viewtag)))
1952 (if (not current-activity)
1953 (message "No activity set")
1954 (let ((text (clearcase-ct-blocking-call "desc"
1959 (if (not (zerop (length text)))
1960 (clearcase-utl-populate-and-view-buffer
1963 (function (lambda (s)
1971 (defun clearcase-next-action-current-buffer ()
1972 "Do the next logical operation on the current file.
1973 Operations include mkelem, checkout, checkin, uncheckout"
1975 (clearcase-next-action buffer-file-name))
1977 (defun clearcase-next-action-dired-files ()
1978 "Do the next logical operation on the marked files.
1979 Operations include mkelem, checkout, checkin, uncheckout.
1980 If all the files are not in an equivalent state, an error is raised."
1983 (clearcase-next-action-seq (dired-get-marked-files)))
1985 (defun clearcase-next-action (file)
1986 (let ((action (clearcase-compute-next-action file)))
1989 ((eq action 'mkelem)
1990 (clearcase-commented-mkelem file))
1992 ((eq action 'checkout)
1993 (clearcase-commented-checkout file))
1995 ((eq action 'uncheckout)
1996 (if (yes-or-no-p "Checked-out file appears unchanged. Cancel checkout ? ")
1997 (clearcase-uncheckout file)))
1999 ((eq action 'illegal-checkin)
2000 (error "This file is checked out by someone else: %s" (clearcase-fprop-user file)))
2002 ((eq action 'checkin)
2003 (clearcase-commented-checkin file))
2006 (error "Can't compute suitable next ClearCase action for file %s" file)))))
2008 (defun clearcase-next-action-seq (files)
2009 "Do the next logical operation on the sequence of FILES."
2011 ;; Check they're all in the same state.
2013 (let ((actions (mapcar (function clearcase-compute-next-action) files)))
2014 (if (not (clearcase-utl-elts-are-eq actions))
2015 (error "Marked files are not all in the same state"))
2016 (let ((action (car actions)))
2019 ((eq action 'mkelem)
2020 (clearcase-commented-mkelem-seq files))
2022 ((eq action 'checkout)
2023 (clearcase-commented-checkout-seq files))
2025 ((eq action 'uncheckout)
2026 (if (yes-or-no-p "Checked-out files appears unchanged. Cancel checkouts ? ")
2027 (clearcase-uncheckout-seq files)))
2029 ((eq action 'illegal-checkin)
2030 (error "These files are checked out by someone else; will no checkin"))
2032 ((eq action 'checkin)
2033 (clearcase-commented-checkin-seq files))
2036 (error "Can't compute suitable next ClearCase action for marked files"))))))
2038 (defun clearcase-compute-next-action (file)
2039 "Compute the next logical action on FILE."
2042 ;; nyi: other cases to consider later:
2044 ;; - file is unreserved
2045 ;; - file is not mastered
2047 ;; Case 1: it is not yet an element
2050 ((clearcase-file-ok-to-mkelem file)
2053 ;; Case 2: file is not checked out
2056 ((clearcase-file-ok-to-checkout file)
2059 ;; Case 3: file is checked-out but not modified in buffer or disk
2060 ;; ==> offer to uncheckout
2062 ((and (clearcase-file-ok-to-uncheckout file)
2063 (not (file-directory-p file))
2064 (not (buffer-modified-p))
2065 (not (clearcase-file-appears-modified-since-checkout-p file)))
2068 ;; Case 4: file is checked-out but by somebody else using this view.
2069 ;; ==> refuse to checkin
2071 ;; This is not reliable on some Windows installations where a user is known
2072 ;; as "esler" on Unix and the ClearCase server, and "ESLER" on the Windows
2075 ((and (not clearcase-on-mswindows)
2076 (clearcase-fprop-checked-out file)
2077 (not (string= (user-login-name)
2078 (clearcase-fprop-user file))))
2081 ;; Case 5: user has checked-out the file
2084 ((clearcase-file-ok-to-checkin file)
2094 (defun clearcase-mkelem-current-buffer ()
2095 "Make the current file into a ClearCase element."
2098 ;; Watch out for new buffers of size 0: the corresponding file
2099 ;; does not exist yet, even though buffer-modified-p is nil.
2101 (if (and (not (buffer-modified-p))
2102 (zerop (buffer-size))
2103 (not (file-exists-p buffer-file-name)))
2104 (set-buffer-modified-p t))
2106 (clearcase-commented-mkelem buffer-file-name))
2108 (defun clearcase-mkelem-dired-files ()
2109 "Make the selected files into ClearCase elements."
2111 (clearcase-commented-mkelem-seq (dired-get-marked-files)))
2117 (defun clearcase-checkin-current-buffer ()
2118 "Checkin the file in the current buffer."
2121 ;; Watch out for new buffers of size 0: the corresponding file
2122 ;; does not exist yet, even though buffer-modified-p is nil.
2124 (if (and (not (buffer-modified-p))
2125 (zerop (buffer-size))
2126 (not (file-exists-p buffer-file-name)))
2127 (set-buffer-modified-p t))
2129 (clearcase-commented-checkin buffer-file-name))
2131 (defun clearcase-checkin-dired-files ()
2132 "Checkin the selected files."
2134 (clearcase-commented-checkin-seq (dired-get-marked-files)))
2136 (defun clearcase-dired-checkin-current-dir ()
2138 (clearcase-commented-checkin (dired-current-directory)))
2142 ;;{{{ Edit checkout comment
2144 (defun clearcase-edit-checkout-comment-current-buffer ()
2145 "Edit the clearcase comment for the checked-out file in the current buffer."
2147 (clearcase-edit-checkout-comment buffer-file-name))
2149 (defun clearcase-edit-checkout-comment-dired-file ()
2150 "Checkin the selected file."
2152 (clearcase-edit-checkout-comment (dired-get-filename)))
2154 (defun clearcase-edit-checkout-comment (file &optional comment)
2155 "Edit comment for FILE by popping up a buffer to accept one. If COMMENT
2156 is specified, save it."
2158 ;; If no comment supplied, go and get one...
2160 (clearcase-comment-start-entry (file-name-nondirectory file)
2161 "Edit the file's check-out comment."
2162 'clearcase-edit-checkout-comment
2163 (list buffer-file-name)
2164 (find-file-noselect file)
2165 (clearcase-fprop-comment file))
2166 ;; We have a comment, save it
2167 (clearcase-comment-save-comment-for-buffer comment clearcase-parent-buffer)))
2173 (defun clearcase-checkout-current-buffer ()
2174 "Checkout the file in the current buffer."
2176 (clearcase-commented-checkout buffer-file-name))
2178 (defun clearcase-checkout-dired-files ()
2179 "Checkout the selected files."
2181 (clearcase-commented-checkout-seq (dired-get-marked-files)))
2183 (defun clearcase-dired-checkout-current-dir ()
2185 (clearcase-commented-checkout (dired-current-directory)))
2191 (defun clearcase-uncheckout-current-buffer ()
2192 "Uncheckout the file in the current buffer."
2194 (clearcase-uncheckout buffer-file-name))
2196 (defun clearcase-uncheckout-dired-files ()
2197 "Uncheckout the selected files."
2199 (clearcase-uncheckout-seq (dired-get-marked-files)))
2201 (defun clearcase-dired-uncheckout-current-dir ()
2203 (clearcase-uncheckout (dired-current-directory)))
2209 (defun clearcase-mkbrtype (typename)
2210 (interactive "sBranch type name: ")
2211 (clearcase-commented-mkbrtype typename))
2217 (defun clearcase-describe-current-buffer ()
2218 "Give a ClearCase description of the file in the current buffer."
2220 (clearcase-describe buffer-file-name))
2222 (defun clearcase-describe-dired-file ()
2223 "Describe the selected files."
2225 (clearcase-describe (dired-get-filename)))
2231 (defun clearcase-what-rule-current-buffer ()
2233 (clearcase-what-rule buffer-file-name))
2235 (defun clearcase-what-rule-dired-file ()
2237 (clearcase-what-rule (dired-get-filename)))
2243 (defun clearcase-list-history-current-buffer ()
2244 "List the change history of the current buffer in a window."
2246 (clearcase-list-history buffer-file-name))
2248 (defun clearcase-list-history-dired-file ()
2249 "List the change history of the current file."
2251 (clearcase-list-history (dired-get-filename)))
2257 (defun clearcase-ediff-pred-current-buffer ()
2258 "Use Ediff to compare a version in the current buffer against its predecessor."
2260 (clearcase-ediff-file-with-version buffer-file-name
2261 (clearcase-fprop-predecessor-version buffer-file-name)))
2263 (defun clearcase-ediff-pred-dired-file ()
2264 "Use Ediff to compare the selected version against its predecessor."
2266 (let ((truename (clearcase-fprop-truename (dired-get-filename))))
2267 (clearcase-ediff-file-with-version truename
2268 (clearcase-fprop-predecessor-version truename))))
2270 (defun clearcase-ediff-branch-base-current-buffer()
2271 "Use Ediff to compare a version in the current buffer
2272 against the base of its branch."
2274 (clearcase-ediff-file-with-version buffer-file-name
2275 (clearcase-vxpath-version-of-branch-base buffer-file-name)))
2277 (defun clearcase-ediff-branch-base-dired-file()
2278 "Use Ediff to compare the selected version against the base of its branch."
2280 (let ((truename (clearcase-fprop-truename (dired-get-filename))))
2281 (clearcase-ediff-file-with-version truename
2282 (clearcase-vxpath-version-of-branch-base truename))))
2284 (defun clearcase-ediff-named-version-current-buffer (version)
2285 ;; nyi: if we're in history-mode, probably should just use
2288 (interactive (list (clearcase-read-version-name "Version for comparison: "
2290 (clearcase-ediff-file-with-version buffer-file-name version))
2292 (defun clearcase-ediff-named-version-dired-file (version)
2293 ;; nyi: if we're in history-mode, probably should just use
2296 (interactive (list (clearcase-read-version-name "Version for comparison: "
2297 (dired-get-filename))))
2298 (clearcase-ediff-file-with-version (clearcase-fprop-truename (dired-get-filename))
2301 (defun clearcase-ediff-file-with-version (truename other-version)
2302 (let ((other-vxpath (clearcase-vxpath-cons-vxpath (clearcase-vxpath-element-part truename)
2304 (if (clearcase-file-is-in-mvfs-p truename)
2305 (ediff-files other-vxpath truename)
2306 (ediff-buffers (clearcase-vxpath-get-version-in-buffer other-vxpath)
2307 (find-file-noselect truename t)))))
2313 (defun clearcase-gui-diff-pred-current-buffer ()
2314 "Use GUI to compare a version in the current buffer against its predecessor."
2316 (clearcase-gui-diff-file-with-version buffer-file-name
2317 (clearcase-fprop-predecessor-version buffer-file-name)))
2319 (defun clearcase-gui-diff-pred-dired-file ()
2320 "Use GUI to compare the selected version against its predecessor."
2322 (let ((truename (clearcase-fprop-truename (dired-get-filename))))
2323 (clearcase-gui-diff-file-with-version truename
2324 (clearcase-fprop-predecessor-version truename))))
2326 (defun clearcase-gui-diff-branch-base-current-buffer()
2327 "Use GUI to compare a version in the current buffer
2328 against the base of its branch."
2330 (clearcase-gui-diff-file-with-version buffer-file-name
2331 (clearcase-vxpath-version-of-branch-base buffer-file-name)))
2333 (defun clearcase-gui-diff-branch-base-dired-file()
2334 "Use GUI to compare the selected version against the base of its branch."
2336 (let ((truename (clearcase-fprop-truename (dired-get-filename))))
2337 (clearcase-gui-diff-file-with-version truename
2338 (clearcase-vxpath-version-of-branch-base truename))))
2340 (defun clearcase-gui-diff-named-version-current-buffer (version)
2341 ;; nyi: if we're in history-mode, probably should just use
2344 (interactive (list (clearcase-read-version-name "Version for comparison: "
2346 (clearcase-gui-diff-file-with-version buffer-file-name version))
2348 (defun clearcase-gui-diff-named-version-dired-file (version)
2349 ;; nyi: if we're in history-mode, probably should just use
2352 (interactive (list (clearcase-read-version-name "Version for comparison: "
2353 (dired-get-filename))))
2354 (clearcase-gui-diff-file-with-version (clearcase-fprop-truename (dired-get-filename))
2357 (defun clearcase-gui-diff-file-with-version (truename other-version)
2358 (let* ((other-vxpath (clearcase-vxpath-cons-vxpath (clearcase-vxpath-element-part truename)
2360 (other-file (if (clearcase-file-is-in-mvfs-p truename)
2362 (clearcase-vxpath-get-version-in-temp-file other-vxpath)))
2363 (gui-name (if clearcase-on-mswindows
2366 (start-process "Diff"
2369 (clearcase-path-native other-file)
2370 (clearcase-path-native truename))))
2376 (defun clearcase-diff-pred-current-buffer ()
2377 "Use Diff to compare a version in the current buffer against its predecessor."
2379 (clearcase-diff-file-with-version buffer-file-name
2380 (clearcase-fprop-predecessor-version buffer-file-name)))
2382 (defun clearcase-diff-pred-dired-file ()
2383 "Use Diff to compare the selected version against its predecessor."
2385 (let ((truename (clearcase-fprop-truename (dired-get-filename))))
2386 (clearcase-diff-file-with-version truename
2387 (clearcase-fprop-predecessor-version truename))))
2389 (defun clearcase-diff-branch-base-current-buffer()
2390 "Use Diff to compare a version in the current buffer
2391 against the base of its branch."
2393 (clearcase-diff-file-with-version buffer-file-name
2394 (clearcase-vxpath-version-of-branch-base buffer-file-name)))
2396 (defun clearcase-diff-branch-base-dired-file()
2397 "Use Diff to compare the selected version against the base of its branch."
2399 (let ((truename (clearcase-fprop-truename (dired-get-filename))))
2400 (clearcase-diff-file-with-version truename
2401 (clearcase-vxpath-version-of-branch-base truename))))
2403 (defun clearcase-diff-named-version-current-buffer (version)
2404 ;; nyi: if we're in history-mode, probably should just use
2407 (interactive (list (clearcase-read-version-name "Version for comparison: "
2409 (clearcase-diff-file-with-version buffer-file-name version))
2411 (defun clearcase-diff-named-version-dired-file (version)
2412 ;; nyi: if we're in history-mode, probably should just use
2415 (interactive (list (clearcase-read-version-name "Version for comparison: "
2416 (dired-get-filename))))
2417 (clearcase-diff-file-with-version (clearcase-fprop-truename (dired-get-filename))
2420 (defun clearcase-diff-file-with-version (truename other-version)
2421 (let ((other-vxpath (clearcase-vxpath-cons-vxpath (clearcase-vxpath-element-part truename)
2423 (if (clearcase-file-is-in-mvfs-p truename)
2424 (clearcase-diff-files other-vxpath truename)
2425 (clearcase-diff-files (clearcase-vxpath-get-version-in-temp-file other-vxpath)
2432 (defun clearcase-version-other-window (version)
2435 (clearcase-read-version-name (format "Version of %s to visit: "
2436 (file-name-nondirectory buffer-file-name))
2438 (find-file-other-window (clearcase-vxpath-cons-vxpath
2439 (clearcase-vxpath-element-part buffer-file-name)
2442 (defun clearcase-browse-vtree-current-buffer ()
2444 (clearcase-browse-vtree buffer-file-name))
2446 (defun clearcase-browse-vtree-dired-file ()
2448 (clearcase-browse-vtree (dired-get-filename)))
2454 (defun clearcase-gui-vtree-browser-current-buffer ()
2456 (clearcase-gui-vtree-browser buffer-file-name))
2458 (defun clearcase-gui-vtree-browser-dired-file ()
2460 (clearcase-gui-vtree-browser (dired-get-filename)))
2462 (defun clearcase-gui-vtree-browser (file)
2463 (let ((gui-name (if clearcase-on-mswindows
2466 (start-process-shell-command "Vtree_browser"
2469 (clearcase-path-native file))))
2475 (defun clearcase-gui-clearexplorer ()
2477 (start-process-shell-command "ClearExplorer"
2482 (defun clearcase-gui-rebase ()
2484 (start-process-shell-command "Rebase"
2487 (if clearcase-on-mswindows
2491 (defun clearcase-gui-deliver ()
2493 (start-process-shell-command "Deliver"
2496 (if clearcase-on-mswindows
2500 (defun clearcase-gui-merge-manager ()
2502 (start-process-shell-command "Merge_manager"
2506 (defun clearcase-gui-project-explorer ()
2508 (start-process-shell-command "Project_explorer"
2512 (defun clearcase-gui-snapshot-view-updater ()
2514 (start-process-shell-command "View_updater"
2520 ;;{{{ Update snapshot
2522 ;; In a file buffer:
2523 ;; - update current-file
2524 ;; - update directory
2527 ;; - update marked files
2530 ;; We allow several simultaneous updates, but only one per view.
2532 (defun clearcase-update-view ()
2534 (clearcase-update (clearcase-fprop-viewtag default-directory)))
2536 (defun clearcase-update-default-directory ()
2538 (clearcase-update (clearcase-fprop-viewtag default-directory)
2541 (defun clearcase-update-current-buffer ()
2543 (clearcase-update (clearcase-fprop-viewtag default-directory)
2546 (defun clearcase-update-dired-files ()
2548 (apply (function clearcase-update)
2549 (cons (clearcase-fprop-viewtag default-directory)
2550 (dired-get-marked-files))))
2559 ;;{{{ Basic ClearCase operations
2561 ;;{{{ Update snapshot view
2563 ;;{{{ Asynchronous post-processing of update
2565 (defvar clearcase-post-update-timer nil)
2566 (defvar clearcase-post-update-work-queue nil)
2568 (defun clearcase-post-update-schedule-work (buffer)
2569 (clearcase-trace "entering clearcase-post-update-schedule-work")
2570 ;; Add to the work queue.
2572 (setq clearcase-post-update-work-queue (cons buffer
2573 clearcase-post-update-work-queue))
2574 ;; Create the timer if necessary.
2576 (if (null clearcase-post-update-timer)
2577 (if clearcase-xemacs-p
2580 (setq clearcase-post-update-timer
2581 (run-with-idle-timer 2 t 'clearcase-post-update-timer-function))
2585 (setq clearcase-post-update-timer (timer-create))
2586 (timer-set-function clearcase-post-update-timer 'clearcase-post-update-timer-function)
2587 (timer-set-idle-time clearcase-post-update-timer 2)
2588 (timer-activate-when-idle clearcase-post-update-timer)))
2589 (clearcase-trace "clearcase-post-update-schedule-work: post-update timer found to be non-null")))
2592 (defun clearcase-post-update-timer-function ()
2593 (clearcase-trace "Entering clearcase-post-update-timer-function")
2594 ;; For (each update-process buffer in the work queue)
2595 ;; if (its process has successfully terminated)
2596 ;; do the post-processing for this update
2597 ;; remove it from the work queue
2599 (clearcase-trace (format "Queue before: %s" clearcase-post-update-work-queue))
2600 (setq clearcase-post-update-work-queue
2602 (clearcase-utl-list-filter
2603 (function clearcase-post-update-check-process-buffer)
2604 clearcase-post-update-work-queue))
2606 (clearcase-trace (format "Queue after: %s" clearcase-post-update-work-queue))
2607 ;; If the work queue is now empty cancel the timer.
2609 (if (null clearcase-post-update-work-queue)
2611 (cancel-timer clearcase-post-update-timer)
2612 (setq clearcase-post-update-timer nil))))
2614 (defun clearcase-post-update-check-process-buffer (buffer)
2615 (clearcase-trace "Entering clearcase-post-update-check-process-buffer")
2617 ;; return t for those buffers that should remain in the work queue
2619 ;; if it has terminated successfully
2620 ;; go sync buffers on the files that were updated
2622 ;; We want to field errors here and when they occurm return nil to avoid a
2625 ;;(condition-case nil
2628 (let ((proc (get-buffer-process buffer)))
2630 ;; Process still exists so keep this on the work queue.
2633 (clearcase-trace "Update process still exists")
2636 ;; Process no longer there, cleaned up by comint code.
2639 ;; Sync any buffers that need it.
2641 (clearcase-trace "Update process finished")
2642 (clearcase-sync-after-scopes-updated (with-current-buffer buffer
2643 ;; Evaluate buffer-local variable.
2645 clearcase-update-buffer-scopes))
2647 ;; Remove from work queue
2651 ;; Error occurred, make sure we return nil to remove the buffer from the
2652 ;; work queue, or a loop could develop.
2657 (defun clearcase-sync-after-scopes-updated (scopes)
2658 (clearcase-trace "Entering clearcase-sync-after-scopes-updated")
2660 ;; nyi: reduce scopes to minimal set of disjoint scopes
2662 ;; Use dynamic binding here since we don't have lexical binding.
2664 (let ((clearcase-dynbound-updated-scopes scopes))
2666 ;; For all buffers...
2671 (let ((visited-file (buffer-file-name buffer)))
2673 (if (clearcase-path-file-in-any-scopes visited-file
2674 clearcase-dynbound-updated-scopes)
2675 ;; This buffer visits a file within an updated scope.
2676 ;; Sync it from disk if it needs it.
2678 (clearcase-sync-from-disk-if-needed visited-file))
2680 ;; Buffer is not visiting a file. If it is a dired-mode buffer
2681 ;; under one of the scopes, revert it.
2683 (with-current-buffer buffer
2684 (if (eq 'dired-mode major-mode)
2685 (if (clearcase-path-file-in-any-scopes default-directory
2686 clearcase-dynbound-updated-scopes)
2687 (dired-revert nil t))))))))
2692 ;; Silence compiler complaints about free variable.
2694 (defvar clearcase-update-buffer-viewtag nil)
2696 (defun clearcase-update (viewtag &rest files)
2697 "Run a cleartool+update process in VIEWTAG
2698 if there isn't one already running in that view.
2699 Other arguments FILES indicate files to update"
2701 ;; Check that there is no update process running in that view.
2703 (if (apply (function clearcase-utl-or-func)
2704 (mapcar (function (lambda (proc)
2705 (if (not (eq 'exit (process-status proc)))
2706 (let ((buf (process-buffer proc)))
2708 (assq 'clearcase-update-buffer-viewtag
2709 (buffer-local-variables buf))
2713 clearcase-update-buffer-viewtag)))))))
2715 (error "There is already an update running in view %s" viewtag))
2718 ;; - create a process in a buffer
2719 ;; - rename the buffer to be of the form *clearcase-update*<N>
2720 ;; - mark it as one of ours by setting clearcase-update-buffer-viewtag
2722 (pop-to-buffer (apply (function make-comint)
2723 (append (list "*clearcase-update-temp-name*"
2724 clearcase-cleartool-path
2729 (rename-buffer "*clearcase-update*" t)
2731 ;; Store in this buffer what view was being updated and what files.
2733 (set (make-local-variable 'clearcase-update-buffer-viewtag) viewtag)
2734 (set (make-local-variable 'clearcase-update-buffer-scopes) files)
2736 ;; nyi: schedule post-update buffer syncing
2737 (clearcase-post-update-schedule-work (current-buffer)))
2743 (defun clearcase-file-ok-to-hijack (file)
2745 "Test if FILE is suitable for hijack."
2749 ;; If it is writeable already, no need to offer a hijack operation, even
2750 ;; though, according to ClearCase, it may not yet be hijacked.
2752 ;;(not (file-writable-p file))
2754 (not (clearcase-fprop-hijacked file))
2755 (clearcase-file-is-in-view-p file)
2756 (not (clearcase-file-is-in-mvfs-p file))
2757 (eq 'version (clearcase-fprop-mtype file))
2758 (not (clearcase-fprop-checked-out file))))
2760 (defun clearcase-hijack-seq (files)
2763 (message "Hijacking...")
2767 (if (not (file-directory-p file))
2768 (clearcase-hijack file))))
2772 (message "Hijacking...done")))
2774 (defun clearcase-hijack (file)
2777 ;; - buffer/files modtimes are equal
2778 ;; - file more recent
2780 ;; - buffer more recent
2781 ;; ==> make file writeable; save buffer ?
2784 ;; - file is hijacked wrt. CC
2785 ;; - buffer is in sync with disk contents, modtime and writeability
2786 ;; except if the user refused to save
2788 (if (not (file-writable-p file))
2789 ;; Make it writeable.
2791 (clearcase-utl-make-writeable file))
2793 ;; Attempt to modify the modtime of the file on disk, otherwise ClearCase
2794 ;; won't actually deem it hijacked. This will silently fail if there is no
2795 ;; "touch" command command available.
2797 (clearcase-utl-touch-file file)
2799 ;; Sync up any buffers.
2801 (clearcase-sync-from-disk file t))
2807 (defun clearcase-file-ok-to-unhijack (file)
2808 "Test if FILE is suitable for unhijack."
2809 (clearcase-fprop-hijacked file))
2811 (defun clearcase-unhijack (file)
2812 (clearcase-unhijack-seq (list file)))
2814 (defun cleartool-unhijack-parse-for-kept-files (ret snapshot-view-root)
2815 ;; Look for occurrences of:
2816 ;; Loading "source\emacs\.emacs.el" (296690 bytes).
2817 ;; (renaming original hijacked object to ".emacs.el.keep.10").
2821 (while (string-match
2822 "^Loading \"\\([^\"]+\\)\"[^\n]+\n(renaming original hijacked object to \"\\([^\"]+\\)\")\\.\n"
2825 (let* ((elt-path (substring ret (match-beginning 1) (match-end 1)))
2826 (abs-elt-path (concat (if snapshot-view-root
2830 (abs-elt-dir (file-name-directory abs-elt-path ))
2831 (kept-file-rel (concat abs-elt-dir
2832 (substring ret (match-beginning 2) (match-end 2))))
2834 ;; This is necessary on Windows to get an absolute path, i.e. one
2835 ;; with a drive letter. Note: probably only correct if
2836 ;; unhijacking files in a single snapshot view, mounted on a
2839 (kept-file (expand-file-name kept-file-rel)))
2840 (setq kept-files (cons kept-file kept-files)))
2841 (setq start (match-end 0)))
2844 (defun clearcase-utl-files-in-same-view-p (files)
2845 (if (< (length files) 2)
2847 (let ((v0 (clearcase-fprop-viewtag (nth 0 files)))
2848 (v1 (clearcase-fprop-viewtag (nth 1 files))))
2849 (if (or (not (stringp v0))
2851 (not (string= v0 v1)))
2853 (clearcase-utl-files-in-same-view-p (cdr files))))))
2855 (defun clearcase-unhijack-seq (files)
2857 ;; Check: there are no directories involved.
2862 (if (file-directory-p file)
2863 (error "Cannot unhijack a directory"))))
2866 ;; Check: all files are in the same snapshot view.
2868 ;; (Why ? The output from ct+update only has view-root-relative paths
2869 ;; and we need to obtain absolute paths of renamed-aside hijacks if we are to
2870 ;; dired-relist them.)
2872 ;; Alternative: partition the set, with each partition containing elements in
2875 (if (not (clearcase-utl-files-in-same-view-p files))
2876 (error "Can't unhijack files in different views in the same operation"))
2878 ;; Run the scoped workspace update synchronously.
2882 (message "Unhijacking...")
2883 (let* ((ret (apply (function clearcase-ct-blocking-call)
2884 (append (list "update"
2885 (if clearcase-keep-unhijacks
2888 "-log" clearcase-sink-file-name)
2890 (snapshot-view-root (clearcase-file-snapshot-root (car files)))
2892 ;; Scan for renamed-aside files.
2894 (kept-files (if clearcase-keep-unhijacks
2895 (cleartool-unhijack-parse-for-kept-files ret
2899 ;; Do post-update synchronisation.
2902 (function clearcase-sync-after-file-updated-from-vob)
2905 ;; Update any dired buffers as to the existence of the kept files.
2907 (if clearcase-keep-unhijacks
2910 (dired-relist-file file)))
2914 (message "Unhijacking...done")))
2920 (defun clearcase-file-ok-to-mkelem (file)
2921 "Test if FILE is okay to mkelem."
2922 (let ((mtype (clearcase-fprop-mtype file)))
2923 (and (not (file-directory-p file))
2924 (and (or (equal 'view-private-object mtype)
2925 (equal 'derived-object mtype))
2926 (not (clearcase-fprop-hijacked file))
2927 (not (clearcase-file-covers-element-p file))))))
2929 (defun clearcase-assert-file-ok-to-mkelem (file)
2930 "Raise an exception if FILE is not suitable for mkelem."
2931 (if (not (clearcase-file-ok-to-mkelem file))
2932 (error "%s cannot be made into an element" file)))
2934 (defun clearcase-commented-mkelem (file &optional okay-to-checkout-dir-first comment)
2935 "Create a new element from FILE. If OKAY-TO-CHECKOUT-DIR-FIRST is non-nil,
2936 the containing directory will be checked out if necessary.
2937 If COMMENT is non-nil, it will be used, otherwise the user will be prompted
2942 (clearcase-assert-file-ok-to-mkelem file)
2944 (let ((containing-dir (file-name-directory file)))
2948 (if (not (eq 'directory-version (clearcase-fprop-mtype containing-dir)))
2949 (error "Parent directory of %s is not a ClearCase versioned directory."
2952 ;; Determine if we'll need to checkout the parent directory first.
2954 (let ((dir-checkout-needed (not (clearcase-fprop-checked-out containing-dir))))
2955 (if dir-checkout-needed
2957 ;; Parent dir will need to be checked out. Get permission if
2960 (if (null okay-to-checkout-dir-first)
2961 (setq okay-to-checkout-dir-first
2962 (or (null clearcase-verify-pre-mkelem-dir-checkout)
2963 (y-or-n-p (format "Checkout directory %s " containing-dir)))))
2964 (if (null okay-to-checkout-dir-first)
2965 (error "Can't make an element unless directory is checked-out."))))
2968 ;; If no comment supplied, go and get one...
2970 (clearcase-comment-start-entry (file-name-nondirectory file)
2971 "Enter initial comment for the new element."
2972 'clearcase-commented-mkelem
2973 (list file okay-to-checkout-dir-first)
2974 (find-file-noselect file)
2975 clearcase-initial-mkelem-comment)
2977 ;; ...otherwise perform the operation.
2980 ;; We may need to checkout the directory.
2982 (if dir-checkout-needed
2983 (clearcase-commented-checkout containing-dir comment))
2985 (clearcase-fprop-unstore-properties file)
2987 (message "Making element %s..." file)
2990 ;; Sync the buffer to disk.
2992 (let ((buffer-on-file (find-buffer-visiting file)))
2995 (set-buffer buffer-on-file)
2996 (clearcase-sync-to-disk))))
2998 (clearcase-ct-do-cleartool-command "mkelem"
3001 (if clearcase-checkin-on-mkelem
3003 (message "Making element %s...done" file)
3007 (clearcase-sync-from-disk file t))))))
3009 (defun clearcase-commented-mkelem-seq (files &optional comment)
3010 "Mkelem a sequence of FILES. If COMMENT is supplied it will be
3011 used, otherwise the user will be prompted to enter one."
3014 (function clearcase-assert-file-ok-to-mkelem)
3018 ;; No comment supplied, go and get one...
3020 (clearcase-comment-start-entry "mkelem"
3021 "Enter comment for elements' creation"
3022 'clearcase-commented-mkelem-seq
3024 ;; ...otherwise operate.
3029 (clearcase-commented-mkelem file nil comment)))
3036 (defun clearcase-file-ok-to-checkin (file)
3037 "Test if FILE is suitable for checkin."
3038 (let ((me (user-login-name)))
3039 (equal me (clearcase-fprop-owner-of-checkout file))))
3041 (defun clearcase-assert-file-ok-to-checkin (file)
3042 "Raise an exception if FILE is not suitable for checkin."
3043 (if (not (clearcase-file-ok-to-checkin file))
3044 (error "You cannot checkin %s" file)))
3046 (defun clearcase-commented-checkin (file &optional comment)
3047 "Check-in FILE with COMMENT. If the comment is omitted,
3048 a buffer is popped up to accept one."
3050 (clearcase-assert-file-ok-to-checkin file)
3053 ;; If no comment supplied, go and get one..
3056 (clearcase-comment-start-entry (file-name-nondirectory file)
3057 "Enter a checkin comment."
3058 'clearcase-commented-checkin
3060 (find-file-noselect file)
3061 (clearcase-fprop-comment file))
3063 ;; Also display a diff, if that is the custom:
3065 (if (and (not (file-directory-p file))
3066 clearcase-diff-on-checkin)
3068 (let ((tmp-buffer (current-buffer)))
3069 (message "Running diff...")
3070 (clearcase-diff-file-with-version file
3071 (clearcase-fprop-predecessor-version file))
3072 (message "Running diff...done")
3073 (set-buffer "*clearcase*")
3074 (if (get-buffer "*clearcase-diff*")
3075 (kill-buffer "*clearcase-diff*"))
3076 (rename-buffer "*clearcase-diff*")
3077 (pop-to-buffer tmp-buffer)))))
3079 ;; ...otherwise perform the operation.
3081 (message "Checking in %s..." file)
3083 ;; Sync the buffer to disk, and get local value of clearcase-checkin-arguments
3085 (let ((buffer-on-file (find-buffer-visiting file)))
3088 (set-buffer buffer-on-file)
3089 (clearcase-sync-to-disk))))
3090 (clearcase-ct-do-cleartool-command "ci"
3093 clearcase-checkin-arguments))
3094 (message "Checking in %s...done" file)
3098 (clearcase-sync-from-disk file t)))
3100 (defun clearcase-commented-checkin-seq (files &optional comment)
3101 "Checkin a sequence of FILES. If COMMENT is supplied it will be
3102 used, otherwise the user will be prompted to enter one."
3104 ;; Check they're all in the right state to be checked-in.
3107 (function clearcase-assert-file-ok-to-checkin)
3111 ;; No comment supplied, go and get one...
3113 (clearcase-comment-start-entry "checkin"
3114 "Enter checkin comment."
3115 'clearcase-commented-checkin-seq
3117 ;; ...otherwise operate.
3122 (clearcase-commented-checkin file comment)))
3129 (defun clearcase-file-ok-to-checkout (file)
3130 "Test if FILE is suitable for checkout."
3131 (let ((mtype (clearcase-fprop-mtype file)))
3132 (and (or (eq 'version mtype)
3133 (eq 'directory-version mtype)
3134 (clearcase-fprop-hijacked file))
3135 (not (clearcase-fprop-checked-out file)))))
3137 (defun clearcase-assert-file-ok-to-checkout (file)
3138 "Raise an exception if FILE is not suitable for checkout."
3139 (if (not (clearcase-file-ok-to-checkout file))
3140 (error "You cannot checkout %s" file)))
3142 ;; nyi: Offer to setact if appropriate
3144 (defun clearcase-commented-checkout (file &optional comment)
3145 "Check-out FILE with COMMENT. If the comment is omitted,
3146 a buffer is popped up to accept one."
3148 (clearcase-assert-file-ok-to-checkout file)
3150 (if (and (null comment)
3151 (not clearcase-suppress-checkout-comments))
3152 ;; If no comment supplied, go and get one...
3154 (clearcase-comment-start-entry (file-name-nondirectory file)
3155 "Enter a checkout comment."
3156 'clearcase-commented-checkout
3158 (find-file-noselect file))
3160 ;; ...otherwise perform the operation.
3162 (message "Checking out %s..." file)
3163 ;; Change buffers to get local value of clearcase-checkin-arguments.
3166 (set-buffer (or (find-buffer-visiting file)
3168 (clearcase-ct-do-cleartool-command "co"
3171 clearcase-checkout-arguments))
3172 (message "Checking out %s...done" file)
3176 (clearcase-sync-from-disk file t)))
3179 (defun clearcase-commented-checkout-seq (files &optional comment)
3180 "Checkout a sequence of FILES. If COMMENT is supplied it will be
3181 used, otherwise the user will be prompted to enter one."
3184 (function clearcase-assert-file-ok-to-checkout)
3187 (if (and (null comment)
3188 (not clearcase-suppress-checkout-comments))
3189 ;; No comment supplied, go and get one...
3191 (clearcase-comment-start-entry "checkout"
3192 "Enter a checkout comment."
3193 'clearcase-commented-checkout-seq
3195 ;; ...otherwise operate.
3200 (clearcase-commented-checkout file comment)))
3207 (defun clearcase-file-ok-to-uncheckout (file)
3208 "Test if FILE is suitable for uncheckout."
3209 (equal (user-login-name)
3210 (clearcase-fprop-owner-of-checkout file)))
3212 (defun clearcase-assert-file-ok-to-uncheckout (file)
3213 "Raise an exception if FILE is not suitable for uncheckout."
3214 (if (not (clearcase-file-ok-to-uncheckout file))
3215 (error "You cannot uncheckout %s" file)))
3217 (defun cleartool-unco-parse-for-kept-file (ret)
3218 ;;Private version of "foo" saved in "foo.keep.1"
3219 (if (string-match "^Private version of .* saved in \"\\([^\"]+\\)\"\\.$" ret)
3220 (substring ret (match-beginning 1) (match-end 1))
3223 (defun clearcase-uncheckout (file)
3226 (clearcase-assert-file-ok-to-uncheckout file)
3228 ;; If it has changed since checkout, insist the user confirm.
3230 (if (and (not (file-directory-p file))
3231 (clearcase-file-appears-modified-since-checkout-p file)
3232 (not clearcase-suppress-confirm)
3233 (not (yes-or-no-p (format "Really discard changes to %s ?" file))))
3234 (message "Uncheckout of %s cancelled" file)
3236 ;; Go ahead and unco.
3238 (message "Cancelling checkout of %s..." file)
3240 ;; - Prompt for -keep or -rm
3241 ;; - offer to remove /0 branches
3243 (let* ((ret (clearcase-ct-blocking-call "unco"
3244 (if clearcase-keep-uncheckouts
3248 ;; Discover the name of the saved.
3250 (kept-file (if clearcase-keep-uncheckouts
3251 (cleartool-unco-parse-for-kept-file ret)
3255 (message "Checkout of %s cancelled (saved in %s)"
3256 (file-name-nondirectory kept-file)
3258 (message "Cancelling checkout of %s...done" file))
3260 ;; Sync any buffers over the file itself.
3262 (clearcase-sync-from-disk file t)
3264 ;; Update any dired buffers as to the existence of the kept file.
3267 (dired-relist-file kept-file)))))
3269 (defun clearcase-uncheckout-seq (files)
3270 "Uncheckout a sequence of FILES."
3273 (function clearcase-assert-file-ok-to-uncheckout)
3277 (function clearcase-uncheckout)
3284 (defun clearcase-describe (file)
3285 "Give a ClearCase description of FILE."
3287 (clearcase-utl-populate-and-view-buffer
3292 (clearcase-ct-do-cleartool-command "describe" file 'unused)))))
3294 (defun clearcase-describe-seq (files)
3295 "Give a ClearCase description of the sequence of FILES."
3296 (error "Not yet implemented"))
3302 (defun clearcase-commented-mkbrtype (typename &optional comment)
3304 (clearcase-comment-start-entry (format "mkbrtype:%s" typename)
3305 "Enter a comment for the new branch type."
3306 'clearcase-commented-mkbrtype
3308 (clearcase-with-tempfile
3310 (write-region comment nil comment-file nil 'noprint)
3311 (let ((qualified-typename typename))
3312 (if (not (string-match "@" typename))
3313 (setq qualified-typename
3314 (format "%s@%s" typename default-directory)))
3316 (clearcase-ct-cleartool-cmd "mkbrtype"
3318 (clearcase-path-native comment-file)
3319 qualified-typename)))))
3323 ;;{{{ Browse vtree (using Dired Mode)
3325 (defun clearcase-file-ok-to-browse (file)
3327 (or (equal 'version (clearcase-fprop-mtype file))
3328 (equal 'directory-version (clearcase-fprop-mtype file)))
3329 (clearcase-file-is-in-mvfs-p file)))
3331 (defun clearcase-browse-vtree (file)
3332 (if (not (clearcase-fprop-file-is-version-p file))
3333 (error "%s is not a Clearcase element" file))
3335 (if (not (clearcase-file-is-in-mvfs-p file))
3336 (error "File is not in MVFS"))
3338 (let* ((version-path (clearcase-vxpath-cons-vxpath
3340 (or (clearcase-vxpath-version-part file)
3341 (clearcase-fprop-version file))))
3342 ;; nyi: Can't seem to get latest first here.
3344 (dired-listing-switches (concat dired-listing-switches
3347 (branch-path (clearcase-vxpath-branch version-path))
3349 ;; Position cursor to the version we came from.
3350 ;; If it was checked-out, go to predecessor.
3352 (version-number (clearcase-vxpath-version
3353 (if (clearcase-fprop-checked-out file)
3354 (clearcase-fprop-predecessor-version file)
3357 (if (file-exists-p version-path)
3359 ;; Invoke dired on the directory of the version branch.
3363 (clearcase-dired-sort-by-date)
3365 (if (re-search-forward (concat "[ \t]+"
3367 (regexp-quote version-number)
3372 (goto-char (match-beginning 1))))
3373 (dired (concat file clearcase-vxpath-glue))
3375 ;; nyi: We want ANY directory in the history tree to appear with
3376 ;; newest first. Probably requires a hook to dired mode.
3378 (clearcase-dired-sort-by-date))))
3384 (defun clearcase-list-history (file)
3385 "List the change history of FILE.
3387 FILE can be a file or a directory. If it is a directory, only the information
3388 on the directory element itself is listed, not on its contents."
3390 (let ((mtype (clearcase-fprop-mtype file)))
3391 (if (or (eq mtype 'version)
3392 (eq mtype 'directory-version))
3394 (message "Listing element history...")
3396 (clearcase-utl-populate-and-view-buffer
3401 (clearcase-ct-do-cleartool-command "lshistory"
3404 (if (eq mtype 'directory-version)
3406 (setq default-directory (file-name-directory file))
3407 (while (looking-at "=3D*\n")
3408 (delete-char (- (match-end 0) (match-beginning 0)))
3410 (goto-char (point-min))
3411 (if (looking-at "[\b\t\n\v\f\r ]+")
3412 (delete-char (- (match-end 0) (match-beginning 0)))))))
3413 (message "Listing element history...done"))
3415 (error "%s is not a ClearCase element" file))))
3421 (defun clearcase-files-are-identical (f1 f2)
3422 "Test if FILE1 and FILE2 have identical contents."
3424 (clearcase-when-debugging
3425 (if (not (file-exists-p f1))
3426 (error "%s non-existent" f1))
3427 (if (not (file-exists-p f2))
3428 (error "%s non-existent" f2)))
3430 (zerop (call-process "cleardiff" nil nil nil "-status_only" f1 f2)))
3432 (defun clearcase-diff-files (file1 file2)
3433 "Run cleardiff on FILE1 and FILE2 and display the differences."
3434 (if clearcase-use-normal-diff
3435 (clearcase-do-command 2
3436 clearcase-normal-diff-program
3438 (append clearcase-normal-diff-arguments
3440 (clearcase-do-command 2
3443 (list "-diff_format" file1)))
3444 (let ((diff-size (save-excursion
3445 (set-buffer "*clearcase*")
3447 (if (zerop diff-size)
3448 (message "No differences")
3449 (clearcase-port-view-buffer-other-window "*clearcase*")
3451 (shrink-window-if-larger-than-buffer))))
3457 (defun clearcase-what-rule (file)
3458 (let ((result (clearcase-ct-cleartool-cmd "ls"
3460 (clearcase-path-native file))))
3461 (if (string-match "Rule: \\(.*\\)\n" result)
3462 (message (substring result
3463 ;; Be a little more verbose
3464 (match-beginning 0) (match-end 1)))
3471 ;;{{{ File property cache
3473 ;; ClearCase properties of files are stored in a vector in a hashtable with the
3474 ;; absolute-filename (with no trailing slashes) as the lookup key.
3478 ;; [0] truename : string
3479 ;; [1] mtype : { nil, view-private-object, version,
3480 ;; directory-version, file-element,
3481 ;; dir-element, derived-object
3483 ;; [2] checked-out : boolean
3484 ;; [3] reserved : boolean
3485 ;; [4] version : string
3486 ;; [5] predecessor-version : string
3488 ;; [7] user : string
3489 ;; [8] date : string (yyyymmdd.hhmmss)
3490 ;; [9] time-last-described : (N, N, N) time when the properties were last read
3492 ;; [10] viewtag : string
3493 ;; [11] comment : string
3494 ;; [12] slink-text : string (empty string if not symlink)
3495 ;; [13] hijacked : boolean
3497 ;; nyi: other possible properties to record:
3498 ;; mtime when last described (lets us know when the cached properties
3503 (defun clearcase-fprop-unparse-properties (properties)
3504 "Return a string suitable for printing PROPERTIES."
3506 (format "truename: %s\n" (aref properties 0))
3507 (format "mtype: %s\n" (aref properties 1))
3508 (format "checked-out: %s\n" (aref properties 2))
3509 (format "reserved: %s\n" (aref properties 3))
3510 (format "version: %s\n" (aref properties 4))
3511 (format "predecessor-version: %s\n" (aref properties 5))
3512 (format "oid: %s\n" (aref properties 6))
3513 (format "user: %s\n" (aref properties 7))
3514 (format "date: %s\n" (aref properties 8))
3515 (format "time-last-described: %s\n" (current-time-string (aref properties 9)))
3516 (format "viewtag: %s\n" (aref properties 10))
3517 (format "comment: %s\n" (aref properties 11))
3518 (format "slink-text: %s\n" (aref properties 12))
3519 (format "hijacked: %s\n" (aref properties 13))))
3521 (defun clearcase-fprop-display-properties (file)
3522 "Display the recorded ClearCase properties of FILE."
3524 (let* ((abs-file (expand-file-name file))
3525 (properties (clearcase-fprop-lookup-properties abs-file)))
3527 (let ((unparsed-properties (clearcase-fprop-unparse-properties properties)))
3528 (clearcase-utl-populate-and-view-buffer
3531 (function (lambda ()
3532 (insert unparsed-properties)))))
3533 (error "Properties for %s not stored" file))))
3535 (defun clearcase-fprop-dump-to-current-buffer ()
3536 "Dump to the current buffer the table recording ClearCase properties of files."
3538 (insert (format "File describe count: %s\n" clearcase-fprop-describe-count))
3542 (let ((properties (symbol-value symbol)))
3544 (format "key: %s\n" (symbol-name symbol))
3546 (clearcase-fprop-unparse-properties properties)))))
3547 clearcase-fprop-hashtable)
3550 (defun clearcase-fprop-dump ()
3552 (clearcase-utl-populate-and-view-buffer
3555 (function (lambda ()
3556 (clearcase-fprop-dump-to-current-buffer)))))
3560 (defvar clearcase-fprop-hashtable (make-vector 31 0)
3561 "Obarray for per-file ClearCase properties.")
3563 (defun clearcase-fprop-canonicalise-path (filename)
3564 ;; We want DIR/y and DIR\y to map to the same cache entry on ms-windows.
3565 ;; We want DIR and DIR/ (and on windows DIR\) to map to the same cache entry.
3567 ;; However, on ms-windows avoid canonicalising X:/ to X: because, for some
3568 ;; reason, cleartool+desc fails on X:, but works on X:/
3570 (setq filename (clearcase-path-canonicalise-slashes filename))
3571 (if (and clearcase-on-mswindows
3572 (string-match (concat "^" "[A-Za-z]:" clearcase-pname-sep-regexp "$")
3575 (clearcase-utl-strip-trailing-slashes filename)))
3577 (defun clearcase-fprop-clear-all-properties ()
3578 "Delete all entries in the clearcase-fprop-hashtable."
3579 (setq clearcase-fprop-hashtable (make-vector 31 0)))
3581 (defun clearcase-fprop-store-properties (file properties)
3582 "For FILE, store its ClearCase PROPERTIES in the clearcase-fprop-hashtable."
3583 (assert (file-name-absolute-p file))
3584 (set (intern (clearcase-fprop-canonicalise-path file)
3585 clearcase-fprop-hashtable) properties))
3587 (defun clearcase-fprop-unstore-properties (file)
3588 "For FILE, delete its entry in the clearcase-fprop-hashtable."
3589 (assert (file-name-absolute-p file))
3590 (unintern (clearcase-fprop-canonicalise-path file) clearcase-fprop-hashtable))
3592 (defun clearcase-fprop-lookup-properties (file)
3593 "For FILE, lookup and return its ClearCase properties from the
3594 clearcase-fprop-hashtable."
3595 (assert (file-name-absolute-p file))
3596 (symbol-value (intern-soft (clearcase-fprop-canonicalise-path file)
3597 clearcase-fprop-hashtable)))
3599 (defun clearcase-fprop-get-properties (file)
3600 "For FILE, make sure its ClearCase properties are in the hashtable
3601 and then return them."
3602 (or (clearcase-fprop-lookup-properties file)
3604 (condition-case signal-info
3605 (clearcase-fprop-read-properties file)
3608 (clearcase-trace (format "(clearcase-fprop-read-properties %s) signalled error: %s"
3611 (make-vector 31 nil))))))
3612 (clearcase-fprop-store-properties file properties)
3615 (defun clearcase-fprop-truename (file)
3616 "For FILE, return its \"truename\" ClearCase property."
3617 (aref (clearcase-fprop-get-properties file) 0))
3619 (defun clearcase-fprop-mtype (file)
3620 "For FILE, return its \"mtype\" ClearCase property."
3621 (aref (clearcase-fprop-get-properties file) 1))
3623 (defun clearcase-fprop-checked-out (file)
3624 "For FILE, return its \"checked-out\" ClearCase property."
3625 (aref (clearcase-fprop-get-properties file) 2))
3627 (defun clearcase-fprop-reserved (file)
3628 "For FILE, return its \"reserved\" ClearCase property."
3629 (aref (clearcase-fprop-get-properties file) 3))
3631 (defun clearcase-fprop-version (file)
3632 "For FILE, return its \"version\" ClearCase property."
3633 (aref (clearcase-fprop-get-properties file) 4))
3635 (defun clearcase-fprop-predecessor-version (file)
3636 "For FILE, return its \"predecessor-version\" ClearCase property."
3637 (aref (clearcase-fprop-get-properties file) 5))
3639 (defun clearcase-fprop-oid (file)
3640 "For FILE, return its \"oid\" ClearCase property."
3641 (aref (clearcase-fprop-get-properties file) 6))
3643 (defun clearcase-fprop-user (file)
3644 "For FILE, return its \"user\" ClearCase property."
3645 (aref (clearcase-fprop-get-properties file) 7))
3647 (defun clearcase-fprop-date (file)
3648 "For FILE, return its \"date\" ClearCase property."
3649 (aref (clearcase-fprop-get-properties file) 8))
3651 (defun clearcase-fprop-time-last-described (file)
3652 "For FILE, return its \"time-last-described\" ClearCase property."
3653 (aref (clearcase-fprop-get-properties file) 9))
3655 (defun clearcase-fprop-viewtag (file)
3656 "For FILE, return its \"viewtag\" ClearCase property."
3657 (aref (clearcase-fprop-get-properties file) 10))
3659 (defun clearcase-fprop-comment (file)
3660 "For FILE, return its \"comment\" ClearCase property."
3661 (aref (clearcase-fprop-get-properties file) 11))
3663 (defun clearcase-fprop-vob-slink-text (file)
3664 "For FILE, return its \"slink-text\" ClearCase property."
3665 (aref (clearcase-fprop-get-properties file) 12))
3667 (defun clearcase-fprop-hijacked (file)
3668 "For FILE, return its \"hijacked\" ClearCase property."
3669 (aref (clearcase-fprop-get-properties file) 13))
3671 (defun clearcase-fprop-set-comment (file comment)
3672 "For FILE, set its \"comment\" ClearCase property to COMMENT."
3673 (aset (clearcase-fprop-get-properties file) 11 comment))
3675 (defun clearcase-fprop-owner-of-checkout (file)
3676 "For FILE, return whether the current user has it checked-out."
3677 (if (clearcase-fprop-checked-out file)
3678 (clearcase-fprop-user file)
3681 (defun clearcase-fprop-file-is-vob-slink-p (object-name)
3682 (not (zerop (length (clearcase-fprop-vob-slink-text object-name)))))
3684 (defun clearcase-fprop-file-is-version-p (object-name)
3686 (let ((mtype (clearcase-fprop-mtype object-name)))
3687 (or (eq 'version mtype)
3688 (eq 'directory-version mtype)))))
3690 ;; Read the object's ClearCase properties using cleartool and the Lisp reader.
3692 ;; nyi: for some reason the \n before the %c necessary here so avoid confusing the
3693 ;; cleartool/tq interface. Completely mysterious. Arrived at by
3696 (defvar clearcase-fprop-fmt-string
3698 ;; Yuck. Different forms of quotation are needed here apparently to deal with
3699 ;; all the various ways of spawning sub-process on the the various platforms
3700 ;; (XEmacs vs. GnuEmacs, Win32 vs. Unix, Cygwin-built vs. native-built).
3702 (if clearcase-on-mswindows
3703 (if clearcase-xemacs-p
3706 (if clearcase-on-cygwin
3709 "[nil \\\"%m\\\" \\\"%f\\\" \\\"%Rf\\\" \\\"%Sn\\\" \\\"%PSn\\\" \\\"%On\\\" \\\"%u\\\" \\\"%Nd\\\" nil nil nil \\\"%[slink_text]p\\\" nil ]\\n%c"
3712 "[nil \\\"%m\\\" \\\"%f\\\" \\\"%Rf\\\" \\\"%Sn\\\" \\\"%PSn\\\" \\\"%On\\\" \\\"%u\\\" \\\"%Nd\\\" nil nil nil \\\"%[slink_text]p\\\" nil]\n%c")
3716 "[nil \"%m\" \"%f\" \"%Rf\" \"%Sn\" \"%PSn\" \"%On\" \"%u\" \"%Nd\" nil nil nil \"%[slink_text]p\" nil]\\n%c")
3720 "'[nil \"%m\" \"%f\" \"%Rf\" \"%Sn\" \"%PSn\" \"%On\" \"%u\" \"%Nd\" nil nil nil \"%[slink_text]p\" nil]\\n%c'")
3722 "Format for cleartool+describe command when reading the
3723 ClearCase properties of a file")
3725 (defvar clearcase-fprop-describe-count 0
3726 "Count the number of times clearcase-fprop-read-properties is called")
3728 (defun clearcase-fprop-read-properties (file)
3729 "Invoke the cleartool+describe command to obtain the ClearCase
3730 properties of FILE."
3731 (assert (file-name-absolute-p file))
3732 (let* ((truename (clearcase-fprop-canonicalise-path (file-truename (expand-file-name file)))))
3734 ;; If the object doesn't exist, signal an error
3736 (if (or (not (file-exists-p (clearcase-vxpath-element-part file)))
3737 (not (file-exists-p (clearcase-vxpath-element-part truename))))
3738 (error "File doesn't exist: %s" file)
3740 ;; Run cleartool+ describe and capture the output as a string:
3742 (let ((desc-string (clearcase-ct-cleartool-cmd "desc"
3744 clearcase-fprop-fmt-string
3745 (clearcase-path-native truename))))
3746 (setq clearcase-fprop-describe-count (1+ clearcase-fprop-describe-count))
3748 ;;(clearcase-trace (format "desc of %s <<<<" truename))
3749 ;;(clearcase-trace desc-string)
3750 ;;(clearcase-trace (format "desc of %s >>>>" truename))
3752 ;; Read all but the comment, using the Lisp reader, and then copy
3753 ;; what's left as the comment. We don't try to use the Lisp reader to
3754 ;; fetch the comment to avoid problems with quotation.
3756 ;; nyi: it would be nice if we could make cleartool use "/" as pname-sep,
3757 ;; because read-from-string will barf on imbedded "\". For now
3758 ;; run clearcase-path-canonicalise-slashes over the cleartool
3759 ;; output before invoking the Lisp reader.
3761 (let* ((first-read (read-from-string (clearcase-path-canonicalise-slashes desc-string)))
3762 (result (car first-read))
3763 (bytes-read (cdr first-read))
3764 (comment (substring desc-string (1+ bytes-read)))) ;; skip \n
3766 ;; Plug in the slots I left empty:
3768 (aset result 0 truename)
3769 (aset result 9 (current-time))
3771 (aset result 11 comment)
3773 ;; Convert mtype to an enumeration:
3775 (let ((mtype-string (aref result 1)))
3777 ((string= mtype-string "version")
3778 (aset result 1 'version))
3780 ((string= mtype-string "directory version")
3781 (aset result 1 'directory-version))
3783 ((string= mtype-string "view private object")
3784 (aset result 1 'view-private-object)
3786 ;; If we're in a snapshot see if it is hijacked by running
3787 ;; ct+desc FILE@@. No error indicates it's hijacked.
3789 (if (clearcase-file-would-be-in-snapshot-p truename)
3793 (clearcase-ct-cleartool-cmd
3796 (concat (clearcase-path-native truename)
3797 clearcase-vxpath-glue)))
3800 ((string= mtype-string "file element")
3801 (aset result 1 'file-element))
3803 ((string= mtype-string "directory element")
3804 (aset result 1 'directory-element))
3806 ((string= mtype-string "derived object")
3807 (aset result 1 'derived-object))
3809 ;; For now treat checked-in DOs as versions.
3811 ((string= mtype-string "derived object version")
3812 (aset result 1 'version))
3814 ;; On NT, coerce the mtype of symlinks into that
3815 ;; of their targets.
3817 ;; nyi: I think this is approximately right.
3819 ((and (string= mtype-string "symbolic link")
3820 clearcase-on-mswindows)
3821 (if (file-directory-p truename)
3822 (aset result 1 'directory-version)
3823 (aset result 1 'version)))
3825 ;; We get this on paths like foo.c@@/main
3827 ((string= mtype-string "branch")
3828 (aset result 1 'branch))
3830 ((string= mtype-string "**null meta type**")
3831 (aset result 1 nil))
3834 (error "Unknown mtype returned by cleartool+describe: %s"
3837 ;; nyi: possible efficiency win: only evaluate the viewtag on demand.
3840 (aset result 10 (clearcase-file-viewtag truename)))
3842 ;; Convert checked-out field to boolean:
3844 (aset result 2 (not (zerop (length (aref result 2)))))
3846 ;; Convert reserved field to boolean:
3848 (aset result 3 (string= "reserved" (aref result 3)))
3850 ;; Return the array of properties.
3856 ;;{{{ View property cache
3858 ;; ClearCase properties of views are stored in a vector in a hashtable
3859 ;; with the viewtag as the lookup key.
3863 ;; [0] ucm : boolean
3864 ;; [1] stream : string
3865 ;; [2] pvob : string
3866 ;; [3] activities : list of strings
3867 ;; [4] current-activity : string
3871 (defun clearcase-vprop-dump-to-current-buffer ()
3872 "Dump to the current buffer the table recording ClearCase properties of views."
3873 (insert (format "View describe count: %s\n" clearcase-vprop-describe-count))
3877 (let ((properties (symbol-value symbol)))
3879 (format "viewtag: %s\n" (symbol-name symbol))
3881 (clearcase-vprop-unparse-properties properties)))))
3882 clearcase-vprop-hashtable)
3885 (defun clearcase-vprop-dump ()
3887 (clearcase-utl-populate-and-view-buffer
3890 (function (lambda ()
3891 (clearcase-vprop-dump-to-current-buffer)))))
3893 (defun clearcase-vprop-unparse-properties (properties)
3894 "Return a string suitable for printing PROPERTIES."
3896 (format "ucm: %s\n" (aref properties 0))
3897 (format "stream: %s\n" (aref properties 1))
3898 (format "pvob: %s\n" (aref properties 2))
3899 (format "activities: %s\n" (aref properties 3))
3900 (format "current-activity: %s\n" (aref properties 4))))
3904 ;;{{{ Asynchronously fetching view properties:
3906 (defvar clearcase-vprop-timer nil)
3907 (defvar clearcase-vprop-work-queue nil)
3909 (defun clearcase-vprop-schedule-work (viewtag)
3910 ;; Add to the work queue.
3912 (setq clearcase-vprop-work-queue (cons viewtag
3913 clearcase-vprop-work-queue))
3914 ;; Create the timer if necessary.
3916 (if (null clearcase-vprop-timer)
3917 (if clearcase-xemacs-p
3920 (setq clearcase-vprop-timer
3921 (run-with-idle-timer 5 t 'clearcase-vprop-timer-function))
3925 (setq clearcase-vprop-timer (timer-create))
3926 (timer-set-function clearcase-vprop-timer 'clearcase-vprop-timer-function)
3927 (timer-set-idle-time clearcase-vprop-timer 5)
3928 (timer-activate-when-idle clearcase-vprop-timer)))))
3930 (defun clearcase-vprop-timer-function ()
3931 ;; Process the work queue and empty it.
3933 (mapcar (function (lambda (viewtag)
3934 (clearcase-vprop-get-properties viewtag)))
3935 clearcase-vprop-work-queue)
3936 (setq clearcase-vprop-work-queue nil)
3938 ;; Cancel the timer.
3940 (cancel-timer clearcase-vprop-timer)
3941 (setq clearcase-vprop-timer nil))
3945 (defvar clearcase-vprop-hashtable (make-vector 31 0)
3946 "Obarray for per-view ClearCase properties.")
3948 (defun clearcase-vprop-clear-all-properties ()
3949 "Delete all entries in the clearcase-vprop-hashtable."
3950 (setq clearcase-vprop-hashtable (make-vector 31 0)))
3952 (defun clearcase-vprop-store-properties (viewtag properties)
3953 "For VIEW, store its ClearCase PROPERTIES in the clearcase-vprop-hashtable."
3954 (set (intern viewtag clearcase-vprop-hashtable) properties))
3956 (defun clearcase-vprop-unstore-properties (viewtag)
3957 "For VIEWTAG, delete its entry in the clearcase-vprop-hashtable."
3958 (unintern viewtag clearcase-vprop-hashtable))
3960 (defun clearcase-vprop-lookup-properties (viewtag)
3961 "For VIEWTAG, lookup and return its ClearCase properties from the
3962 clearcase-vprop-hashtable."
3963 (symbol-value (intern-soft viewtag clearcase-vprop-hashtable)))
3965 (defun clearcase-vprop-get-properties (viewtag)
3966 "For VIEWTAG, make sure it's ClearCase properties are in the hashtable
3967 and then return them."
3968 (or (clearcase-vprop-lookup-properties viewtag)
3969 (let ((properties (clearcase-vprop-read-properties viewtag)))
3970 (clearcase-vprop-store-properties viewtag properties)
3973 (defun clearcase-vprop-ucm (viewtag)
3974 "For VIEWTAG, return its \"ucm\" ClearCase property."
3975 (aref (clearcase-vprop-get-properties viewtag) 0))
3977 (defun clearcase-vprop-stream (viewtag)
3978 "For VIEWTAG, return its \"stream\" ClearCase property."
3979 (aref (clearcase-vprop-get-properties viewtag) 1))
3981 (defun clearcase-vprop-pvob (viewtag)
3982 "For VIEWTAG, return its \"stream\" ClearCase property."
3983 (aref (clearcase-vprop-get-properties viewtag) 2))
3985 (defun clearcase-vprop-activities (viewtag)
3986 "For VIEWTAG, return its \"activities\" ClearCase property."
3988 ;; If the activity set has been flushed, go and schedule a re-fetch.
3990 (let ((properties (clearcase-vprop-get-properties viewtag)))
3991 (if (null (aref properties 3))
3992 (aset properties 3 (clearcase-vprop-read-activities-asynchronously viewtag))))
3994 ;; Now poll, waiting for the activities to be available.
3996 (let ((loop-count 0))
3997 ;; If there is a background process still reading the activities,
3998 ;; wait for it to finish.
4000 ;; nyi: probably want a timeout here.
4002 ;; nyi: There seems to be a race on NT in accept-process-output so that
4003 ;; we would wait forever.
4005 (if (not clearcase-on-mswindows)
4006 ;; Unix synchronization with the end of the process
4007 ;; which is reading activities.
4009 (while (bufferp (aref (clearcase-vprop-get-properties viewtag) 3))
4011 (set-buffer (aref (clearcase-vprop-get-properties viewtag) 3))
4012 (message "Reading activity list...")
4013 (setq loop-count (1+ loop-count))
4014 (accept-process-output clearcase-vprop-async-proc)))
4016 ;; NT synchronization with the end of the process which is reading
4019 ;; Unfortunately on NT we can't rely on the process sentinel being called
4020 ;; so we have to explicitly test the process status.
4022 (while (bufferp (aref (clearcase-vprop-get-properties viewtag) 3))
4023 (message "Reading activity list...")
4025 (set-buffer (aref (clearcase-vprop-get-properties viewtag) 3))
4026 (if (or (not (processp clearcase-vprop-async-proc))
4027 (eq 'exit (process-status clearcase-vprop-async-proc)))
4029 ;; The process has finished or gone away and apparently
4030 ;; the sentinel didn't get called which would have called
4031 ;; clearcase-vprop-finish-reading-activities, so call it
4034 (clearcase-vprop-finish-reading-activities (current-buffer))
4036 ;; The process is apparently still running, so wait
4038 (setq loop-count (1+ loop-count))
4041 (if (not (zerop loop-count))
4042 (message "Reading activity list...done"))
4044 (aref (clearcase-vprop-get-properties viewtag) 3)))
4046 (defun clearcase-vprop-current-activity (viewtag)
4047 "For VIEWTAG, return its \"current-activity\" ClearCase property."
4048 (aref (clearcase-vprop-get-properties viewtag) 4))
4050 (defun clearcase-vprop-set-activities (viewtag activities)
4051 "For VIEWTAG, set its \"activities\" ClearCase property to ACTIVITIES."
4052 (let ((properties (clearcase-vprop-lookup-properties viewtag)))
4053 ;; We must only set the activities for an existing vprop entry.
4056 (aset properties 3 activities)))
4058 (defun clearcase-vprop-flush-activities (viewtag)
4059 "For VIEWTAG, set its \"activities\" ClearCase property to nil,
4060 to cause a future re-fetch."
4061 (clearcase-vprop-set-activities viewtag nil))
4063 (defun clearcase-vprop-set-current-activity (viewtag activity)
4064 "For VIEWTAG, set its \"current-activity\" ClearCase property to ACTIVITY."
4065 (aset (clearcase-vprop-get-properties viewtag) 4 activity))
4067 ;; Read the object's ClearCase properties using cleartool lsview and cleartool lsstream.
4069 (defvar clearcase-vprop-describe-count 0
4070 "Count the number of times clearcase-vprop-read-properties is called")
4072 (defvar clearcase-lsstream-fmt-string
4073 (if clearcase-on-mswindows
4074 (if clearcase-xemacs-p
4077 (if clearcase-on-cygwin
4080 "[\\\"%n\\\" \\\"%[master]p\\\" ]"
4083 "[\\\"%n\\\" \\\"%[master]p\\\" ]")
4086 "[\"%n\" \"%[master]p\" ]")
4089 "'[\"%n\" \"%[master]p\" ]'"))
4091 (defun clearcase-vprop-read-properties (viewtag)
4092 "Invoke cleartool commands to obtain the ClearCase
4093 properties of VIEWTAG."
4095 ;; We used to use "ct+lsview -properties -full TAG", but this seemed to take
4096 ;; a long time in some circumstances. It appears to be because the
4097 ;; ADM_VIEW_GET_INFO RPC can take up to 60 seconds in certain circumstances
4098 ;; (typically on my laptop with self-contained ClearCase region).
4100 ;; Accordingly, since we don't really need to store snapshotness, the minimum
4101 ;; we really need to discover about a view is whether it is UCM-attached. For
4102 ;; this the much faster ct+lsstream suffices.
4104 (let* ((result (make-vector 5 nil)))
4105 (if (not clearcase-v3)
4109 (activity-names nil)
4110 (activity-titles nil)
4112 (current-activity nil)
4115 ;; This was necessary to make sure the "done" message was always
4116 ;; displayed. Not quite sure why.
4120 (message "Reading view properties...")
4121 (setq ret (clearcase-ct-blocking-call "lsstream" "-fmt"
4122 clearcase-lsstream-fmt-string
4125 (setq clearcase-vprop-describe-count (1+ clearcase-vprop-describe-count))
4127 (if (setq ucm (not (zerop (length ret))))
4129 ;; It's apparently a UCM view
4131 (let* ((first-read (read-from-string (clearcase-utl-escape-backslashes ret)))
4132 (array-read (car first-read))
4133 (bytes-read (cdr first-read)))
4137 (setq stream (aref array-read 0))
4139 ;; Get PVOB tag from something like "unix@/vobs/projects"
4141 (let ((s (aref array-read 1)))
4142 (if (string-match "@" s)
4143 (setq pvob (substring s (match-end 0)))
4146 ;; Get the activity list and store as a list of (NAME . TITLE) pairs
4148 (setq activities (clearcase-vprop-read-activities-asynchronously viewtag))
4150 ;; Get the current activity
4152 (let ((name-string (clearcase-ct-blocking-call "lsact" "-cact" "-fmt" "%n"
4154 (if (not (zerop (length name-string)))
4155 (setq current-activity name-string)))
4158 (aset result 1 stream)
4159 (aset result 2 pvob)
4160 (aset result 3 activities)
4161 (aset result 4 current-activity))))
4163 (message "Reading view properties...done"))))
4167 (defvar clearcase-vprop-async-viewtag nil)
4168 (defvar clearcase-vprop-async-proc nil)
4169 (defun clearcase-vprop-read-activities-asynchronously (viewtag)
4170 (let ((buf-name (format "*clearcase-activities-%s*" viewtag)))
4171 ;; Clean up old instance of the buffer we use to fetch activities:
4173 (let ((buf (get-buffer buf-name)))
4178 (if (and (boundp 'clearcase-vprop-async-proc)
4179 clearcase-vprop-async-proc)
4181 (kill-process clearcase-vprop-async-proc)
4183 (kill-buffer buf))))
4185 ;; Create a buffer and an associated new process to read activities in the
4186 ;; background. We return the buffer to be stored in the activities field of
4187 ;; the view-properties record. The function clearcase-vprop-activities will
4188 ;; recognise when the asynch fetching is still underway and wait for it to
4191 ;; The process has a sentinel function which is supposed to get called when
4192 ;; the process finishes. This sometimes doesn't happen on Windows, so that
4193 ;; clearcase-vprop-activities has to do a bit more work. (Perhaps a race
4194 ;; exists: the process completes before the sentinel can be set ?)
4196 (let* ((buf (get-buffer-create buf-name))
4197 (proc (start-process (format "*clearcase-activities-process-%s*" viewtag)
4199 clearcase-cleartool-path
4200 "lsact" "-view" viewtag)))
4201 (process-kill-without-query proc)
4204 ;; Create a sentinel to parse and store the activities when the
4205 ;; process finishes. We record the viewtag as a buffer-local
4206 ;; variable so the sentinel knows where to store the activities.
4208 (set (make-local-variable 'clearcase-vprop-async-viewtag) viewtag)
4209 (set (make-local-variable 'clearcase-vprop-async-proc) proc)
4210 (set-process-sentinel proc 'clearcase-vprop-read-activities-sentinel))
4211 ;; Return the buffer.
4215 (defun clearcase-vprop-read-activities-sentinel (process event-string)
4216 (clearcase-trace "Activity reading process sentinel called")
4217 (if (not (equal "finished\n" event-string))
4220 (error "Reading activities failed: %s" event-string))
4221 (clearcase-vprop-finish-reading-activities (process-buffer process)))
4223 (defun clearcase-vprop-finish-reading-activities (buffer)
4224 (let ((activity-list nil))
4225 (message "Parsing view activities...")
4228 (if (or (not (boundp 'clearcase-vprop-async-viewtag))
4229 (null clearcase-vprop-async-viewtag))
4230 (error "Internal error: clearcase-vprop-async-viewtag not set"))
4232 ;; Check that our buffer is the one currently expected to supply the
4233 ;; activities. (Avoid races.)
4235 (let ((properties (clearcase-vprop-lookup-properties clearcase-vprop-async-viewtag)))
4237 (eq buffer (aref properties 3)))
4240 ;; Parse the buffer, slicing out the 2nd and 4th fields as name and title.
4242 (goto-char (point-min))
4243 (while (re-search-forward "^[^ \t]+[ \t]+\\([^ \t]+\\)[ \t]+[^ \t]+[ \t]+\"+\\(.*\\)\"$" nil t)
4244 (let ((id (buffer-substring (match-beginning 1)
4246 (title (buffer-substring (match-beginning 2)
4248 (setq activity-list (cons (cons id title)
4251 ;; We've got activity-list in the reverse order that
4252 ;; cleartool+lsactivity generated them. I think this is reverse
4253 ;; chronological order, so keep this order since it is more
4254 ;; convenient when setting to an activity.
4256 ;;(setq activity-list (nreverse activity-list))
4258 (clearcase-vprop-set-activities clearcase-vprop-async-viewtag activity-list))
4260 (kill-buffer buffer))))
4261 (message "Parsing view activities...done")))
4263 ;;{{{ old synchronous activity reader
4265 ;; (defun clearcase-vprop-read-activities-synchronously (viewtag)
4266 ;; "Return a list of (activity-name . title) pairs for VIEWTAG"
4267 ;; ;; nyi: ought to use a variant of clearcase-ct-blocking-call that returns a buffer
4268 ;; ;; rather than a string
4270 ;; ;; Performance: takes around 30 seconds to read 1000 activities.
4271 ;; ;; Too slow to invoke willy-nilly on integration streams for example,
4272 ;; ;; which typically can have 1000+ activities.
4274 ;; (let ((ret (clearcase-ct-blocking-call "lsact" "-view" viewtag)))
4275 ;; (let ((buf (get-buffer-create "*clearcase-temp-activities*"))
4276 ;; (activity-list nil))
4281 ;; (goto-char (point-min))
4282 ;; ;; Slice out the 2nd and 4th fields as name and title
4284 ;; (while (re-search-forward "^[^ \t]+[ \t]+\\([^ \t]+\\)[ \t]+[^ \t]+[ \t]+\"+\\(.*\\)\"$" nil t)
4285 ;; (setq activity-list (cons (cons (buffer-substring (match-beginning 1)
4287 ;; (buffer-substring (match-beginning 2)
4290 ;; (kill-buffer buf))
4292 ;; ;; We've got activity-list in the reverse order that
4293 ;; ;; cleartool+lsactivity generated them. I think this is reverse
4294 ;; ;; chronological order, so keep this order since it is more
4295 ;; ;; convenient when setting to an activity.
4297 ;; ;;(nreverse activity-list))))
4304 ;;{{{ Determining if a checkout was modified.
4306 ;; How to tell if a file changed since checkout ?
4308 ;; In the worst case we actually run "ct diff -pred" but we attempt several
4309 ;; less expensive tests first.
4311 ;; 1. If it's size differs from pred.
4312 ;; 2. The mtime and the ctime are no longer the same.
4314 ;; nyi: Other cheaper tests we could use:
4316 ;; (a) After each Emacs-driven checkout go and immediately fetch the mtime of
4317 ;; the file and store as fprop-checkout-mtime. Then use that to compare
4318 ;; against current mtime. This at least would make this function work
4319 ;; right on files checked out by the current Emacs process.
4321 ;; (b) In the MVFS, after each Emacs-driven checkout go and immediately fetch
4322 ;; the OID and store as fprop-checkout-oid. Then use that to compare
4323 ;; against the current oid (the MVFS assigns a new OID at each write).
4324 ;; This might not always be a win since we'd still need to run cleartool
4325 ;; to get the current OID.
4327 (defun clearcase-file-appears-modified-since-checkout-p (file)
4328 "Return whether FILE appears to have been modified since checkout.
4329 It doesn't examine the file contents."
4331 (if (not (clearcase-fprop-checked-out file))
4334 (let ((mvfs (clearcase-file-is-in-mvfs-p file)))
4336 ;; We consider various cases in order of increasing cost to compute.
4339 ;; Case 1: (MVFS only) the size is different to its predecessor.
4344 (clearcase-utl-file-size file)
4345 ;; nyi: For the snapshot case it'd be nice to get the size of the
4346 ;; predecessor by using "ct+desc -pred -fmt" but there doesn't
4347 ;; seem to be a format descriptor for file size. On the other hand
4348 ;; ct+dump can obtain the size.
4350 (clearcase-utl-file-size (clearcase-vxpath-cons-vxpath
4352 (clearcase-fprop-predecessor-version
4358 ;; Case 2: (MVFS only) the mtime and the ctime are no longer the same.
4360 ;; nyi: At least on Windows there seems to be a small number of seconds
4361 ;; difference here even when the file is not modified.
4362 ;; So we really check to see of they are close.
4364 ;; nyi: This doesn't work in a snapshot view.
4367 (not (clearcase-utl-filetimes-close (clearcase-utl-file-mtime file)
4368 (clearcase-utl-file-ctime file)
4372 'ctime-mtime-not-close))
4375 ;; Case 3: last resort. Actually run a diff against predecessor.
4377 (let ((ret (clearcase-ct-blocking-call "diff"
4382 (if (not (zerop (length ret)))
4393 ;;{{{ Tests for view-residency
4395 ;;{{{ Tests for MVFS file residency
4397 ;; nyi: probably superseded by clearcase-file-would-be-in-view-p
4398 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4400 ;; nyi: this should get at least partially invalidated when
4401 ;; VOBs are unmounted.
4403 ;; nyi: make this different for NT
4405 (defvar clearcase-always-mvfs-regexp (if (not clearcase-on-mswindows)
4408 ;; nyi: express this using drive variable
4412 clearcase-pname-sep-regexp)))
4414 ;; This prevents the clearcase-file-vob-root function from pausing for long periods
4415 ;; stat-ing /net/host@@
4417 ;; nyi: is there something equivalent on NT I need to avoid ?
4420 (defvar clearcase-never-mvfs-regexps (if clearcase-on-mswindows
4424 "^/tmp_mnt/net/[^/]+/"
4426 "Regexps matching those paths we can assume are never inside the MVFS.")
4428 (defvar clearcase-known-vob-root-cache nil)
4430 (defun clearcase-file-would-be-in-mvfs-p (filename)
4431 "Return whether FILE, after it is created, would reside in an MVFS filesystem."
4432 (let ((truename (file-truename filename)))
4433 (if (file-exists-p truename)
4434 (clearcase-file-is-in-mvfs-p truename)
4435 (let ((containing-dir (file-name-as-directory (file-name-directory truename))))
4436 (clearcase-file-is-in-mvfs-p containing-dir)))))
4438 (defun clearcase-file-is-in-mvfs-p (filename)
4439 "Return whether existing FILE, resides in an MVFS filesystem."
4440 (let ((truename (file-truename filename)))
4443 ;; case 1: its prefix matches an "always VOB" prefix like /vobs/...
4445 ;; nyi: problem here: we return true for "/vobs/nonexistent/"
4447 (numberp (string-match clearcase-always-mvfs-regexp truename))
4449 ;; case 2: it has a prefix which is a known VOB-root
4451 (clearcase-file-matches-vob-root truename clearcase-known-vob-root-cache)
4453 ;; case 3: it has an ancestor dir which is a newly met VOB-root
4455 (clearcase-file-vob-root truename))))
4457 (defun clearcase-wd-is-in-mvfs ()
4458 "Return whether the current directory resides in an MVFS filesystem."
4459 (clearcase-file-is-in-mvfs-p (file-truename ".")))
4461 (defun clearcase-file-matches-vob-root (truename vob-root-list)
4462 "Return whether TRUENAME has a prefix in VOB-ROOT-LIST."
4463 (if (null vob-root-list)
4465 (or (numberp (string-match (regexp-quote (car vob-root-list))
4467 (clearcase-file-matches-vob-root truename (cdr vob-root-list)))))
4469 (defun clearcase-file-vob-root (truename)
4470 "File the highest versioned directory in TRUENAME."
4472 ;; Use known non-MVFS patterns to rule some paths out.
4474 (if (apply (function clearcase-utl-or-func)
4475 (mapcar (function (lambda (regexp)
4476 (string-match regexp truename)))
4477 clearcase-never-mvfs-regexps))
4479 (let ((previous-dir nil)
4480 (dir (file-name-as-directory (file-name-directory truename)))
4481 (highest-versioned-directory nil))
4483 (while (not (string-equal dir previous-dir))
4484 (if (clearcase-file-covers-element-p dir)
4485 (setq highest-versioned-directory dir))
4486 (setq previous-dir dir)
4487 (setq dir (file-name-directory (directory-file-name dir))))
4489 (if highest-versioned-directory
4490 (add-to-list 'clearcase-known-vob-root-cache highest-versioned-directory))
4492 highest-versioned-directory)))
4494 ;; Note: you should probably be using clearcase-fprop-mtype instead of this
4495 ;; unless you really know what you're doing (nyi: check usages of this.)
4497 (defun clearcase-file-covers-element-p (path)
4498 "Determine quickly if PATH refers to a Clearcase element,
4499 without caching the result."
4501 ;; nyi: Even faster: consult the fprop cache first ?
4503 (let ((element-dir (concat (clearcase-vxpath-element-part path) clearcase-vxpath-glue)))
4504 (and (file-exists-p path)
4505 (file-directory-p element-dir))))
4509 ;;{{{ Tests for snapshot view residency
4511 ;; nyi: probably superseded by clearcase-file-would-be-in-view-p
4512 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4514 (defvar clearcase-known-snapshot-root-cache nil)
4516 (defun clearcase-file-would-be-in-snapshot-p (filename)
4517 "Return whether FILE, after it is created, would reside in a snapshot view.
4518 If so, return the viewtag."
4519 (let ((truename (file-truename filename)))
4520 (if (file-exists-p truename)
4521 (clearcase-file-is-in-snapshot-p truename)
4522 (let ((containing-dir (file-name-as-directory (file-name-directory truename))))
4523 (clearcase-file-is-in-snapshot-p containing-dir)))))
4525 (defun clearcase-file-is-in-snapshot-p (truename)
4526 "Return whether existing FILE, resides in a snapshot view.
4527 If so, return the viewtag."
4530 ;; case 1: it has a prefix which is a known snapshot-root
4532 (clearcase-file-matches-snapshot-root truename clearcase-known-snapshot-root-cache)
4534 ;; case 2: it has an ancestor dir which is a newly met VOB-root
4536 (clearcase-file-snapshot-root truename)))
4538 (defun clearcase-wd-is-in-snapshot ()
4539 "Return whether the current directory resides in a snapshot view."
4540 (clearcase-file-is-in-snapshot-p (file-truename ".")))
4542 (defun clearcase-file-matches-snapshot-root (truename snapshot-root-list)
4543 "Return whether TRUENAME has a prefix in SNAPSHOT-ROOT-LIST."
4544 (if (null snapshot-root-list)
4546 (or (numberp (string-match (regexp-quote (car snapshot-root-list))
4548 (clearcase-file-matches-snapshot-root truename (cdr snapshot-root-list)))))
4550 ;; This prevents the clearcase-file-snapshot-root function from pausing for long periods
4551 ;; stat-ing /net/host@@
4553 ;; nyi: is there something equivalent on NT I need to avoid ?
4556 (defvar clearcase-never-snapshot-regexps (if clearcase-on-mswindows
4560 "^/tmp_mnt/net/[^/]+/"
4562 "Regexps matching those paths we can assume are never inside a snapshot view.")
4564 (defun clearcase-file-snapshot-root (truename)
4565 "File the the snapshot view root containing TRUENAME."
4567 ;; Use known non-snapshot patterns to rule some paths out.
4569 (if (apply (function clearcase-utl-or-func)
4570 (mapcar (function (lambda (regexp)
4571 (string-match regexp truename)))
4572 clearcase-never-snapshot-regexps))
4574 (let ((previous-dir nil)
4575 (dir (file-name-as-directory (file-name-directory truename)))
4580 (while (and (not (string-equal dir previous-dir))
4583 ;; See if .view.dat exists and contains a valid view uuid
4585 (let ((view-dat-name (concat dir (if clearcase-on-mswindows
4586 "view.dat" ".view.dat"))))
4587 (if (file-readable-p view-dat-name)
4588 (let ((uuid (clearcase-viewdat-to-uuid view-dat-name)))
4591 (setq viewtag (clearcase-view-uuid-to-tag uuid))
4593 (setq viewroot dir)))))))
4595 (setq previous-dir dir)
4596 (setq dir (file-name-directory (directory-file-name dir))))
4599 (add-to-list 'clearcase-known-snapshot-root-cache viewroot))
4601 ;; nyi: update a viewtag==>viewroot map ?
4605 (defun clearcase-viewdat-to-uuid (file)
4606 "Extract the view-uuid from a .view.dat file."
4607 ;; nyi, but return non-nil so clearcase-file-snapshot-root works
4611 (defun clearcase-view-uuid-to-tag (uuid)
4612 "Look up the view-uuid in the register to discover its tag."
4613 ;; nyi, but return non-nil so clearcase-file-snapshot-root works
4619 ;; This is simple-minded but seems to work because cleartool+describe
4620 ;; groks snapshot views.
4622 ;; nyi: Might be wise to cache view-roots to speed this up because the
4623 ;; filename-handlers call this.
4625 ;; nyi: Some possible shortcuts
4626 ;; 1. viewroot-relative path [syntax]
4627 ;; 2. under m:/ on NT [syntax]
4628 ;; 3. setviewed on Unix [find a containing VOB-root]
4629 ;; 4. subst-ed view on NT (calling net use seems very slow though)
4630 ;; [find a containing VOB-root]
4633 (defun clearcase-file-would-be-in-view-p (filename)
4634 "Return whether FILE, after it is created, would reside in a ClearCase view."
4635 (let ((truename (file-truename (expand-file-name filename))))
4637 ;; We use clearcase-path-file-really-exists-p here to make sure we are dealing
4638 ;; with a real file and not something faked by Emacs' file name handlers
4641 (if (clearcase-path-file-really-exists-p truename)
4642 (clearcase-file-is-in-view-p truename)
4643 (let ((containing-dir (file-name-as-directory (file-name-directory truename))))
4644 (and (clearcase-path-file-really-exists-p containing-dir)
4645 (clearcase-file-is-in-view-p containing-dir))))))
4647 (defun clearcase-file-is-in-view-p (filename)
4648 (let ((truename (file-truename (expand-file-name filename))))
4649 ;; Shortcut if the file is a version-extended path.
4651 (or (clearcase-file-snapshot-root truename)
4652 (clearcase-vxpath-p truename)
4653 (clearcase-fprop-mtype truename)
4655 ;; nyi: How to efficiently know if we're in a dynamic-view root
4656 ;; 1. Test each contained name for elementness.
4658 ;; 2. If it is viewroot-relative.
4659 ;; Okay but not sufficient.
4660 ;; How about case v:/ when view is substed ?
4661 ;; 3. We're setviewed.
4662 ;; Okay but not sufficient.
4663 ;; Maintain a cache of viewroots ?
4666 (defun clearcase-file-viewtag (filename)
4667 "Find the viewtag associated with existing FILENAME."
4669 (clearcase-when-debugging
4670 (assert (file-exists-p filename)))
4672 (let ((truename (file-truename (expand-file-name filename))))
4675 ;; Case 1: viewroot-relative path
4678 ((clearcase-vrpath-p truename)
4679 (clearcase-vrpath-viewtag truename))
4681 ;; Case 2: under m:/ on NT
4684 ((and clearcase-on-mswindows
4685 (string-match (concat clearcase-viewroot-drive
4686 clearcase-pname-sep-regexp
4688 clearcase-non-pname-sep-regexp "*"
4692 (substring truename (match-beginning 1) (match-end 1)))
4694 ;; Case 3: setviewed on Unix
4695 ;; ==> read EV, but need to check it's beneath a VOB-root
4697 ((and clearcase-setview-viewtag
4698 (clearcase-file-would-be-in-mvfs-p truename))
4699 clearcase-setview-viewtag)
4701 ;; Case 4: subst-ed view on NT
4702 ;; ==> use ct+pwv -wdview
4703 ;; Case 5: snapshot view
4704 ;; ==> use ct+pwv -wdview
4706 (clearcase-file-wdview truename)))))
4708 (defun clearcase-file-wdview (truename)
4709 "Return the working-directory view associated with TRUENAME,
4711 (let ((default-directory (if (file-directory-p truename)
4713 (file-name-directory truename))))
4714 (clearcase-ct-cd default-directory)
4715 (let ((ret (clearcase-ct-blocking-call "pwv" "-wdview" "-short")))
4716 (if (not (string-match " NONE " ret))
4717 (clearcase-utl-1st-line-of-string ret)))))
4721 ;;{{{ The cleartool sub-process
4723 ;; We use pipes rather than pty's for two reasons:
4725 ;; 1. NT only has pipes
4726 ;; 2. On Solaris there appeared to be a problem in the pty handling part
4727 ;; of Emacs, which resulted in Emacs/tq seeing too many cleartool prompt
4728 ;; strings. This would occasionally occur and prevent the tq-managed
4729 ;; interactions with the cleartool sub-process from working correctly.
4731 ;; Now we use pipes. Cleartool detects the "non-tty" nature of the output
4732 ;; device and doesn't send a prompt. We manufacture an end-of-transaction
4733 ;; marker by sending a "pwd -h" after each cleartool sub-command and then use
4734 ;; the expected output of "Usage: pwd\n" as our end-of-txn pattern for tq.
4736 ;; Even using pipes, the semi-permanent outboard-process using tq doesn't work
4737 ;; well on NT. There appear to be bugs in accept-process-output such that:
4738 ;; 0. there apparently were hairy race conditions, which a sprinkling
4739 ;; of (accept-process-output nil 1) seemed to avoid somewhat.
4740 ;; 1. it never seems to timeout if you name a process as arg1.
4741 ;; 2. it always seems to wait for TIMEOUT, even if there is output ready.
4742 ;; The result seemed to be less responsive tha just calling a fresh cleartool
4743 ;; process for each invocation of clearcase-ct-blocking-call
4745 ;; It still seems worthwhile to make it work on NT, as clearcase-ct-blocking-call
4746 ;; typically takes about 0.5 secs on NT versus 0.05 sec on Solaris,
4747 ;; an order of magnitude difference.
4750 (defconst clearcase-ct-eotxn-cmd "pwd -h\n")
4751 (defconst clearcase-ct-eotxn-response "Usage: pwd\n")
4752 (defconst clearcase-ct-eotxn-response-length (length clearcase-ct-eotxn-response))
4754 (defconst clearcase-ct-subproc-timeout 30
4755 "Timeout on calls to subprocess")
4757 (defvar clearcase-ct-tq nil
4758 "Transaction queue to talk to ClearTool in a subprocess")
4760 (defvar clearcase-ct-return nil
4761 "Return value when we're involved in a blocking call")
4763 (defvar clearcase-ct-view ""
4764 "Current view of cleartool subprocess, or the empty string if none")
4766 (defvar clearcase-ct-wdir ""
4767 "Current working directory of cleartool subprocess,
4768 or the empty string if none")
4770 (defvar clearcase-ct-running nil)
4772 (defun clearcase-ct-accept-process-output (proc timeout)
4773 (accept-process-output proc timeout))
4775 (defun clearcase-ct-start-cleartool ()
4777 (clearcase-trace "clearcase-ct-start-cleartool()")
4778 (let ((process-environment (append '("ATRIA_NO_BOLD=1"
4779 "ATRIA_FORCE_GUI=1")
4780 ;;; emacs is a GUI, right? :-)
4781 process-environment)))
4782 (clearcase-trace (format "Starting cleartool in %s" default-directory))
4783 (let* ( ;; Force the use of a pipe
4785 (process-connection-type nil)
4787 (start-process "cleartool" ;; Absolute path won't work here
4789 clearcase-cleartool-path)))
4790 (process-kill-without-query cleartool-process)
4791 (setq clearcase-ct-view "")
4792 (setq clearcase-ct-tq (tq-create cleartool-process))
4793 (tq-enqueue clearcase-ct-tq
4794 clearcase-ct-eotxn-cmd ;; question
4795 clearcase-ct-eotxn-response ;; regexp
4796 'clearcase-ct-running ;; closure
4798 (while (not clearcase-ct-running)
4799 (message "waiting for cleartool to start...")
4800 (clearcase-ct-accept-process-output (tq-process clearcase-ct-tq)
4801 clearcase-ct-subproc-timeout))
4802 ;; Assign a sentinel to restart it if it dies.
4803 ;; nyi: This needs debugging.
4804 ;;(set-process-sentinel cleartool-process 'clearcase-ct-sentinel)
4806 (clearcase-trace "clearcase-ct-start-cleartool() done")
4807 (message "waiting for cleartool to start...done"))))
4809 ;; nyi: needs debugging.
4811 (defun clearcase-ct-sentinel (process event-string)
4812 (clearcase-trace (format "Cleartool process sentinel called: %s" event-string))
4813 (if (not (eq 'run (process-status process)))
4815 ;; Restart the dead cleartool.
4817 (clearcase-trace "Cleartool process restarted")
4818 (clearcase-ct-start-cleartool))))
4820 (defun clearcase-ct-kill-cleartool ()
4821 "Kill off cleartool subprocess. If another one is needed,
4822 it will be restarted. This may be useful if you're debugging clearcase."
4824 (clearcase-ct-kill-tq))
4826 (defun clearcase-ct-callback (arg val)
4827 (clearcase-trace (format "clearcase-ct-callback:<\n"))
4828 (clearcase-trace val)
4829 (clearcase-trace (format "clearcase-ct-callback:>\n"))
4830 ;; This can only get called when the last thing received from
4831 ;; the cleartool sub-process was clearcase-ct-eotxn-response,
4832 ;; so it is safe to just remove it here.
4834 (setq clearcase-ct-return (substring val 0 (- clearcase-ct-eotxn-response-length))))
4836 (defun clearcase-ct-do-cleartool-command (command file comment &optional extra-args)
4837 "Execute a cleartool command, notifying user and checking for
4838 errors. Output from COMMAND goes to buffer *clearcase*. The last argument of the
4839 command is the name of FILE; this is appended to an optional list of
4843 (setq file (expand-file-name file)))
4845 (error "command must not be a list"))
4846 (if clearcase-command-messages
4848 (message "Running %s on %s..." command file)
4849 (message "Running %s..." command)))
4850 (let ((camefrom (current-buffer))
4853 (set-buffer (get-buffer-create "*clearcase*"))
4854 (setq buffer-read-only nil)
4856 (set (make-local-variable 'clearcase-parent-buffer) camefrom)
4857 (set (make-local-variable 'clearcase-parent-buffer-name)
4858 (concat " from " (buffer-name camefrom)))
4860 ;; This is so that command arguments typed in the *clearcase* buffer will
4861 ;; have reasonable defaults.
4864 (setq default-directory (file-name-directory file)))
4867 (function (lambda (s)
4869 (not (zerop (length s)))
4871 (append squeezed (list s))))))
4874 (clearcase-with-tempfile
4876 (if (not (eq comment 'unused))
4879 (write-region comment nil comment-file nil 'noprint)
4880 (setq squeezed (append squeezed (list "-cfile" (clearcase-path-native comment-file)))))
4881 (setq squeezed (append squeezed (list "-nc")))))
4883 (setq squeezed (append squeezed (list (clearcase-path-native file)))))
4884 (let ((default-directory (file-name-directory
4885 (or file default-directory))))
4886 (clearcase-ct-cd default-directory)
4887 (if clearcase-command-messages
4888 (message "Running %s..." command))
4890 (apply 'clearcase-ct-cleartool-cmd (append (list command) squeezed)))
4891 (if clearcase-command-messages
4892 (message "Running %s...done" command))))
4894 (goto-char (point-min))
4895 (clearcase-view-mode 0 camefrom)
4896 (set-buffer-modified-p nil) ; XEmacs - fsf uses `not-modified'
4897 (if (re-search-forward "^cleartool: Error:.*$" nil t)
4899 (setq status (buffer-substring (match-beginning 0) (match-end 0)))
4900 (clearcase-port-view-buffer-other-window "*clearcase*")
4901 (shrink-window-if-larger-than-buffer)
4902 (error "Running %s...FAILED (%s)" command status))
4903 (if clearcase-command-messages
4904 (message "Running %s...OK" command)))
4905 (set-buffer camefrom)
4908 (defun clearcase-ct-cd (dir)
4910 (string= dir clearcase-ct-wdir))
4912 (clearcase-ct-blocking-call "cd" (clearcase-path-native dir))
4913 (setq clearcase-ct-wdir dir)))
4915 (defun clearcase-ct-cleartool-cmd (&rest cmd)
4916 (apply 'clearcase-ct-blocking-call cmd))
4918 ;; NT Emacs - needs a replacement for tq.
4920 (defun clearcase-ct-get-command-stdout (program &rest args)
4922 Returns PROGRAM's stdout.
4923 ARGS is the command line arguments to PROGRAM."
4924 (let ((buf (get-buffer-create "cleartoolexecution")))
4928 (apply 'call-process program nil buf nil args)
4930 (kill-buffer buf))))
4932 ;; The TQ interaction still doesn't work on NT.
4934 (defvar clearcase-disable-tq clearcase-on-mswindows
4935 "Set to T if the Emacs/cleartool interactions via tq are not working right.")
4937 (defun clearcase-ct-blocking-call (&rest cmd)
4938 (clearcase-trace (format "clearcase-ct-blocking-call(%s)" cmd))
4940 (setq clearcase-ct-return nil)
4942 (if clearcase-disable-tq
4945 (setq clearcase-ct-return (apply 'clearcase-ct-get-command-stdout
4946 clearcase-cleartool-path cmd))
4950 (setq clearcase-ct-return nil)
4951 (if (not clearcase-ct-tq)
4952 (clearcase-ct-start-cleartool))
4958 ;; If the token has imbedded spaces and is not already quoted,
4959 ;; add double quotes.
4961 (setq command (concat command
4963 (clearcase-utl-quote-if-nec token)))))
4965 (tq-enqueue clearcase-ct-tq
4966 (concat command "\n"
4967 clearcase-ct-eotxn-cmd) ;; question
4968 clearcase-ct-eotxn-response ;; regexp
4970 'clearcase-ct-callback) ;; function
4971 (while (not clearcase-ct-return)
4972 (clearcase-ct-accept-process-output (tq-process clearcase-ct-tq)
4973 clearcase-ct-subproc-timeout)))
4976 (while (tq-queue clearcase-ct-tq)
4977 (tq-queue-pop clearcase-ct-tq)))))
4978 (if (string-match "cleartool: Error:" clearcase-ct-return)
4979 (error "cleartool process error %s: "
4980 (substring clearcase-ct-return (match-end 0))))
4981 (clearcase-trace (format "command-result(%s)" clearcase-ct-return))
4982 clearcase-ct-return)
4984 (defun clearcase-ct-kill-tq ()
4985 (setq clearcase-ct-running nil)
4986 (setq clearcase-ct-tq nil)
4987 (process-send-eof (tq-process clearcase-ct-tq))
4988 (kill-process (tq-process clearcase-ct-tq)))
4990 (defun clearcase-ct-kill-buffer-hook ()
4992 ;; NT Emacs - doesn't use tq.
4994 (if (not clearcase-on-mswindows)
4995 (let ((kill-buffer-hook nil))
4996 (if (and (boundp 'clearcase-ct-tq)
4998 (eq (current-buffer) (tq-buffer clearcase-ct-tq)))
4999 (error "Don't kill TQ buffer %s, use `clearcase-ct-kill-tq'" (current-buffer))))))
5001 (add-hook 'kill-buffer-hook 'clearcase-ct-kill-buffer-hook)
5005 ;;{{{ Invoking a command
5007 ;; nyi Would be redundant if we didn't need it to invoke normal-diff-program
5009 (defun clearcase-do-command (okstatus command file &optional extra-args)
5010 "Execute a version-control command, notifying user and checking for errors.
5011 The command is successful if its exit status does not exceed OKSTATUS.
5012 Output from COMMAND goes to buffer *clearcase*. The last argument of the command is
5013 an optional list of EXTRA-ARGS."
5014 (setq file (expand-file-name file))
5015 (if clearcase-command-messages
5016 (message "Running %s on %s..." command file))
5017 (let ((camefrom (current-buffer))
5021 (set-buffer (get-buffer-create "*clearcase*"))
5022 (setq buffer-read-only nil)
5024 (set (make-local-variable 'clearcase-parent-buffer) camefrom)
5025 (set (make-local-variable 'clearcase-parent-buffer-name)
5026 (concat " from " (buffer-name camefrom)))
5027 ;; This is so that command arguments typed in the *clearcase* buffer will
5028 ;; have reasonable defaults.
5030 (setq default-directory (file-name-directory file)
5031 file (file-name-nondirectory file))
5034 (function (lambda (s)
5036 (not (zerop (length s)))
5038 (append squeezed (list s))))))
5040 (setq squeezed (append squeezed (list file)))
5041 (setq status (apply 'call-process command nil t nil squeezed))
5042 (goto-char (point-min))
5043 (clearcase-view-mode 0 camefrom)
5044 (set-buffer-modified-p nil) ; XEmacs - fsf uses `not-modified'
5045 (if (or (not (integerp status)) (< okstatus status))
5047 (clearcase-port-view-buffer-other-window "*clearcase*")
5048 (shrink-window-if-larger-than-buffer)
5049 (error "Running %s...FAILED (%s)" command
5050 (if (integerp status)
5051 (format "status %d" status)
5053 (if clearcase-command-messages
5054 (message "Running %s...OK" command)))
5055 (set-buffer camefrom)
5060 ;;{{{ Viewtag management
5064 (defun clearcase-viewtag-try-to-start-view (viewtag)
5065 "If VIEW is not apparently already visible under viewroot, start it."
5066 (if (not (member viewtag (clearcase-viewtag-started-viewtags)))
5067 (clearcase-viewtag-start-view viewtag)))
5069 (defun clearcase-viewtag-started-viewtags-alist ()
5070 "Return an alist of views that are currently visible under the viewroot."
5074 (list (concat tag "/"))))
5075 (clearcase-viewtag-started-viewtags)))
5077 (defun clearcase-viewtag-started-viewtags ()
5078 "Return the list of viewtags already visible under the viewroot."
5079 (let ((raw-list (if clearcase-on-mswindows
5080 (directory-files clearcase-viewroot-drive)
5081 (directory-files clearcase-viewroot))))
5082 (clearcase-utl-list-filter
5083 (function (lambda (string)
5084 ;; Exclude the ones that start with ".",
5085 ;; and the ones that end with "@@".
5087 (and (not (equal ?. (aref string 0)))
5088 (not (string-match "@@$" string)))))
5091 ;; nyi: Makes sense on NT ?
5092 ;; Probably also want to run subst ?
5093 ;; Need a better high-level interface to start-view
5095 (defun clearcase-viewtag-start-view (viewtag)
5096 "If VIEWTAG is in our cache of valid view names, start it."
5097 (if (clearcase-viewtag-exists viewtag)
5099 (message "Starting view server for %s..." viewtag)
5100 (clearcase-ct-blocking-call "startview" viewtag)
5101 (message "Starting view server for %s...done" viewtag))))
5109 (defvar clearcase-viewtag-cache nil
5110 "Oblist of all known viewtags.")
5112 (defvar clearcase-viewtag-dir-cache nil
5113 "Oblist of all known viewtag dirs.")
5115 (defvar clearcase-viewtag-cache-timeout 1800
5116 "*Default timeout of all-viewtag cache, in seconds.")
5118 (defun clearcase-viewtag-schedule-cache-invalidation ()
5119 "Schedule the next invalidation of clearcase-viewtag-cache."
5120 (run-at-time (format "%s sec" clearcase-viewtag-cache-timeout)
5122 (function (lambda (&rest ignore)
5123 (setq clearcase-viewtag-cache nil)))
5140 (defun clearcase-viewtag-read-all-viewtags ()
5141 "Invoke ct+lsview to get all viewtags, and return an obarry containing them."
5142 (message "Fetching view names...")
5143 (let* ((default-directory "/")
5144 (result (make-vector 1021 0))
5145 (raw-views-string (clearcase-ct-blocking-call "lsview" "-short"))
5146 (view-list (clearcase-utl-split-string-at-char raw-views-string ?\n)))
5147 (message "Fetching view names...done")
5148 (mapcar (function (lambda (string)
5149 (set (intern string result) t)))
5153 (defun clearcase-viewtag-populate-caches ()
5154 (setq clearcase-viewtag-cache (clearcase-viewtag-read-all-viewtags))
5155 (let ((dir-cache (make-vector 1021 0)))
5157 (function (lambda (sym)
5158 (set (intern (concat (symbol-name sym) "/") dir-cache) t)))
5159 clearcase-viewtag-cache)
5160 (setq clearcase-viewtag-dir-cache dir-cache))
5161 (clearcase-viewtag-schedule-cache-invalidation))
5163 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5167 ;; Exported interfaces
5169 ;; This is for completion of viewtags.
5171 (defun clearcase-viewtag-all-viewtags-obarray ()
5172 "Return an obarray of all valid viewtags as of the last time we looke d."
5173 (if (null clearcase-viewtag-cache)
5174 (clearcase-viewtag-populate-caches))
5175 clearcase-viewtag-cache)
5177 ;; This is for completion of viewtag dirs, like /view/my_view_name/
5178 ;; The trailing slash is required for compatibility with other instances
5179 ;; of filename completion in Emacs.
5181 (defun clearcase-viewtag-all-viewtag-dirs-obarray ()
5182 "Return an obarray of all valid viewtag directory names as of the last time we looked."
5183 (if (null clearcase-viewtag-dir-cache)
5184 (clearcase-viewtag-populate-caches))
5185 clearcase-viewtag-dir-cache)
5187 (defun clearcase-viewtag-exists (viewtag)
5188 (symbol-value (intern-soft viewtag (clearcase-viewtag-all-viewtags-obarray))))
5196 ;;{{{ Pathnames: version-extended
5198 (defun clearcase-vxpath-p (path)
5199 (or (string-match (concat clearcase-vxpath-glue "/") path)
5200 (string-match (concat clearcase-vxpath-glue "\\\\") path)))
5202 (defun clearcase-vxpath-element-part (vxpath)
5203 "Return the element part of version-extended PATH."
5204 (if (string-match clearcase-vxpath-glue vxpath)
5205 (substring vxpath 0 (match-beginning 0))
5208 (defun clearcase-vxpath-version-part (vxpath)
5209 "Return the version part of version-extended PATH."
5210 (if (string-match clearcase-vxpath-glue vxpath)
5211 (substring vxpath (match-end 0))
5214 (defun clearcase-vxpath-branch (vxpath)
5215 "Return the branch part of a version-extended path or of a version"
5216 (if (clearcase-vxpath-p vxpath)
5217 (clearcase-vxpath-cons-vxpath
5218 (clearcase-vxpath-element-part vxpath)
5219 (file-name-directory (clearcase-vxpath-version-part vxpath)))
5220 (file-name-directory vxpath)))
5222 (defun clearcase-vxpath-version (vxpath)
5223 "Return the numeric version part of a version-extended path or of a version"
5224 (if (clearcase-vxpath-p vxpath)
5225 (file-name-nondirectory (clearcase-vxpath-version-part vxpath))
5226 (file-name-nondirectory vxpath)))
5228 (defun clearcase-vxpath-cons-vxpath (file version &optional viewtag)
5229 "Make a ClearCase version-extended pathname for ELEMENT's version VERSION.
5230 If ELEMENT is actually a version-extended pathname, substitute VERSION for
5231 the version included in ELEMENT. If VERSION is nil, remove the version-extended
5234 If optional VIEWTAG is specified, make a view-relative pathname, possibly
5235 replacing the existing view prefix."
5236 (let* ((element (clearcase-vxpath-element-part file))
5237 (glue-fmt (if (and (> (length version) 0)
5238 (= (aref version 0) ?/))
5239 (concat "%s" clearcase-vxpath-glue "%s")
5240 (concat "%s" clearcase-vxpath-glue "/%s")))
5241 (relpath (clearcase-vrpath-tail element)))
5243 (setq element (concat clearcase-viewroot "/" viewtag (or relpath element))))
5245 (format glue-fmt element version)
5248 ;; NYI: This should cache the predecessor version as a property
5251 (defun clearcase-vxpath-of-predecessor (file)
5252 "Compute the version-extended pathname of the predecessor version of FILE."
5253 (if (not (equal 'version (clearcase-fprop-mtype file)))
5254 (error "Not a clearcase version: %s" file))
5255 (let ((abs-file (expand-file-name file)))
5256 (let ((ver (clearcase-utl-1st-line-of-string
5257 (clearcase-ct-cleartool-cmd "describe"
5260 (clearcase-path-native abs-file)))))
5261 (clearcase-path-canonicalise-slashes (concat
5262 (clearcase-vxpath-element-part file)
5263 clearcase-vxpath-glue
5266 (defun clearcase-vxpath-version-extend (file)
5267 "Compute the version-extended pathname of FILE."
5268 (if (not (equal 'version (clearcase-fprop-mtype file)))
5269 (error "Not a clearcase version: %s" file))
5270 (let ((abs-file (expand-file-name file)))
5271 (clearcase-path-canonicalise-slashes
5272 (clearcase-utl-1st-line-of-string
5273 (clearcase-ct-cleartool-cmd "describe"
5276 clearcase-vxpath-glue
5278 (clearcase-path-native abs-file))))))
5280 (defun clearcase-vxpath-of-branch-base (file)
5281 "Compute the version-extended pathname of the version at the branch base of FILE."
5282 (let* ((file-version-path
5283 (if (clearcase-fprop-checked-out file)
5284 ;; If the file is checked-out, start with its predecessor version...
5286 (clearcase-vxpath-version-extend (clearcase-vxpath-of-predecessor file))
5287 ;; ...otherwise start with the file's version.
5289 (clearcase-vxpath-version-extend file)))
5290 (file-version-number (string-to-int (clearcase-vxpath-version file-version-path)))
5291 (branch (clearcase-vxpath-branch file-version-path)))
5292 (let* ((base-number 0)
5293 (base-version-path (format "%s%d" branch base-number)))
5294 (while (and (not (clearcase-file-is-in-snapshot-p base-version-path))
5295 (not (file-exists-p base-version-path))
5296 (< base-number file-version-number))
5297 (setq base-number (1+ base-number))
5298 (setq base-version-path (format "%s%d" branch base-number)))
5299 base-version-path)))
5301 (defun clearcase-vxpath-version-of-branch-base (file)
5302 (clearcase-vxpath-version-part (clearcase-vxpath-of-branch-base file)))
5304 (defun clearcase-vxpath-get-version-in-buffer (vxpath)
5305 "Return a buffer containing the version named by VXPATH.
5306 Intended for use in snapshot views."
5307 (let* ((temp-file (clearcase-vxpath-get-version-in-temp-file vxpath))
5308 (buffer (find-file-noselect temp-file t)))
5310 ;; XEmacs throws an error if you delete a read-only file
5312 (if clearcase-xemacs-p
5313 (if (not (file-writable-p temp-file))
5314 (set-file-modes temp-file (string-to-number "666" 8))))
5316 (delete-file temp-file)
5319 (defun clearcase-vxpath-get-version-in-temp-file (vxpath)
5320 "Return the name of a temporary file containing the version named by VXPATH.
5321 Intended for use in snapshot views."
5323 (let ((temp-file (clearcase-utl-tempfile-name vxpath)))
5325 (clearcase-ct-blocking-call "get"
5327 (clearcase-path-native temp-file)
5328 (clearcase-path-native vxpath))
5333 ;;{{{ Pathnames: viewroot-relative
5335 ;; nyi: make all this work with viewroot-drive-relative files too
5337 (defun clearcase-vrpath-p (path)
5338 "Return whether PATH is viewroot-relative."
5339 (string-match clearcase-vrpath-regexp path))
5341 (defun clearcase-vrpath-head (vrpath)
5342 "Given viewroot-relative PATH, return the prefix including the view-tag."
5343 (if (string-match clearcase-vrpath-regexp vrpath)
5344 (substring vrpath (match-end 0))))
5346 (defun clearcase-vrpath-tail (vrpath)
5347 "Given viewroot-relative PATH, return the suffix after the view-tag."
5348 (if (string-match clearcase-vrpath-regexp vrpath)
5349 (substring vrpath (match-end 0))))
5351 (defun clearcase-vrpath-viewtag (vrpath)
5352 "Given viewroot-relative PATH, return the view-tag."
5353 (if (string-match clearcase-vrpath-regexp vrpath)
5354 (substring vrpath (match-beginning 1) (match-end 1))))
5356 ;; Remove useless viewtags from a pathname.
5357 ;; e.g. if we're setviewed to view "VIEWTAG"
5358 ;; (clearcase-path-remove-useless-viewtags "/view/VIEWTAG/PATH")
5360 ;; (clearcase-path-remove-useless-viewtags "/view/z/view/y/PATH")
5361 ;; ==> /view/y/"PATH"
5363 (defvar clearcase-multiple-viewroot-regexp
5366 clearcase-pname-sep-regexp
5367 clearcase-non-pname-sep-regexp "+"
5370 clearcase-pname-sep-regexp
5374 (defun clearcase-path-remove-useless-viewtags (pathname)
5375 ;; Try to avoid file-name-handler recursion here:
5377 (let ((setview-root clearcase-setview-root))
5381 (setq setview-root (concat setview-root "/")))
5385 ((string-match clearcase-multiple-viewroot-regexp pathname)
5386 (clearcase-path-remove-useless-viewtags (substring pathname (match-beginning 1))))
5389 (string= setview-root "/"))
5392 ;; If pathname has setview-root as a proper prefix,
5393 ;; strip it off and recurse:
5396 (< (length setview-root) (length pathname))
5397 (string= setview-root (substring pathname 0 (length setview-root))))
5398 (clearcase-path-remove-useless-viewtags (substring pathname (- (length setview-root) 1))))
5405 ;; Don't pass the "INPLACE" parameter to subst-char-in-string here since the
5406 ;; parameter is not necessarily a local variable (in some cases it is
5407 ;; buffer-file-name and replacing / with \ in it wreaks havoc).
5409 (defun clearcase-path-canonicalise-slashes (path)
5410 (if (not clearcase-on-mswindows)
5412 (subst-char-in-string ?\\ ?/ path)))
5414 (defun clearcase-path-canonical (path)
5415 (if (not clearcase-on-mswindows)
5417 (if clearcase-on-cygwin
5418 (substring (shell-command-to-string (concat "cygpath -u '" path "'")) 0 -1)
5419 (subst-char-in-string ?\\ ?/ path))))
5421 (defun clearcase-path-native (path)
5422 (if (not clearcase-on-mswindows)
5424 (if clearcase-on-cygwin
5425 (substring (shell-command-to-string (concat "cygpath -w " path)) 0 -1)
5426 (subst-char-in-string ?/ ?\\ path))))
5428 (defun clearcase-path-file-really-exists-p (filename)
5429 "Test if a file really exists, when all file-name handlers are disabled."
5430 (let ((inhibit-file-name-operation 'file-exists-p)
5431 (inhibit-file-name-handlers (mapcar
5434 file-name-handler-alist)))
5435 (file-exists-p filename)))
5437 (defun clearcase-path-file-in-any-scopes (file scopes)
5440 (while (and (null result)
5442 (if (clearcase-path-file-in-scope file (car cursor))
5444 (setq cursor (cdr cursor)))
5448 (defun clearcase-path-file-in-scope (file scope)
5449 (assert (file-name-absolute-p file))
5450 (assert (file-name-absolute-p scope))
5453 ;; Pathnames are equal
5455 (string= file scope)
5457 ;; scope-qua-dir is an ancestor of file (proper string prefix)
5459 (let ((scope-as-dir (concat scope "/")))
5460 (string= scope-as-dir
5461 (substring file 0 (length scope-as-dir))))))
5467 (defun clearcase-mode-line-buffer-id (filename)
5468 "Compute an abbreviated version string for the mode-line.
5469 It will be in one of three forms: /main/NNN, or .../branchname/NNN, or DO-NAME"
5471 (if (clearcase-fprop-checked-out filename)
5472 (if (clearcase-fprop-reserved filename)
5475 (let ((ver-string (clearcase-fprop-version filename)))
5476 (if (not (zerop (length ver-string)))
5477 (let ((i (length ver-string))
5479 ;; Search back from the end to the second-last slash
5483 (if (equal ?/ (aref ver-string (1- i)))
5484 (setq slash-count (1+ slash-count)))
5487 (concat "..." (substring ver-string i))
5488 (substring ver-string i)))))))
5492 ;;{{{ Minibuffer reading
5494 ;;{{{ clearcase-read-version-name
5496 (defun clearcase-read-version-name (prompt file)
5497 "Display PROMPT and read a version string for FILE in the minibuffer,
5498 with completion if possible."
5499 (let* ((insert-default-directory nil)
5500 (predecessor (clearcase-fprop-predecessor-version file))
5501 (default-filename (clearcase-vxpath-cons-vxpath file predecessor))
5503 ;; To get this to work it is necessary to make Emacs think
5504 ;; we're completing with respect to "ELEMENT@@/" rather
5505 ;; than "ELEMENT@@". Otherwise when we enter a version
5506 ;; like "/main/NN", it thinks we entered an absolute path.
5507 ;; So instead, we prompt the user to enter "main/..../NN"
5508 ;; and add back the leading slash before returning.
5510 (completing-dir (concat file "@@/")))
5511 (if (and (clearcase-file-is-in-mvfs-p file) (not clearcase-on-mswindows))
5512 ;; Completion only works in MVFS:
5514 (concat "/" (read-file-name prompt
5516 (substring predecessor 1)
5519 (substring predecessor 1)))
5520 (concat "/" (read-string prompt
5521 (substring predecessor 1)
5526 ;;{{{ clearcase-read-label-name
5530 (defun clearcase-read-label-name (prompt)
5531 "Read a label name."
5533 (let* ((string (clearcase-ct-cleartool-cmd "lstype"
5538 (mapcar (function (lambda (arg)
5539 (if (string-match "(locked)" arg)
5541 (setq labels (cons (list arg) labels)))))
5542 (clearcase-utl-split-string string "\n"))
5543 (completing-read prompt labels nil t)))
5549 ;;{{{ Directory-tree walking
5551 (defun clearcase-dir-all-files (func &rest args)
5552 "Invoke FUNC f ARGS on each regular file f in default directory."
5553 (let ((dir default-directory))
5554 (message "Scanning directory %s..." dir)
5555 (mapcar (function (lambda (f)
5556 (let ((dirf (expand-file-name f dir)))
5557 (apply func dirf args))))
5558 (directory-files dir))
5559 (message "Scanning directory %s...done" dir)))
5561 (defun clearcase-file-tree-walk-internal (file func args quiet)
5562 (if (not (file-directory-p file))
5563 (apply func file args)
5565 (message "Traversing directory %s..." file))
5566 (let ((dir (file-name-as-directory file)))
5570 (string-equal f ".")
5571 (string-equal f "..")
5572 (member f clearcase-directory-exclusion-list)
5573 (let ((dirf (concat dir f)))
5575 (file-symlink-p dirf) ;; Avoid possible loops
5576 (clearcase-file-tree-walk-internal dirf func args quiet))))))
5577 (directory-files dir)))))
5579 (defun clearcase-file-tree-walk (func &rest args)
5580 "Walk recursively through default directory.
5581 Invoke FUNC f ARGS on each non-directory file f underneath it."
5582 (clearcase-file-tree-walk-internal default-directory func args nil)
5583 (message "Traversing directory %s...done" default-directory))
5585 (defun clearcase-subdir-tree-walk (func &rest args)
5586 "Walk recursively through default directory.
5587 Invoke FUNC f ARGS on each subdirectory underneath it."
5588 (clearcase-subdir-tree-walk-internal default-directory func args nil)
5589 (message "Traversing directory %s...done" default-directory))
5591 (defun clearcase-subdir-tree-walk-internal (file func args quiet)
5592 (if (file-directory-p file)
5593 (let ((dir (file-name-as-directory file)))
5594 (apply func dir args)
5596 (message "Traversing directory %s..." file))
5600 (string-equal f ".")
5601 (string-equal f "..")
5602 (member f clearcase-directory-exclusion-list)
5603 (let ((dirf (concat dir f)))
5605 (file-symlink-p dirf) ;; Avoid possible loops
5606 (clearcase-subdir-tree-walk-internal dirf
5610 (directory-files dir)))))
5614 ;;{{{ Buffer context
5616 ;; nyi: it would be nice if we could restore fold context too, for folded files.
5618 ;; Save a bit of the text around POSN in the current buffer, to help
5619 ;; us find the corresponding position again later. This works even
5620 ;; if all markers are destroyed or corrupted.
5622 (defun clearcase-position-context (posn)
5625 (buffer-substring posn
5626 (min (point-max) (+ posn 100)))))
5628 ;; Return the position of CONTEXT in the current buffer, or nil if we
5629 ;; couldn't find it.
5631 (defun clearcase-find-position-by-context (context)
5632 (let ((context-string (nth 2 context)))
5633 (if (equal "" context-string)
5636 (let ((diff (- (nth 1 context) (buffer-size))))
5637 (if (< diff 0) (setq diff (- diff)))
5638 (goto-char (nth 0 context))
5639 (if (or (search-forward context-string nil t)
5640 ;; Can't use search-backward since the match may continue
5643 (progn (goto-char (- (point) diff (length context-string)))
5644 ;; goto-char doesn't signal an error at
5645 ;; beginning of buffer like backward-char would.
5647 (search-forward context-string nil t)))
5648 ;; to beginning of OSTRING
5650 (- (point) (length context-string))))))))
5654 ;;{{{ Synchronizing buffers with disk
5656 (defun clearcase-sync-after-file-updated-from-vob (file)
5657 ;; Do what is needed after a file in a snapshot is updated or a checkout is
5660 ;; "ct+update" will not always make the file readonly, if, for
5661 ;; example, its contents didn't actually change. But we'd like
5662 ;; update to result in a readonly file, so force it here.
5664 (clearcase-utl-make-unwriteable file)
5667 ;; If this returns true, there was a buffer visiting the file and it it
5668 ;; flushed fprops...
5670 (clearcase-sync-from-disk-if-needed file)
5672 ;; ...otherwise, just sync this other state:
5675 (clearcase-fprop-unstore-properties file)
5676 (dired-relist-file file))))
5678 (defun clearcase-sync-from-disk (file &optional no-confirm)
5680 (clearcase-fprop-unstore-properties file)
5681 ;; If the given file is in any buffer, revert it.
5683 (let ((buffer (find-buffer-visiting file)))
5687 (clearcase-buffer-revert no-confirm)
5688 (clearcase-fprop-get-properties file)
5690 ;; Make sure the mode-line gets updated.
5692 (setq clearcase-mode
5693 (concat " ClearCase:"
5694 (clearcase-mode-line-buffer-id file)))
5695 (force-mode-line-update))))
5697 ;; Update any Dired Mode buffers that list this file.
5699 (dired-relist-file file)
5701 ;; If the file was a directory, update any dired-buffer for
5704 (mapcar (function (lambda (buffer)
5708 (dired-buffers-for-dir file)))
5710 (defun clearcase-sync-from-disk-if-needed (file)
5712 ;; If the buffer on FILE is out of sync with its file, synch it. Returns t if
5713 ;; clearcase-sync-from-disk is called.
5715 (let ((buffer (find-buffer-visiting file)))
5717 ;; Buffer can be out of sync in two ways:
5718 ;; (a) Buffer is modified (hasn't been written)
5719 ;; (b) Buffer is recording a different modtime to what the file has.
5720 ;; This is what happens when the file is updated by another
5722 ;; (c) Buffer and file differ in their writeability.
5724 (or (buffer-modified-p buffer)
5725 (not (verify-visited-file-modtime buffer))
5726 (eq (file-writable-p file)
5727 (with-current-buffer buffer buffer-read-only))))
5729 (clearcase-sync-from-disk file
5730 ;; Only confirm for modified buffers.
5732 (not (buffer-modified-p buffer)))
5737 (defun clearcase-sync-to-disk (&optional not-urgent)
5739 ;; Make sure the current buffer and its working file are in sync
5740 ;; NOT-URGENT means it is ok to continue if the user says not to save.
5742 (if (buffer-modified-p)
5743 (if (or clearcase-suppress-confirm
5744 (y-or-n-p (format "Buffer %s modified; save it? "
5749 (error "Aborted")))))
5752 (defun clearcase-buffer-revert (&optional no-confirm)
5753 ;; Should never call for Dired buffers
5755 (assert (not (eq major-mode 'dired-mode)))
5757 ;; Revert buffer, try to keep point and mark where user expects them in spite
5758 ;; of changes because of expanded version-control key words. This is quite
5759 ;; important since otherwise typeahead won't work as expected.
5762 (let ((point-context (clearcase-position-context (point)))
5764 ;; Use clearcase-utl-mark-marker to avoid confusion in transient-mark-mode.
5765 ;; XEmacs - mark-marker t, FSF Emacs - mark-marker.
5767 (mark-context (if (eq (marker-buffer (clearcase-utl-mark-marker))
5769 (clearcase-position-context (clearcase-utl-mark-marker))))
5770 (camefrom (current-buffer)))
5772 ;; nyi: Should we run font-lock ?
5773 ;; Want to avoid re-doing a buffer that is already correct, such as on
5774 ;; check-in/check-out.
5775 ;; For now do-nothing.
5777 ;; The actual revisit.
5778 ;; For some reason, revert-buffer doesn't recompute whether View Minor Mode
5779 ;; should be on, so turn it off and then turn it on if necessary.
5781 ;; nyi: Perhaps we should re-find-file ?
5783 (or clearcase-xemacs-p
5784 (if (fboundp 'view-mode)
5786 (revert-buffer t no-confirm t)
5787 (or clearcase-xemacs-p
5788 (if (and (boundp 'view-read-only)
5793 ;; Restore point and mark.
5795 (let ((new-point (clearcase-find-position-by-context point-context)))
5797 (goto-char new-point))
5799 (let ((new-mark (clearcase-find-position-by-context mark-context)))
5801 (set-mark new-mark))))
5803 ;; Restore a semblance of folded state.
5805 (if (and (boundp 'folded-file)
5808 (folding-open-buffer)
5809 (folding-whole-buffer)
5811 (folding-goto-char new-point)))))))
5817 ;;{{{ Displaying content in special buffers
5819 (defun clearcase-utl-populate-and-view-buffer (buffer
5821 content-generating-func)
5822 "Empty BUFFER, and populate it by applying to ARGS the CONTENT-GENERATING-FUNC,
5823 and display in a separate window."
5825 (clearcase-utl-edit-and-view-buffer
5831 (apply content-generating-func args)))))
5833 (defun clearcase-utl-edit-and-view-buffer (buffer
5835 content-editing-func)
5836 "Empty BUFFER, and edit it by applying to ARGS the CONTENT-EDITING-FUNC,
5837 and display in a separate window."
5839 (let ( ;; Create the buffer if necessary.
5841 (buf (get-buffer-create buffer))
5843 ;; Record where we came from.
5845 (camefrom (current-buffer)))
5848 (clearcase-view-mode 0 camefrom)
5852 (apply content-editing-func args)
5854 ;; Display the buffer.
5856 (clearcase-port-view-buffer-other-window buf)
5858 (set-buffer-modified-p nil) ; XEmacs - fsf uses `not-modified'
5859 (shrink-window-if-larger-than-buffer)))
5863 ;;{{{ Temporary files
5865 (defvar clearcase-tempfiles nil)
5866 (defun clearcase-utl-tempfile-name (&optional vxpath)
5870 (if (string-match "\\(\\.[^.]+\\)@@" vxpath)
5871 (setq ext (match-string 1 vxpath)))))
5872 (let ((filename (concat
5873 (make-temp-name (clearcase-path-canonical
5874 ;; Use TEMP e.v. if set.
5876 (concat (or (getenv "TEMP") "/tmp")
5879 ;; Store its name for later cleanup.
5881 (setq clearcase-tempfiles (cons filename clearcase-tempfiles))
5884 (defun clearcase-utl-clean-tempfiles ()
5887 (if (file-exists-p tempfile)
5889 (delete-file tempfile)
5891 clearcase-tempfiles)
5892 (setq clearcase-tempfiles nil))
5896 (defun clearcase-utl-touch-file (file)
5897 "Attempt to update the modtime of FILE. Return t if it worked."
5899 ;; Silently fail if there is no "touch" command available. Couldn't find a
5900 ;; convenient way to update a file's modtime in ELisp.
5904 (shell-command (concat "touch " file))
5908 (defun clearcase-utl-filetimes-close (filetime1 filetime2 tolerance)
5909 "Test if FILETIME1 and FILETIME2 are within TOLERANCE of each other."
5910 ;; nyi: To do this correctly we need to know MAXINT.
5911 ;; For now this is correct enough since we only use this as a guideline to
5912 ;; avoid generating a diff.
5914 (if (equal (first filetime1) (first filetime2))
5915 (< (abs (- (second filetime1) (second filetime2))) tolerance)
5918 (defun clearcase-utl-emacs-date-to-clearcase-date (s)
5920 (substring s 20) ;; yyyy
5921 (int-to-string (clearcase-utl-month-unparse (substring s 4 7))) ;; mm
5922 (substring s 8 10) ;; dd
5924 (substring s 11 13) ;; hh
5925 (substring s 14 16) ;; mm
5926 (substring s 17 19))) ;; ss
5928 (defun clearcase-utl-month-unparse (s)
5930 ((string= s "Jan") 1)
5931 ((string= s "Feb") 2)
5932 ((string= s "Mar") 3)
5933 ((string= s "Apr") 4)
5934 ((string= s "May") 5)
5935 ((string= s "Jun") 6)
5936 ((string= s "Jul") 7)
5937 ((string= s "Aug") 8)
5938 ((string= s "Sep") 9)
5939 ((string= s "Oct") 10)
5940 ((string= s "Nov") 11)
5941 ((string= s "Dec") 12)))
5943 (defun clearcase-utl-strip-trailing-slashes (name)
5944 (let* ((len (length name)))
5945 (while (and (> len 1)
5946 (or (equal ?/ (aref name (1- len)))
5947 (equal ?\\ (aref name (1- len)))))
5948 (setq len (1- len)))
5949 (substring name 0 len)))
5951 (defun clearcase-utl-file-size (file)
5952 (nth 7 (file-attributes file)))
5953 (defun clearcase-utl-file-atime (file)
5954 (nth 4 (file-attributes file)))
5955 (defun clearcase-utl-file-mtime (file)
5956 (nth 5 (file-attributes file)))
5957 (defun clearcase-utl-file-ctime (file)
5958 (nth 6 (file-attributes file)))
5960 (defun clearcase-utl-kill-view-buffer ()
5962 (let ((buf (current-buffer)))
5963 (delete-windows-on buf)
5966 (defun clearcase-utl-escape-double-quotes (s)
5967 "Escape any double quotes in string S"
5968 (mapconcat (function (lambda (char)
5969 (if (equal ?\" char)
5975 (defun clearcase-utl-escape-backslashes (s)
5976 "Double any backslashes in string S"
5977 (mapconcat (function (lambda (char)
5978 (if (equal ?\\ char)
5984 (defun clearcase-utl-quote-if-nec (token)
5985 "If TOKEN contains whitespace and is not already quoted,
5986 wrap it in double quotes."
5987 (if (and (string-match "[ \t]" token)
5988 (not (equal ?\" (aref token 0)))
5989 (not (equal ?\' (aref token 0))))
5990 (concat "\"" token "\"")
5993 (defun clearcase-utl-or-func (&rest args)
5994 "A version of `or' that can be applied to a list."
5997 (while (and (null result)
6001 (setq cursor (cdr cursor)))
6004 (defun clearcase-utl-any (predicate list)
6005 "Returns t if PREDICATE is satisfied by any element in LIST."
6008 (while (and (null result)
6010 (if (funcall predicate (car cursor))
6012 (setq cursor (cdr cursor)))
6015 (defun clearcase-utl-every (predicate list)
6016 "Returns t if PREDICATE is satisfied by every element in LIST."
6021 (if (not (funcall predicate (car cursor)))
6023 (setq cursor (cdr cursor)))
6026 (defun clearcase-utl-list-filter (predicate list)
6027 "Map PREDICATE over each element of LIST, and return a list of the elements
6028 that mapped to non-nil."
6031 (while (not (null cursor))
6032 (let ((elt (car cursor)))
6033 (if (funcall predicate elt)
6034 (setq result (cons elt result)))
6035 (setq cursor (cdr cursor))))
6038 (defun clearcase-utl-elts-are-eq (l)
6039 "Test if all elements of LIST are eq."
6042 (let ((head (car l))
6044 (mapcar (function (lambda (elt)
6045 (if (not (eq elt head))
6046 (setq answer nil))))
6050 ;; FSF Emacs - doesn't like parameters on mark-marker.
6052 (defun clearcase-utl-mark-marker ()
6053 (if clearcase-xemacs-p
6057 (defun clearcase-utl-syslog (buf value)
6059 (let ((tmpbuf (get-buffer buf)))
6060 (if (bufferp tmpbuf)
6063 (goto-char (point-max))
6064 (insert (format "%s\n" value)))))))
6066 ;; Extract the first line of a string.
6068 (defun clearcase-utl-1st-line-of-string (s)
6072 (while (and (< i len)
6078 (defun clearcase-utl-split-string (str pat &optional indir suffix)
6081 (last (length str)))
6082 (while (< start last)
6083 (if (string-match pat str start)
6085 (let ((tmp (substring str start (match-beginning 0))))
6086 (if suffix (setq tmp (concat tmp suffix)))
6087 (setq ret (cons (if indir (cons tmp nil)
6090 (setq start (match-end 0)))
6092 (setq ret (cons (substring str start) ret))))
6095 (defun clearcase-utl-split-string-at-char (str char)
6100 ;; Collect next token
6102 (let ((token-begin i))
6105 (while (and (< i eos)
6106 (not (eq char (aref str i))))
6109 (setq ret (cons (substring str token-begin i)
6115 (defun clearcase-utl-add-env (env var)
6118 (vname (substring var 0
6119 (and (string-match "=" var)
6121 (let ((vnl (length vname)))
6123 (if (and (> (length (car a)) vnl)
6124 (string= (substring (car a) 0 vnl)
6126 (throw 'return env))
6131 (defun clearcase-utl-augment-env-from-view-config-spec (old-env tag &optional add-ons)
6133 (cc-env (clearcase-misc-extract-evs-from-config-spe tag)))
6135 ;; 1. Add-on bindings at the front:
6138 (setq newenv (clearcase-utl-add-env newenv (car add-ons)))
6139 (setq add-ons (cdr add-ons)))
6141 ;; 2. Then bindings defined in the config-spec:
6144 (setq newenv (clearcase-utl-add-env newenv (car cc-env)))
6145 (setq cc-env (cdr cc-env)))
6147 ;; 3. Lastly bindings that were in the old environment.
6150 (setq newenv (clearcase-utl-add-env newenv (car old-env)))
6151 (setq old-env (cdr old-env)))
6154 (defun clearcase-utl-make-writeable (file)
6155 ;; Equivalent to chmod u+w
6157 (set-file-modes file
6158 (logior #o0200 (file-modes file))))
6160 (defun clearcase-utl-make-unwriteable (file)
6161 ;; Equivalent to chmod u-w
6163 (set-file-modes file
6164 (logand #o7577 (file-modes file))))
6172 ;; Predicate to determine if ClearCase menu items are relevant.
6173 ;; nyi" this should disappear
6175 (defun clearcase-buffer-contains-version-p ()
6176 "Return true if the current buffer contains a ClearCase file or directory."
6177 (let ((object-name (if (eq major-mode 'dired-mode)
6180 (clearcase-fprop-file-is-version-p object-name)))
6182 ;;{{{ clearcase-mode menu
6186 ;; This version of the menu will hide rather than grey out inapplicable entries.
6188 (defvar clearcase-menu-contents-minimised
6191 ["Checkin" clearcase-checkin-current-buffer
6193 :visible (clearcase-file-ok-to-checkin buffer-file-name)]
6195 ["Edit checkout comment" clearcase-edit-checkout-comment-current-buffer
6197 :visible (clearcase-file-ok-to-checkin buffer-file-name)]
6199 ["Checkout" clearcase-checkout-current-buffer
6201 :visible (clearcase-file-ok-to-checkout buffer-file-name)]
6203 ["Hijack" clearcase-hijack-current-buffer
6205 :visible (clearcase-file-ok-to-hijack buffer-file-name)]
6207 ["Unhijack" clearcase-unhijack-current-buffer
6209 :visible (clearcase-file-ok-to-unhijack buffer-file-name)]
6211 ["Uncheckout" clearcase-uncheckout-current-buffer
6212 :visible (clearcase-file-ok-to-uncheckout buffer-file-name)]
6214 ["Find checkouts" clearcase-find-checkouts-in-current-view t]
6216 ["Make element" clearcase-mkelem-current-buffer
6217 :visible (clearcase-file-ok-to-mkelem buffer-file-name)]
6219 "---------------------------------"
6220 ["Describe version" clearcase-describe-current-buffer
6221 :visible (clearcase-buffer-contains-version-p)]
6223 ["Describe file" clearcase-describe-current-buffer
6224 :visible (not (clearcase-buffer-contains-version-p))]
6226 ["Annotate version" clearcase-annotate-current-buffer
6227 :visible (clearcase-buffer-contains-version-p)]
6229 ["Show config-spec rule" clearcase-what-rule-current-buffer
6230 :visible (clearcase-buffer-contains-version-p)]
6232 ;; nyi: enable this also when setviewed ?
6234 ["Edit config-spec" clearcase-edcs-edit t]
6236 "---------------------------------"
6237 (list "Compare (Emacs)..."
6238 ["Compare with predecessor" clearcase-ediff-pred-current-buffer
6240 :visible (clearcase-buffer-contains-version-p)]
6241 ["Compare with branch base" clearcase-ediff-branch-base-current-buffer
6243 :visible (clearcase-buffer-contains-version-p)]
6244 ["Compare with named version" clearcase-ediff-named-version-current-buffer
6246 :visible (clearcase-buffer-contains-version-p)])
6247 (list "Compare (GUI)..."
6248 ["Compare with predecessor" clearcase-gui-diff-pred-current-buffer
6250 :visible (clearcase-buffer-contains-version-p)]
6251 ["Compare with branch base" clearcase-gui-diff-branch-base-current-buffer
6253 :visible (clearcase-buffer-contains-version-p)]
6254 ["Compare with named version" clearcase-gui-diff-named-version-current-buffer
6256 :visible (clearcase-buffer-contains-version-p)])
6257 (list "Compare (diff)..."
6258 ["Compare with predecessor" clearcase-diff-pred-current-buffer
6260 :visible (clearcase-buffer-contains-version-p)]
6261 ["Compare with branch base" clearcase-diff-branch-base-current-buffer
6263 :visible (clearcase-buffer-contains-version-p)]
6264 ["Compare with named version" clearcase-diff-named-version-current-buffer
6266 :visible (clearcase-buffer-contains-version-p)])
6267 "---------------------------------"
6268 ["Browse versions (dired)" clearcase-browse-vtree-current-buffer
6269 :visible (clearcase-file-ok-to-browse buffer-file-name)]
6270 ["Vtree browser GUI" clearcase-gui-vtree-browser-current-buffer
6272 :visible (clearcase-buffer-contains-version-p)]
6273 "---------------------------------"
6274 (list "Update snapshot..."
6275 ["Update view" clearcase-update-view
6277 :visible (and (clearcase-file-is-in-view-p default-directory)
6278 (not (clearcase-file-is-in-mvfs-p default-directory)))]
6279 ["Update directory" clearcase-update-default-directory
6281 :visible (and (clearcase-file-is-in-view-p default-directory)
6282 (not (clearcase-file-is-in-mvfs-p default-directory)))]
6283 ["Update this file" clearcase-update-current-buffer
6285 :visible (and (clearcase-file-ok-to-checkout buffer-file-name)
6286 (not (clearcase-file-is-in-mvfs-p buffer-file-name)))]
6288 "---------------------------------"
6289 (list "Element history..."
6290 ["Element history (full)" clearcase-list-history-current-buffer
6292 :visible (clearcase-buffer-contains-version-p)]
6293 ["Element history (branch)" clearcase-list-history-current-buffer
6295 :visible (clearcase-buffer-contains-version-p)]
6296 ["Element history (me)" clearcase-list-history-current-buffer
6298 :visible (clearcase-buffer-contains-version-p)])
6299 "---------------------------------"
6300 ["Show current activity" clearcase-ucm-describe-current-activity
6302 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6303 ["Make activity" clearcase-ucm-mkact-current-dir
6305 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6306 ["Set activity..." clearcase-ucm-set-activity-current-dir
6308 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6309 ["Set NO activity" clearcase-ucm-set-activity-none-current-dir
6311 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6312 ["Rebase this stream" clearcase-gui-rebase
6314 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6315 ["Deliver from this stream" clearcase-gui-deliver
6317 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6318 "---------------------------------"
6319 (list "ClearCase GUI"
6320 ["ClearCase Explorer" clearcase-gui-clearexplorer
6322 :visible clearcase-on-mswindows]
6323 ["Project Explorer" clearcase-gui-project-explorer
6325 ["Merge Manager" clearcase-gui-merge-manager
6327 ["Snapshot View Updater" clearcase-gui-snapshot-view-updater
6329 "---------------------------------"
6332 ;; Enable this when current buffer is on VOB.
6334 ["Make branch type" clearcase-mkbrtype
6337 "---------------------------------"
6338 ["Report Bug in ClearCase Mode" clearcase-submit-bug-report
6341 ["Dump internals" clearcase-dump
6343 :visible (or (equal "rwhitby" (user-login-name))
6344 (equal "esler" (user-login-name)))]
6346 ["Flush caches" clearcase-flush-caches
6348 :visible (or (equal "rwhitby" (user-login-name))
6349 (equal "esler" (user-login-name)))]
6351 "---------------------------------"
6352 ["Customize..." (customize-group 'clearcase)
6355 (defvar clearcase-menu-contents
6358 ["Checkin" clearcase-checkin-current-buffer
6360 :active (clearcase-file-ok-to-checkin buffer-file-name)]
6362 ["Edit checkout comment" clearcase-edit-checkout-comment-current-buffer
6364 :active (clearcase-file-ok-to-checkin buffer-file-name)]
6366 ["Checkout" clearcase-checkout-current-buffer
6368 :active (clearcase-file-ok-to-checkout buffer-file-name)]
6370 ["Hijack" clearcase-hijack-current-buffer
6372 :active (clearcase-file-ok-to-hijack buffer-file-name)]
6374 ["Unhijack" clearcase-unhijack-current-buffer
6376 :active (clearcase-file-ok-to-unhijack buffer-file-name)]
6378 ["Uncheckout" clearcase-uncheckout-current-buffer
6379 :active (clearcase-file-ok-to-uncheckout buffer-file-name)]
6381 ["Make element" clearcase-mkelem-current-buffer
6382 :active (clearcase-file-ok-to-mkelem buffer-file-name)]
6384 "---------------------------------"
6385 ["Describe version" clearcase-describe-current-buffer
6386 :active (clearcase-buffer-contains-version-p)]
6388 ["Describe file" clearcase-describe-current-buffer
6389 :active (not (clearcase-buffer-contains-version-p))]
6391 ["Annotate version" clearcase-annotate-current-buffer
6393 :active (clearcase-buffer-contains-version-p)]
6395 ["Show config-spec rule" clearcase-what-rule-current-buffer
6396 :active (clearcase-buffer-contains-version-p)]
6398 ;; nyi: enable this also when setviewed ?
6400 ["Edit config-spec" clearcase-edcs-edit t]
6402 "---------------------------------"
6403 (list "Compare (Emacs)..."
6404 ["Compare with predecessor" clearcase-ediff-pred-current-buffer
6406 :active (clearcase-buffer-contains-version-p)]
6407 ["Compare with branch base" clearcase-ediff-branch-base-current-buffer
6409 :active (clearcase-buffer-contains-version-p)]
6410 ["Compare with named version" clearcase-ediff-named-version-current-buffer
6412 :active (clearcase-buffer-contains-version-p)])
6413 (list "Compare (GUI)..."
6414 ["Compare with predecessor" clearcase-gui-diff-pred-current-buffer
6416 :active (clearcase-buffer-contains-version-p)]
6417 ["Compare with branch base" clearcase-gui-diff-branch-base-current-buffer
6419 :active (clearcase-buffer-contains-version-p)]
6420 ["Compare with named version" clearcase-gui-diff-named-version-current-buffer
6422 :active (clearcase-buffer-contains-version-p)])
6423 (list "Compare (diff)..."
6424 ["Compare with predecessor" clearcase-diff-pred-current-buffer
6426 :active (clearcase-buffer-contains-version-p)]
6427 ["Compare with branch base" clearcase-diff-branch-base-current-buffer
6429 :active (clearcase-buffer-contains-version-p)]
6430 ["Compare with named version" clearcase-diff-named-version-current-buffer
6432 :active (clearcase-buffer-contains-version-p)])
6433 "---------------------------------"
6434 ["Browse versions (dired)" clearcase-browse-vtree-current-buffer
6435 :active (clearcase-file-ok-to-browse buffer-file-name)]
6436 ["Vtree browser GUI" clearcase-gui-vtree-browser-current-buffer
6438 :active (clearcase-buffer-contains-version-p)]
6439 "---------------------------------"
6440 (list "Update snapshot..."
6441 ["Update view" clearcase-update-view
6443 :active (and (clearcase-file-is-in-view-p default-directory)
6444 (not (clearcase-file-is-in-mvfs-p default-directory)))]
6445 ["Update directory" clearcase-update-default-directory
6447 :active (and (clearcase-file-is-in-view-p default-directory)
6448 (not (clearcase-file-is-in-mvfs-p default-directory)))]
6449 ["Update this file" clearcase-update-current-buffer
6451 :active (and (clearcase-file-ok-to-checkout buffer-file-name)
6452 (not (clearcase-file-is-in-mvfs-p buffer-file-name)))]
6454 "---------------------------------"
6455 (list "Element history..."
6456 ["Element history (full)" clearcase-list-history-current-buffer
6458 :active (clearcase-buffer-contains-version-p)]
6459 ["Element history (branch)" clearcase-list-history-current-buffer
6461 :active (clearcase-buffer-contains-version-p)]
6462 ["Element history (me)" clearcase-list-history-current-buffer
6464 :active (clearcase-buffer-contains-version-p)])
6465 "---------------------------------"
6466 ["Show current activity" clearcase-ucm-describe-current-activity
6468 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6469 ["Make activity" clearcase-ucm-mkact-current-dir
6471 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6472 ["Set activity..." clearcase-ucm-set-activity-current-dir
6474 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6475 ["Set NO activity" clearcase-ucm-set-activity-none-current-dir
6477 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6478 ["Rebase this stream" clearcase-gui-rebase
6480 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6481 ["Deliver from this stream" clearcase-gui-deliver
6483 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6484 "---------------------------------"
6485 (list "ClearCase GUI"
6486 ["ClearCase Explorer" clearcase-gui-clearexplorer
6488 :active clearcase-on-mswindows]
6489 ["Project Explorer" clearcase-gui-project-explorer
6491 ["Merge Manager" clearcase-gui-merge-manager
6493 ["Snapshot View Updater" clearcase-gui-snapshot-view-updater
6495 "---------------------------------"
6498 ;; Enable this when current buffer is on VOB.
6500 ["Make branch type" clearcase-mkbrtype
6503 "---------------------------------"
6504 ["Report Bug in ClearCase Mode" clearcase-submit-bug-report
6507 ["Dump internals" clearcase-dump
6509 :active (or (equal "rwhitby" (user-login-name))
6510 (equal "esler" (user-login-name)))]
6512 ["Flush caches" clearcase-flush-caches
6514 :active (or (equal "rwhitby" (user-login-name))
6515 (equal "esler" (user-login-name)))]
6517 "---------------------------------"
6518 ["Customize..." (customize-group 'clearcase)
6521 (if (and clearcase-minimise-menus
6522 (not clearcase-xemacs-p))
6523 (setq clearcase-menu-contents clearcase-menu-contents-minimised))
6527 (if (>= emacs-major-version '20)
6533 (list clearcase-mode-map)
6535 clearcase-menu-contents)
6537 (or clearcase-xemacs-p
6538 (add-to-list 'menu-bar-final-items 'ClearCase))))
6542 ;;{{{ clearcase-dired-mode menu
6544 ;;{{{ Related functions
6546 ;; nyi: this probably gets run for each menu element.
6547 ;; For better efficiency, look into using a one-pass ":filter"
6548 ;; to construct this menu dynamically.
6550 (defun clearcase-dired-mark-count ()
6551 (let ((old-point (point))
6553 (goto-char (point-min))
6554 (while (re-search-forward
6555 (concat "^" (regexp-quote (char-to-string
6556 dired-marker-char))) nil t)
6557 (setq count (1+ count)))
6558 (goto-char old-point)
6561 (defun clearcase-dired-current-ok-to-checkin ()
6562 (let ((file (dired-get-filename nil t)))
6564 (clearcase-file-ok-to-checkin file))))
6566 (defun clearcase-dired-current-ok-to-checkout ()
6567 (let ((file (dired-get-filename nil t)))
6569 (clearcase-file-ok-to-checkout file))))
6571 (defun clearcase-dired-current-ok-to-uncheckout ()
6572 (let ((file (dired-get-filename nil t)))
6574 (clearcase-file-ok-to-uncheckout file))))
6576 (defun clearcase-dired-current-ok-to-hijack ()
6577 (let ((file (dired-get-filename nil t)))
6579 (clearcase-file-ok-to-hijack file))))
6581 (defun clearcase-dired-current-ok-to-unhijack ()
6582 (let ((file (dired-get-filename nil t)))
6584 (clearcase-file-ok-to-unhijack file))))
6586 (defun clearcase-dired-current-ok-to-mkelem ()
6587 (let ((file (dired-get-filename nil t)))
6589 (clearcase-file-ok-to-mkelem file))))
6591 (defun clearcase-dired-current-ok-to-browse ()
6592 (let ((file (dired-get-filename nil t)))
6593 (clearcase-file-ok-to-browse file)))
6595 (defvar clearcase-dired-max-marked-files-to-check 5
6596 "The maximum number of marked files in a Dired buffer when constructing
6597 the ClearCase menu.")
6599 ;; nyi: speed these up by stopping check when a non-qualifying file is found
6601 ;; - hook the menu constuction and figure out what ops apply
6602 ;; - hook mark/unmark/move cursor
6604 (defun clearcase-dired-marked-ok-to-checkin ()
6605 (let ((files (dired-get-marked-files)))
6606 (or (> (length files) clearcase-dired-max-marked-files-to-check)
6607 (clearcase-utl-every (function clearcase-file-ok-to-checkin)
6610 (defun clearcase-dired-marked-ok-to-checkout ()
6611 (let ((files (dired-get-marked-files)))
6612 (or (> (length files) clearcase-dired-max-marked-files-to-check)
6613 (clearcase-utl-every (function clearcase-file-ok-to-checkout)
6616 (defun clearcase-dired-marked-ok-to-uncheckout ()
6617 (let ((files (dired-get-marked-files)))
6618 (or (> (length files) clearcase-dired-max-marked-files-to-check)
6619 (clearcase-utl-every (function clearcase-file-ok-to-uncheckout)
6622 (defun clearcase-dired-marked-ok-to-hijack ()
6623 (let ((files (dired-get-marked-files)))
6624 (or (> (length files) clearcase-dired-max-marked-files-to-check)
6625 (clearcase-utl-every (function clearcase-file-ok-to-hijack)
6628 (defun clearcase-dired-marked-ok-to-unhijack ()
6629 (let ((files (dired-get-marked-files)))
6630 (or (> (length files) clearcase-dired-max-marked-files-to-check)
6631 (clearcase-utl-every (function clearcase-file-ok-to-unhijack)
6634 (defun clearcase-dired-marked-ok-to-mkelem ()
6635 (let ((files (dired-get-marked-files)))
6636 (or (> (length files) clearcase-dired-max-marked-files-to-check)
6637 (clearcase-utl-every (function clearcase-file-ok-to-mkelem)
6640 (defun clearcase-dired-current-dir-ok-to-checkin ()
6641 (let ((dir (dired-current-directory)))
6642 (clearcase-file-ok-to-checkin dir)))
6644 (defun clearcase-dired-current-dir-ok-to-checkout ()
6645 (let ((dir (dired-current-directory)))
6646 (clearcase-file-ok-to-checkout dir)))
6648 (defun clearcase-dired-current-dir-ok-to-uncheckout ()
6649 (let ((dir (dired-current-directory)))
6650 (clearcase-file-ok-to-uncheckout dir)))
6656 ;; This version of the menu will hide rather than grey out inapplicable entries.
6658 (defvar clearcase-dired-menu-contents-minimised
6663 ["Checkin file" clearcase-checkin-dired-files
6665 :visible (and (< (clearcase-dired-mark-count) 2)
6666 (clearcase-dired-current-ok-to-checkin))]
6668 ["Edit checkout comment" clearcase-edit-checkout-comment-dired-file
6670 :visible (and (< (clearcase-dired-mark-count) 2)
6671 (clearcase-dired-current-ok-to-checkin))]
6673 ["Checkout file" clearcase-checkout-dired-files
6675 :visible (and (< (clearcase-dired-mark-count) 2)
6676 (clearcase-dired-current-ok-to-checkout))]
6678 ["Uncheckout file" clearcase-uncheckout-dired-files
6680 :visible (and (< (clearcase-dired-mark-count) 2)
6681 (clearcase-dired-current-ok-to-uncheckout))]
6683 ["Hijack file" clearcase-hijack-dired-files
6685 :visible (and (< (clearcase-dired-mark-count) 2)
6686 (clearcase-dired-current-ok-to-hijack))]
6688 ["Unhijack file" clearcase-unhijack-dired-files
6690 :visible (and (< (clearcase-dired-mark-count) 2)
6691 (clearcase-dired-current-ok-to-unhijack))]
6693 ["Find checkouts" clearcase-find-checkouts-in-current-view t]
6695 ["Make file an element" clearcase-mkelem-dired-files
6696 :visible (and (< (clearcase-dired-mark-count) 2)
6697 (clearcase-dired-current-ok-to-mkelem))]
6701 ["Checkin marked files" clearcase-checkin-dired-files
6703 :visible (and (>= (clearcase-dired-mark-count) 2)
6704 (clearcase-dired-marked-ok-to-checkin))]
6706 ["Checkout marked files" clearcase-checkout-dired-files
6708 :visible (and (>= (clearcase-dired-mark-count) 2)
6709 (clearcase-dired-marked-ok-to-checkout))]
6711 ["Uncheckout marked files" clearcase-uncheckout-dired-files
6713 :visible (and (>= (clearcase-dired-mark-count) 2)
6714 (clearcase-dired-marked-ok-to-uncheckout))]
6716 ["Hijack marked files" clearcase-hijack-dired-files
6718 :visible (and (>= (clearcase-dired-mark-count) 2)
6719 (clearcase-dired-marked-ok-to-hijack))]
6721 ["Unhijack marked files" clearcase-unhijack-dired-files
6723 :visible (and (>= (clearcase-dired-mark-count) 2)
6724 (clearcase-dired-marked-ok-to-unhijack))]
6726 ["Make marked files elements" clearcase-mkelem-dired-files
6728 :visible (and (>= (clearcase-dired-mark-count) 2)
6729 (clearcase-dired-marked-ok-to-mkelem))]
6732 ;; Current directory
6734 ["Checkin current-dir" clearcase-dired-checkin-current-dir
6736 :visible (clearcase-dired-current-dir-ok-to-checkin)]
6738 ["Checkout current dir" clearcase-dired-checkout-current-dir
6740 :visible (clearcase-dired-current-dir-ok-to-checkout)]
6742 ["Uncheckout current dir" clearcase-dired-uncheckout-current-dir
6744 :visible (clearcase-dired-current-dir-ok-to-uncheckout)]
6746 "---------------------------------"
6747 ["Describe file" clearcase-describe-dired-file
6750 ["Annotate file" clearcase-annotate-dired-file
6753 ["Show config-spec rule" clearcase-what-rule-dired-file
6757 ["Edit config-spec" clearcase-edcs-edit t]
6759 "---------------------------------"
6760 (list "Compare (Emacs)..."
6761 ["Compare with predecessor" clearcase-ediff-pred-dired-file
6764 ["Compare with branch base" clearcase-ediff-branch-base-dired-file
6767 ["Compare with named version" clearcase-ediff-named-version-dired-file
6770 (list "Compare (GUI)..."
6771 ["Compare with predecessor" clearcase-gui-diff-pred-dired-file
6774 ["Compare with branch base" clearcase-gui-diff-branch-base-dired-file
6777 ["Compare with named version" clearcase-gui-diff-named-version-dired-file
6780 (list "Compare (diff)..."
6781 ["Compare with predecessor" clearcase-diff-pred-dired-file
6784 ["Compare with branch base" clearcase-diff-branch-base-dired-file
6787 ["Compare with named version" clearcase-diff-named-version-dired-file
6790 "---------------------------------"
6791 ["Browse versions (dired)" clearcase-browse-vtree-dired-file
6792 :visible (clearcase-dired-current-ok-to-browse)]
6793 ["Vtree browser GUI" clearcase-gui-vtree-browser-dired-file
6796 "---------------------------------"
6797 (list "Update snapshot..."
6798 ["Update view" clearcase-update-view
6800 :visible (and (clearcase-file-is-in-view-p default-directory)
6801 (not (clearcase-file-is-in-mvfs-p default-directory)))]
6802 ["Update directory" clearcase-update-default-directory
6804 :visible (and (clearcase-file-is-in-view-p default-directory)
6805 (not (clearcase-file-is-in-mvfs-p default-directory)))]
6806 ["Update file" clearcase-update-dired-files
6808 :visible (and (< (clearcase-dired-mark-count) 2)
6809 (clearcase-dired-current-ok-to-checkout)
6810 (not (clearcase-file-is-in-mvfs-p default-directory)))]
6811 ["Update marked files" clearcase-update-dired-files
6813 :visible (and (>= (clearcase-dired-mark-count) 2)
6814 (not (clearcase-file-is-in-mvfs-p default-directory)))]
6816 "---------------------------------"
6817 (list "Element history..."
6818 ["Element history (full)" clearcase-list-history-dired-file
6821 ["Element history (branch)" clearcase-list-history-dired-file
6824 ["Element history (me)" clearcase-list-history-dired-file
6827 "---------------------------------"
6828 ["Show current activity" clearcase-ucm-describe-current-activity
6830 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6831 ["Make activity" clearcase-ucm-mkact-current-dir
6833 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6834 ["Set activity..." clearcase-ucm-set-activity-current-dir
6836 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6837 ["Set NO activity" clearcase-ucm-set-activity-none-current-dir
6839 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6840 ["Rebase this stream" clearcase-gui-rebase
6842 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6843 ["Deliver from this stream" clearcase-gui-deliver
6845 :visible (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
6846 "---------------------------------"
6847 (list "ClearCase GUI"
6848 ["ClearCase Explorer" clearcase-gui-clearexplorer
6850 :visible clearcase-on-mswindows]
6851 ["Project Explorer" clearcase-gui-project-explorer
6853 ["Merge Manager" clearcase-gui-merge-manager
6855 ["Snapshot View Updater" clearcase-gui-snapshot-view-updater
6857 "---------------------------------"
6859 ["Make branch type" clearcase-mkbrtype
6862 "---------------------------------"
6863 ["Report Bug in ClearCase Mode" clearcase-submit-bug-report
6866 ["Dump internals" clearcase-dump
6868 :visible (or (equal "rwhitby" (user-login-name))
6869 (equal "esler" (user-login-name)))]
6871 ["Flush caches" clearcase-flush-caches
6873 :visible (or (equal "rwhitby" (user-login-name))
6874 (equal "esler" (user-login-name)))]
6876 "---------------------------------"
6877 ["Customize..." (customize-group 'clearcase)
6880 (defvar clearcase-dired-menu-contents
6885 ["Checkin file" clearcase-checkin-dired-files
6887 :active (and (< (clearcase-dired-mark-count) 2)
6888 (clearcase-dired-current-ok-to-checkin))]
6890 ["Edit checkout comment" clearcase-edit-checkout-comment-dired-file
6892 :active (and (< (clearcase-dired-mark-count) 2)
6893 (clearcase-dired-current-ok-to-checkin))]
6895 ["Checkout file" clearcase-checkout-dired-files
6897 :active (and (< (clearcase-dired-mark-count) 2)
6898 (clearcase-dired-current-ok-to-checkout))]
6900 ["Uncheckout file" clearcase-uncheckout-dired-files
6902 :active (and (< (clearcase-dired-mark-count) 2)
6903 (clearcase-dired-current-ok-to-uncheckout))]
6905 ["Hijack file" clearcase-hijack-dired-files
6907 :active (and (< (clearcase-dired-mark-count) 2)
6908 (clearcase-dired-current-ok-to-hijack))]
6910 ["Unhijack file" clearcase-unhijack-dired-files
6912 :active (and (< (clearcase-dired-mark-count) 2)
6913 (clearcase-dired-current-ok-to-unhijack))]
6915 ["Make file an element" clearcase-mkelem-dired-files
6916 :active (and (< (clearcase-dired-mark-count) 2)
6917 (clearcase-dired-current-ok-to-mkelem))]
6921 ["Checkin marked files" clearcase-checkin-dired-files
6923 :active (and (>= (clearcase-dired-mark-count) 2)
6924 (clearcase-dired-marked-ok-to-checkin))]
6926 ["Checkout marked files" clearcase-checkout-dired-files
6928 :active (and (>= (clearcase-dired-mark-count) 2)
6929 (clearcase-dired-marked-ok-to-checkout))]
6931 ["Uncheckout marked files" clearcase-uncheckout-dired-files
6933 :active (and (>= (clearcase-dired-mark-count) 2)
6934 (clearcase-dired-marked-ok-to-uncheckout))]
6936 ["Hijack marked files" clearcase-hijack-dired-files
6938 :active (and (>= (clearcase-dired-mark-count) 2)
6939 (clearcase-dired-marked-ok-to-hijack))]
6941 ["Unhijack marked files" clearcase-unhijack-dired-files
6943 :active (and (>= (clearcase-dired-mark-count) 2)
6944 (clearcase-dired-marked-ok-to-unhijack))]
6946 ["Make marked files elements" clearcase-mkelem-dired-files
6948 :active (and (>= (clearcase-dired-mark-count) 2)
6949 (clearcase-dired-marked-ok-to-mkelem))]
6952 ;; Current directory
6954 ["Checkin current-dir" clearcase-dired-checkin-current-dir
6956 :active (clearcase-dired-current-dir-ok-to-checkin)]
6958 ["Checkout current dir" clearcase-dired-checkout-current-dir
6960 :active (clearcase-dired-current-dir-ok-to-checkout)]
6962 ["Uncheckout current dir" clearcase-dired-uncheckout-current-dir
6964 :active (clearcase-dired-current-dir-ok-to-uncheckout)]
6966 "---------------------------------"
6967 ["Describe file" clearcase-describe-dired-file
6970 ["Annotate file" clearcase-annotate-dired-file
6973 ["Show config-spec rule" clearcase-what-rule-dired-file
6977 ["Edit config-spec" clearcase-edcs-edit t]
6979 "---------------------------------"
6980 (list "Compare (Emacs)..."
6981 ["Compare with predecessor" clearcase-ediff-pred-dired-file
6984 ["Compare with branch base" clearcase-ediff-branch-base-dired-file
6987 ["Compare with named version" clearcase-ediff-named-version-dired-file
6990 (list "Compare (GUI)..."
6991 ["Compare with predecessor" clearcase-gui-diff-pred-dired-file
6994 ["Compare with branch base" clearcase-gui-diff-branch-base-dired-file
6997 ["Compare with named version" clearcase-gui-diff-named-version-dired-file
7000 (list "Compare (diff)..."
7001 ["Compare with predecessor" clearcase-diff-pred-dired-file
7004 ["Compare with branch base" clearcase-diff-branch-base-dired-file
7007 ["Compare with named version" clearcase-diff-named-version-dired-file
7010 "---------------------------------"
7011 ["Browse versions (dired)" clearcase-browse-vtree-dired-file
7012 :active (clearcase-dired-current-ok-to-browse)]
7013 ["Vtree browser GUI" clearcase-gui-vtree-browser-dired-file
7016 "---------------------------------"
7017 (list "Update snapshot..."
7018 ["Update view" clearcase-update-view
7020 :active (and (clearcase-file-is-in-view-p default-directory)
7021 (not (clearcase-file-is-in-mvfs-p default-directory)))]
7022 ["Update directory" clearcase-update-default-directory
7024 :active (and (clearcase-file-is-in-view-p default-directory)
7025 (not (clearcase-file-is-in-mvfs-p default-directory)))]
7026 ["Update file" clearcase-update-dired-files
7028 :active (and (< (clearcase-dired-mark-count) 2)
7029 (clearcase-dired-current-ok-to-checkout)
7030 (not (clearcase-file-is-in-mvfs-p default-directory)))]
7031 ["Update marked files" clearcase-update-dired-files
7033 :active (and (>= (clearcase-dired-mark-count) 2)
7034 (not (clearcase-file-is-in-mvfs-p default-directory)))]
7036 "---------------------------------"
7037 (list "Element history..."
7038 ["Element history (full)" clearcase-list-history-dired-file
7041 ["Element history (branch)" clearcase-list-history-dired-file
7044 ["Element history (me)" clearcase-list-history-dired-file
7047 "---------------------------------"
7048 ["Show current activity" clearcase-ucm-describe-current-activity
7050 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
7051 ["Make activity" clearcase-ucm-mkact-current-dir
7053 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
7054 ["Set activity..." clearcase-ucm-set-activity-current-dir
7056 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
7057 ["Set NO activity" clearcase-ucm-set-activity-none-current-dir
7059 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
7060 ["Rebase this stream" clearcase-gui-rebase
7062 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
7063 ["Deliver from this stream" clearcase-gui-deliver
7065 :active (clearcase-vprop-ucm (clearcase-fprop-viewtag default-directory))]
7066 "---------------------------------"
7067 (list "ClearCase GUI"
7068 ["ClearCase Explorer" clearcase-gui-clearexplorer
7070 :active clearcase-on-mswindows]
7071 ["Project Explorer" clearcase-gui-project-explorer
7073 ["Merge Manager" clearcase-gui-merge-manager
7075 ["Snapshot View Updater" clearcase-gui-snapshot-view-updater
7077 "---------------------------------"
7079 ["Make branch type" clearcase-mkbrtype
7082 "---------------------------------"
7083 ["Report Bug in ClearCase Mode" clearcase-submit-bug-report
7086 ["Dump internals" clearcase-dump
7088 :active (or (equal "rwhitby" (user-login-name))
7089 (equal "esler" (user-login-name)))]
7091 ["Flush caches" clearcase-flush-caches
7093 :active (or (equal "rwhitby" (user-login-name))
7094 (equal "esler" (user-login-name)))]
7096 "---------------------------------"
7097 ["Customize..." (customize-group 'clearcase)
7100 (if (and clearcase-minimise-menus
7101 (not clearcase-xemacs-p))
7102 (setq clearcase-dired-menu-contents clearcase-dired-menu-contents-minimised))
7106 (if (>= emacs-major-version '20)
7109 clearcase-dired-menu
7110 (list clearcase-dired-mode-map)
7111 "ClearCase Dired menu"
7112 clearcase-dired-menu-contents)
7114 (or clearcase-xemacs-p
7115 (add-to-list 'menu-bar-final-items 'ClearCase))))
7123 ;;{{{ Single-selection buffer widget
7125 ;; Keep the compiler quiet by declaring these
7126 ;; buffer-local variables here thus.
7128 (defvar clearcase-selection-window-config nil)
7129 (defvar clearcase-selection-interpreter nil)
7130 (defvar clearcase-selection-continuation nil)
7131 (defvar clearcase-selection-operands nil)
7133 (defun clearcase-ucm-make-selection-window (buffer-name
7135 selection-interpreter
7138 (let ((buf (get-buffer-create buffer-name)))
7144 (setq buffer-read-only nil)
7146 (setq truncate-lines t)
7150 (goto-char (point-min))
7151 (insert buffer-contents)
7153 ;; Insert mouse-highlighting
7156 (goto-char (point-min))
7157 (while (< (point) (point-max))
7161 (put-text-property (point)
7165 'mouse-face 'highlight))
7171 (setq buffer-read-only t)
7172 (use-local-map clearcase-selection-keymap)
7174 ;; Set up the interpreter and continuation
7176 (set (make-local-variable 'clearcase-selection-window-config)
7177 (current-window-configuration))
7178 (set (make-local-variable 'clearcase-selection-interpreter)
7179 selection-interpreter)
7180 (set (make-local-variable 'clearcase-selection-continuation)
7182 (set (make-local-variable 'clearcase-selection-operands)
7185 ;; Display the buffer
7189 (shrink-window-if-larger-than-buffer)
7190 (message "Use RETURN to select an item")))
7192 (defun clearcase-selection-continue ()
7196 ;; Call the interpreter to extract the item of interest
7199 (let ((item (funcall clearcase-selection-interpreter)))
7200 ;; Call the continuation.
7202 (apply clearcase-selection-continuation
7203 (append clearcase-selection-operands (list item))))
7205 ;; Restore window config
7207 (let ((sel-buffer (current-buffer)))
7208 (if clearcase-selection-window-config
7209 (set-window-configuration clearcase-selection-window-config))
7210 (delete-windows-on sel-buffer)
7211 (kill-buffer sel-buffer)))
7213 (defun clearcase-selection-mouse-continue (click)
7215 (mouse-set-point click)
7216 (clearcase-selection-continue))
7218 (defvar clearcase-selection-keymap
7219 (let ((map (make-sparse-keymap)))
7220 (define-key map [return] 'clearcase-selection-continue)
7221 (define-key map [mouse-2] 'clearcase-selection-mouse-continue)
7222 (define-key map "q" 'clearcase-utl-kill-view-buffer)
7223 ;; nyi: refresh list
7224 ;; (define-key map "g" 'clearcase-selection-get)
7231 ;;{{{ Integration with Emacs
7233 ;;{{{ Functions: examining the ClearCase installation
7235 ;; Discover ClearCase version-string
7237 (defun clearcase-get-version-string ()
7238 ;; Some care seems to be necessary to avoid problems caused by odd settings
7239 ;; of the "SHELL" environment variable. I found that simply
7240 ;; (shell-command-to-string "cleartool -version") on Windows-2000 with
7241 ;; SHELL==cmd.exe just returned a copy of the Windows command prompt. The
7242 ;; result was that clearcase-integrate would not complete.
7244 ;; The follow seems to work.
7246 (if clearcase-on-mswindows
7247 (shell-command-to-string "cmd /c cleartool -version")
7248 (shell-command-to-string "sh -c \"cleartool -version\"")))
7250 ;; Find where cleartool is installed.
7252 (defun clearcase-find-cleartool ()
7253 "Search directories listed in the PATH environment variable
7254 looking for a cleartool executable. If found return the full pathname."
7255 (let ((dir-list (parse-colon-path (getenv "PATH")))
7256 (cleartool-name (if clearcase-on-mswindows
7259 (cleartool-path nil))
7262 (function (lambda (dir)
7263 (let ((f (expand-file-name (concat dir cleartool-name))))
7264 (if (file-executable-p f)
7266 (setq cleartool-path f)
7267 (throw 'found t))))))
7272 (defun clearcase-non-lt-registry-server-online-p ()
7273 "Heuristic to determine if the local host is network-connected to
7274 its ClearCase servers. Used for a non-LT system."
7277 (buf (get-buffer-create " *clearcase-lsregion*")))
7281 (let ((process (start-process "lsregion"
7286 (timeout-occurred nil))
7288 ;; Now wait a little while, if necessary, for some output.
7290 (while (and (null result)
7291 (not timeout-occurred)
7292 (< (buffer-size) (length "Tag: ")))
7293 (if (null (accept-process-output process 10))
7294 (setq timeout-occurred t))
7295 (goto-char (point-min))
7296 (if (looking-at "Tag: ")
7299 (kill-process process)
7301 ;; If servers are apparently not online, keep the
7302 ;; buffer around so we can see what lsregion reported.
7304 (sit-for 0.01); Fix by AJM to prevent kill-buffer claiming process still running
7309 ;; We could have an LT system, which lacks ct+lsregion, but has ct+lssite.
7311 (defun clearcase-lt-registry-server-online-p ()
7312 "Heuristic to determine if the local host is network-connected to
7313 its ClearCase servers. Used for LT system."
7316 (buf (get-buffer-create " *clearcase-lssite*")))
7320 (let ((process (start-process "lssite"
7325 (timeout-occurred nil))
7327 ;; Now wait a little while, if necessary, for some output.
7329 (while (and (null result)
7330 (not timeout-occurred)
7331 (< (buffer-size) (length " view_cache_size")))
7332 (if (null (accept-process-output process 10))
7333 (setq timeout-occurred t))
7334 (goto-char (point-min))
7335 (if (re-search-forward "view_cache_size" nil t)
7338 (kill-process process)
7341 ;; If servers are apparently not online, keep the
7342 ;; buffer around so we can see what lssite reported.
7344 (sit-for 0.01); Fix by AJM to prevent kill-buffer claiming process still running
7349 ;; Find out if the ClearCase registry server is accessible.
7350 ;; We could be on a disconnected laptop.
7352 (defun clearcase-registry-server-online-p ()
7353 "Heuristic to determine if the local host is network-connected to
7354 its ClearCase server(s)."
7357 (clearcase-lt-registry-server-online-p)
7358 (clearcase-non-lt-registry-server-online-p)))
7361 ;;{{{ Functions: hooks
7363 ;;{{{ A find-file hook to turn on clearcase-mode
7365 (defun clearcase-hook-find-file-hook ()
7366 (let ((filename (buffer-file-name)))
7369 (clearcase-fprop-unstore-properties filename)
7370 (if (clearcase-file-would-be-in-view-p filename)
7372 ;; 1. Activate minor mode
7376 ;; 2. Pre-fetch file properties
7378 (if (file-exists-p filename)
7380 (clearcase-fprop-get-properties filename)
7382 ;; 3. Put branch/ver in mode-line
7384 (setq clearcase-mode
7385 (concat " ClearCase:"
7386 (clearcase-mode-line-buffer-id filename)))
7387 (force-mode-line-update)
7389 ;; 4. Schedule the asynchronous fetching of the view's properties
7390 ;; next time Emacs is idle enough.
7392 (clearcase-vprop-schedule-work (clearcase-fprop-viewtag filename))
7394 ;; 5. Set backup policy
7396 (unless clearcase-make-backup-files
7397 (make-local-variable 'backup-inhibited)
7398 (setq backup-inhibited t))))
7400 (clearcase-set-auto-mode)))))))
7402 (defun clearcase-set-auto-mode ()
7403 "Check again for the mode of the current buffer when using ClearCase version extended paths."
7405 (let* ((version (clearcase-vxpath-version-part (buffer-file-name)))
7406 (buffer-file-name (clearcase-vxpath-element-part (buffer-file-name))))
7408 ;; Need to recheck the major mode only if a version was appended.
7415 ;;{{{ A find-file hook for version-extended pathnames
7417 (defun clearcase-hook-vxpath-find-file-hook ()
7418 (if (clearcase-vxpath-p default-directory)
7419 (let ((element (clearcase-vxpath-element-part default-directory))
7420 (version (clearcase-vxpath-version-part default-directory)))
7422 ;; 1. Set the buffer name to <filename>@@/<branch path>/<version>.
7424 (let ((new-buffer-name
7425 (concat (file-name-nondirectory element)
7426 clearcase-vxpath-glue
7430 (or (string= new-buffer-name (buffer-name))
7432 ;; Uniquify the name, if necessary.
7435 (uniquifier-string ""))
7436 (while (get-buffer (concat new-buffer-name uniquifier-string))
7437 (setq uniquifier-string (format "<%d>" n))
7440 (concat new-buffer-name uniquifier-string)))))
7442 ;; 2. Set the default directory to the dir containing <filename>.
7444 (let ((new-dir (file-name-directory element)))
7445 (setq default-directory new-dir))
7447 ;; 3. Disable auto-saving.
7449 ;; If we're visiting <filename>@@/<branch path>/199
7450 ;; we don't want Emacs trying to find a place to create a "#199#.
7452 (auto-save-mode 0))))
7456 ;;{{{ A dired-mode-hook to turn on clearcase-dired-mode
7458 (defun clearcase-hook-dired-mode-hook ()
7459 ;; Force a re-computation of whether the directory is within ClearCase.
7461 (clearcase-fprop-unstore-properties default-directory)
7463 ;; Wrap this in an exception handler. Otherwise, diredding into
7464 ;; a deregistered or otherwise defective snapshot-view fails.
7467 ;; If this directory is below a ClearCase element,
7468 ;; 1. turn on ClearCase Dired Minor Mode.
7469 ;; 2. display branch/ver in mode-line
7471 (if (clearcase-file-would-be-in-view-p default-directory)
7473 (if clearcase-auto-dired-mode
7475 (clearcase-dired-mode 1)
7476 (clearcase-fprop-get-properties default-directory)
7477 (clearcase-vprop-schedule-work (clearcase-fprop-viewtag default-directory))))
7478 (setq clearcase-dired-mode
7479 (concat " ClearCase:"
7480 (clearcase-mode-line-buffer-id default-directory)))
7481 (force-mode-line-update)))
7482 (error (message "Error fetching ClearCase properties of %s" default-directory))))
7486 ;;{{{ A dired-after-readin-hook to add ClearCase information to the display
7488 (defun clearcase-hook-dired-after-readin-hook ()
7490 ;; If in clearcase-dired-mode, reformat the buffer.
7492 (if clearcase-dired-mode
7494 (clearcase-dired-reformat-buffer)
7495 (if clearcase-dired-show-view
7496 (clearcase-dired-insert-viewtag))))
7501 ;;{{{ A write-file-hook to auto-insert a version-string.
7503 ;; To use this, put a line containing this in the first 8 lines of your file:
7504 ;; ClearCase-version: </main/laptop/155>
7505 ;; and make sure that clearcase-version-stamp-active gets set to true at least
7506 ;; locally in the file.
7508 (defvar clearcase-version-stamp-line-limit 1000)
7509 (defvar clearcase-version-stamp-begin-regexp "ClearCase-version:[ \t]<")
7510 (defvar clearcase-version-stamp-end-regexp ">")
7511 (defvar clearcase-version-stamp-active nil)
7513 (defun clearcase-increment-version (version-string)
7514 (let* ((branch (clearcase-vxpath-branch version-string))
7515 (number (clearcase-vxpath-version version-string))
7516 (new-number (1+ (string-to-number number))))
7517 (format "%s%d" branch new-number)))
7519 (defun clearcase-version-stamp ()
7521 (if (and clearcase-mode
7522 clearcase-version-stamp-active
7523 (file-exists-p buffer-file-name)
7524 (equal 'version (clearcase-fprop-mtype buffer-file-name)))
7525 (let ((latest-version (clearcase-fprop-predecessor-version buffer-file-name)))
7527 ;; Note: If the buffer happens to be folded, we may not find the place
7528 ;; to insert the version-stamp. Folding mode really needs to supply a
7529 ;; 'save-folded-excursion function to solve this one. We won't attempt
7530 ;; a cheaper hack here.
7535 (goto-char (point-min))
7536 (forward-line clearcase-version-stamp-line-limit)
7537 (let ((limit (point))
7540 (goto-char (point-min))
7541 (while (and (< (point) limit)
7542 (re-search-forward clearcase-version-stamp-begin-regexp
7545 (setq v-start (point))
7547 (let ((line-end (point)))
7549 (if (re-search-forward clearcase-version-stamp-end-regexp
7552 (setq v-end (match-beginning 0)))))
7554 (let ((new-version-stamp (clearcase-increment-version latest-version)))
7556 (delete-region v-start v-end)
7557 (insert-and-inherit new-version-stamp)))))))))
7559 (defun clearcase-hook-write-file-hook ()
7561 (clearcase-version-stamp)
7562 ;; Important to return nil so the files eventually gets written.
7568 ;;{{{ A kill-buffer hook
7570 (defun clearcase-hook-kill-buffer-hook ()
7571 (let ((filename (buffer-file-name)))
7573 ;; W3 has buffers in which 'buffer-file-name is bound to
7574 ;; a URL. Don't attempt to unstore their properties.
7576 (boundp 'buffer-file-truename)
7577 buffer-file-truename)
7578 (clearcase-fprop-unstore-properties filename))))
7582 ;;{{{ A kill-emacs-hook
7584 (defun clearcase-hook-kill-emacs-hook ()
7585 (clearcase-utl-clean-tempfiles))
7590 ;;{{{ Function: to replace toggle-read-only
7592 (defun clearcase-toggle-read-only (&optional arg)
7593 "Change read-only status of current buffer, perhaps via version control.
7594 If the buffer is visiting a ClearCase version, then check the file in or out.
7595 Otherwise, just change the read-only flag of the buffer. If called with an
7596 argument then just change the read-only flag even if visiting a ClearCase
7601 ((and (clearcase-fprop-mtype buffer-file-name)
7603 (file-writable-p buffer-file-name)
7607 ((clearcase-fprop-mtype buffer-file-name)
7608 (clearcase-next-action-current-buffer))
7611 (toggle-read-only))))
7614 ;;{{{ Functions: file-name-handlers
7616 ;;{{{ Start dynamic views automatically when paths to them are used
7618 ;; This handler starts views when viewroot-relative paths are dereferenced.
7620 ;; nyi: for now really only seems useful on Unix.
7622 (defun clearcase-viewroot-relative-file-name-handler (operation &rest args)
7624 (clearcase-when-debugging
7625 (if (fboundp 'clearcase-utl-syslog)
7626 (clearcase-utl-syslog "*clearcase-fh-trace*"
7627 (cons "clearcase-viewroot-relative-file-name-handler:"
7628 (cons operation args)))))
7630 ;; Inhibit the handler to avoid recursion.
7632 (let ((inhibit-file-name-handlers
7633 (cons 'clearcase-viewroot-relative-file-name-handler
7634 (and (eq inhibit-file-name-operation operation)
7635 inhibit-file-name-handlers)))
7636 (inhibit-file-name-operation operation))
7638 (let ((first-arg (car args)))
7639 ;; We don't always get called with a string.
7640 ;; e.g. one file operation is verify-visited-file-modtime, whose
7641 ;; first argument is a buffer.
7643 (if (stringp first-arg)
7645 ;; Now start the view if necessary
7648 (let* ((path (clearcase-path-remove-useless-viewtags first-arg))
7649 (viewtag (clearcase-vrpath-viewtag path))
7650 (default-directory (clearcase-path-remove-useless-viewtags default-directory)))
7652 (clearcase-viewtag-try-to-start-view viewtag))))))
7653 (apply operation args))))
7657 ;;{{{ Completion on viewtags
7659 ;; This handler provides completion for viewtags.
7661 (defun clearcase-viewtag-file-name-handler (operation &rest args)
7663 (clearcase-when-debugging
7664 (if (fboundp 'clearcase-utl-syslog)
7665 (clearcase-utl-syslog "*clearcase-fh-trace*"
7666 (cons "clearcase-viewtag-file-name-handler:"
7667 (cons operation args)))))
7670 ((eq operation 'file-name-completion)
7671 (save-match-data (apply 'clearcase-viewtag-completion args)))
7673 ((eq operation 'file-name-all-completions)
7674 (save-match-data (apply 'clearcase-viewtag-completions args)))
7677 (let ((inhibit-file-name-handlers
7678 (cons 'clearcase-viewtag-file-name-handler
7679 (and (eq inhibit-file-name-operation operation)
7680 inhibit-file-name-handlers)))
7681 (inhibit-file-name-operation operation))
7682 (apply operation args)))))
7684 (defun clearcase-viewtag-completion (file dir)
7685 (try-completion file (clearcase-viewtag-all-viewtag-dirs-obarray)))
7687 (defun clearcase-viewtag-completions (file dir)
7688 (let ((tags (all-completions file
7689 (clearcase-viewtag-all-viewtags-obarray))))
7691 (function (lambda (tag)
7697 ;;{{{ File name handler for version extended file names
7699 ;; For version extended pathnames there are two possible answers
7701 ;; file-name-directory
7702 ;; file-name-nondirectory
7704 ;; 1. that pertaining to the element path, e.g.
7705 ;; (file-name-directory "DIR/FILE@@/BRANCH/VERSION")
7707 ;; 2. that pertaining to the version path, e.g.
7708 ;; (file-name-directory "DIR/FILE@@/BRANCH/VERSION")
7709 ;; ==> "DIR/FILE@@/BRANCH/"
7711 ;; Often we'd like the former, but sometimes we'd like the latter, for example
7712 ;; inside clearcase-browse-vtree, where it calls dired. Within dired on Gnu
7713 ;; Emacs, it calls file-name-directory on the supplied pathname and in this
7714 ;; case we want the version (i.e. branch) path to be used.
7716 ;; How to get the behaviour we want ?
7721 ;; Define a variable clearcase-treat-branches-as-dirs, which modifies
7722 ;; the behaviour of clearcase-vxpath-file-name-handler to give answer (1).
7724 ;; Just before we invoke dired inside clearcase-browse-vtree, dynamically
7725 ;; bind clearcase-treat-branches-as-dirs to t. Also in the resulting Dired Mode
7726 ;; buffer, make clearcase-treat-branches-as-dirs buffer-local and set it.
7728 ;; Unfortunately this doesn't quite give us what we want. For example I often
7729 ;; invoke grep from a dired buffer on a branch-qua-directory to scan all the
7730 ;; version on that branch for a certain string. The grep-mode buffer has no
7731 ;; buffer-local binding for clearcase-treat-branches-as-dirs so the grep
7732 ;; command runs in "DIR/" instead of in "DIR/FILE@@/BRANCH/".
7737 ;; Modify the semantics of clearcase-vxpath-file-name-handler so that
7738 ;; if the filename given is a pathname to an existing branch-qua-directory
7739 ;; give answer 2, otherwise give answer 1.
7744 ;; Use the existence of a Dired Mode buffer on "DIR/FILE@@/BRANCH/" to
7745 ;; change the semantics of clearcase-vxpath-file-name-handler.
7747 ;; (A) is unsatisfactory and I'm not entirely happy with (B) nor (C) so for now
7748 ;; I'm going to disable this filename handler until I'm more convinced it is
7751 (defun clearcase-vxpath-file-name-handler (operation &rest args)
7752 (clearcase-when-debugging
7753 (if (fboundp 'clearcase-utl-syslog)
7754 (clearcase-utl-syslog "*clearcase-fh-trace*"
7755 (cons "clearcase-vxpath-file-name-handler:"
7756 (cons operation args)))))
7757 ;; Inhibit recursion:
7759 (let ((inhibit-file-name-handlers
7760 (cons 'clearcase-vxpath-file-name-handler
7761 (and (eq inhibit-file-name-operation operation)
7762 inhibit-file-name-handlers)))
7763 (inhibit-file-name-operation operation))
7765 (cond ((eq operation 'file-name-nondirectory)
7766 (file-name-nondirectory (clearcase-vxpath-element-part
7769 ((eq operation 'file-name-directory)
7770 (file-name-directory (clearcase-vxpath-element-part
7774 (apply operation args)))))
7779 ;;{{{ Advice: Disable VC in the MVFS
7781 ;; This handler ensures that VC doesn't attempt to operate inside the MVFS.
7782 ;; This stops it from futile searches for RCS directories and the like inside.
7783 ;; It prevents a certain amount of clutter in the MVFS' noent-cache.
7786 (defadvice vc-registered (around clearcase-interceptor disable compile)
7787 "Disable normal behavior if in a clearcase dynamic view.
7788 This is enabled/disabled by clearcase-integrate/clearcase-unintegrate."
7789 (if (clearcase-file-would-be-in-view-p (ad-get-arg 0))
7795 ;;{{{ Functions: integrate and un-integrate.
7797 (defun clearcase-integrate ()
7798 "Enable ClearCase integration"
7803 (clearcase-fprop-clear-all-properties)
7804 (clearcase-vprop-clear-all-properties)
7806 ;; 1. Install hooks.
7808 (add-hook 'find-file-hooks 'clearcase-hook-find-file-hook)
7809 (add-hook 'find-file-hooks 'clearcase-hook-vxpath-find-file-hook)
7810 (add-hook 'dired-mode-hook 'clearcase-hook-dired-mode-hook)
7811 (add-hook 'dired-after-readin-hook 'clearcase-hook-dired-after-readin-hook)
7812 (add-hook 'kill-buffer-hook 'clearcase-hook-kill-buffer-hook)
7813 (add-hook 'write-file-hooks 'clearcase-hook-write-file-hook)
7814 (add-hook 'kill-emacs-hook 'clearcase-hook-kill-emacs-hook)
7816 ;; 2. Install file-name handlers.
7818 ;; 2.1 Start views when //view/TAG or m:/TAG is referenced.
7820 (add-to-list 'file-name-handler-alist
7821 (cons clearcase-vrpath-regexp
7822 'clearcase-viewroot-relative-file-name-handler))
7824 ;; 2.2 Completion on viewtags.
7826 (if clearcase-complete-viewtags
7827 (add-to-list 'file-name-handler-alist
7828 (cons clearcase-viewtag-regexp
7829 'clearcase-viewtag-file-name-handler)))
7831 ;; 2.3 Turn off RCS/VCS/SCCS activity inside a ClearCase dynamic view.
7833 (if clearcase-suppress-vc-within-mvfs
7834 (when clearcase-suppress-vc-within-mvfs
7835 (ad-enable-advice 'vc-registered 'around 'clearcase-interceptor)
7836 (ad-activate 'vc-registered)))
7838 ;; Disabled for now. See comments above clearcase-vxpath-file-name-handler.
7840 ;; ;; 2.4 Add file name handler for version extended path names
7842 ;; (add-to-list 'file-name-handler-alist
7843 ;; (cons clearcase-vxpath-glue 'clearcase-vxpath-file-name-handler))
7846 (defun clearcase-unintegrate ()
7847 "Disable ClearCase integration"
7852 (clearcase-fprop-clear-all-properties)
7853 (clearcase-vprop-clear-all-properties)
7857 (remove-hook 'find-file-hooks 'clearcase-hook-find-file-hook)
7858 (remove-hook 'find-file-hooks 'clearcase-hook-vxpath-find-file-hook)
7859 (remove-hook 'dired-mode-hook 'clearcase-hook-dired-mode-hook)
7860 (remove-hook 'dired-after-readin-hook 'clearcase-hook-dired-after-readin-hook)
7861 (remove-hook 'kill-buffer-hook 'clearcase-hook-kill-buffer-hook)
7862 (remove-hook 'write-file-hooks 'clearcase-hook-write-file-hook)
7863 (remove-hook 'kill-emacs-hook 'clearcase-hook-kill-emacs-hook)
7865 ;; 2. Remove file-name handlers.
7867 (setq file-name-handler-alist
7868 (delete-if (function
7871 '(clearcase-viewroot-relative-file-name-handler
7872 clearcase-viewtag-file-name-handler
7873 clearcase-vxpath-file-name-handler))))
7874 file-name-handler-alist))
7876 ;; 3. Turn on RCS/VCS/SCCS activity everywhere.
7878 (ad-disable-advice 'vc-registered 'around 'clearcase-interceptor)
7879 (ad-activate 'vc-registered))
7883 ;; Here's where we really wire it all in:
7885 (defvar clearcase-cleartool-path nil)
7886 (defvar clearcase-clearcase-version-installed nil)
7887 (defvar clearcase-lt nil)
7888 (defvar clearcase-v3 nil)
7889 (defvar clearcase-v4 nil)
7890 (defvar clearcase-v6 nil)
7891 (defvar clearcase-servers-online nil)
7892 (defvar clearcase-setview-root nil)
7893 (defvar clearcase-setview-viewtag)
7894 (defvar clearcase-setview-root nil)
7895 (defvar clearcase-setview-viewtag nil)
7898 ;; If the SHELL environment variable points to the wrong place,
7899 ;; call-process fails on Windows and this startup fails.
7900 ;; Check for this and unset the useless EV.
7902 (let ((shell-ev-value (getenv "SHELL")))
7903 (if clearcase-on-mswindows
7904 (if (stringp shell-ev-value)
7905 (if (not (executable-find shell-ev-value))
7906 (setenv "SHELL" nil)))))
7908 ;; Things have to be done here in a certain order.
7910 ;; 1. Make sure cleartool is on the shell search PATH.
7912 (if (setq clearcase-cleartool-path (clearcase-find-cleartool))
7914 ;; 2. Try to discover what version of ClearCase we have:
7916 (setq clearcase-clearcase-version-installed (clearcase-get-version-string))
7918 (not (null (string-match "ClearCase LT"
7919 clearcase-clearcase-version-installed))))
7921 (not (null (string-match "^ClearCase version 3"
7922 clearcase-clearcase-version-installed))))
7924 (not (null (string-match "^ClearCase version 4"
7925 clearcase-clearcase-version-installed))))
7927 (not (null (string-match "^ClearCase \\(LT \\)?version 2002.05"
7928 clearcase-clearcase-version-installed))))
7930 (not (null (string-match "^ClearCase \\(LT \\)?version 2003.06"
7931 clearcase-clearcase-version-installed))))
7933 ;; 3. Gather setview information:
7935 (if (setq clearcase-setview-root (if (not clearcase-on-mswindows)
7936 (getenv "CLEARCASE_ROOT")))
7937 (setq clearcase-setview-viewtag
7938 (file-name-nondirectory clearcase-setview-root)))
7940 ;; 4. Discover if the servers appear to be online.
7942 (setq clearcase-servers-online (clearcase-registry-server-online-p))
7944 (if clearcase-servers-online
7946 ;; 5. Everything seems in place to ensure that ClearCase mode will
7947 ;; operate correctly, so integrate now.
7950 (clearcase-integrate)
7951 ;; Schedule a fetching of the view properties when next idle.
7952 ;; This avoids awkward pauses after the user reaches for the
7953 ;; ClearCase menubar entry.
7955 (if clearcase-setview-viewtag
7956 (clearcase-vprop-schedule-work clearcase-setview-viewtag)))))))
7958 (if (not clearcase-servers-online)
7959 (message "ClearCase apparently not online. ClearCase/Emacs integration not installed."))
7963 (provide 'clearcase)
7965 ;;; clearcase.el ends here
7969 ;; clearcase-version-stamp-active: t