;; From gamin@carette.Foo.COM Fri Jun 27 05:34:38 1997
;; Return-Path:
;; Received: from relay.lehman.com by batman.lehman.com (4.1/Lehman Bros. V1.6)
;; id AA21870; Fri, 27 Jun 97 05:34:38 EDT
;; Received: from lehman.Lehman.COM by relay.lehman.com (4.1/LB-0.6)
;; id AA15021; Fri, 27 Jun 97 05:34:37 EDT
;; Received: (from smap@localhost) by lehman.Lehman.COM (8.8.5/8.6.12) id FAA13815 for ; Fri, 27 Jun 1997 05:33:20 -0400 (EDT)
;; Received: from bizet.videotron.net(205.151.222.75) by lehman via smap (V1.3)
;; id tmp013812; Fri Jun 27 05:33:12 1997
;; Received: from carette.Foo.COM (ppp143.103.mmtl.videotron.net [207.253.103.143]) by bizet.videotron.net (8.8.5/8.8.2) with ESMTP id FAA03499 for ; Fri, 27 Jun 1997 05:33:03 -0400 (EDT)
;; Received: (from gamin@localhost) by carette.Foo.COM (8.7.6/8.7.3) id FAA07422; Fri, 27 Jun 1997 05:32:11 -0400
;; Date: Fri, 27 Jun 1997 05:32:11 -0400
;; Message-Id: <199706270932.FAA07422@carette.Foo.COM>
;; To: flee@lehman.com (Franklin Lee)
;; Subject: Re: Global Replace modifications: context diff
;; References: <1068-Thu26Jun1997233127-0400-flee@lehman.com>
;; From: Martin Boyer
;; Status: RO
;; >I was able to find what appears to be the 'original' version of
;; >global-replace from which I made the modifications. The context diff
;; >is below.
;; Cool!
;; It turns out that I only had to merge the "win32 drive:/path" part.
;; The rest is only relevant to very old versions of Emacs and XEmacs.
;; >I had difficulties with global-replace in this regard because of how
;; >tightly it was coupled to features in compile.el.
;; That is the main difference between the version you had and the new
;; version. The new version is no longer so incestuous. It also has a
;; new name (globrep.el).
;; Let me know if this works for you (a context diff between what you
;; have and the new version would be too big).
;; Martin
;------------------------------------------------------------------------------
;
; Nom : (global-replace-lines)
; (global-replace )
; (global-grep-and-replace
; )
; Fonction: Search and replace strings in multiple files.
; Fichiers: globrep.el
; Notes : This version is known to work on XEmacs 19.14.
;
; Créé : 20 avril 90 --------- Martin Boyer
; Modifié : 27 juin 97 -------10- Martin Boyer
; $Id$
;
; Historique:
; $Log$
;
; 3 août 94 --------7- Martin Boyer
; Renamed the file as globrep.el to accomodate broken systems that
; allow only 14 characters in a file name. Used query-replace-map.
; Version 2.1.
;
; 26 juillet 94 -----6- Martin Boyer
; Added VC support. Used `compilation-next-error-locus' and
; `compilation-goto-locus' instead of local hacks. Version 2.0.
;
; 20 mars 94 --------5- Martin Boyer
; Added code to preformat a "ChangeLog" entry. Version 1.2.
;
; 7 mars 94 --------4- Martin Boyer
; Updated the documentation after initial comments from the net
; and the FSF. This is version 1.1.
;
; 1 mars 94 --------3- Martin Boyer
; Version 1.0: Prepared for distribution through LCD.
;
; 23 novembre 93 ----2- Martin Boyer
; Ported to version 19.
; No longer requires a modified version of compile.el.
;------------------------------------------------------------------------------
;;; COPYRIGHT NOTICE
;;;
;; Copyright (C) 1990, 1994 Martin Boyer and Hydro-Quebec.
;; Copyright (C) 1994 Free Software Foundation
;; Copyright (C) 1997 Martin Boyer
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; author of this program or to the
;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
;; Send bug reports to the author, Martin Boyer .
;;;; Contributors
;;
;; Thanks to Justin Gordon for doing the conversion
;; to XEmacs (with the help of Ben Wing):
;;
;; Here are the converted emacs files. Special thanks to Ben who
;; provided me the XEmacs equivalents of read-event and
;; listify-key-sequence: Use `next-command-event' for read-event and
;; `character-to-event' for listify-key-sequence. character-to-event
;; converts a single key chord. This function might not be
;; necessary, however, because `next-command-event' returns more
;; sensible things than `read-event' does.
;;
;; Thanks to Gary Oberbrunner for providing the
;; global-replace-use-vc variable:
;;
;; I don't know if you are still maintaining globrep.el, but I made
;; some changes to it to allow it to ignore VC. This is important
;; when the VC backend is CVS, since CVS doesn't need checkouts.
;; Without these changes, globrep confuses CVS quite badly.
;;
;; Thanks to Franklin Lee for providing the idea for
;; the NT port:
;;
;; Dear M. Boyer,
;;
;; Please excuse my lack of French in thanking you for writing
;; global-replace!
;;
;; I am using it with Win NT/95 emacs v 19.34, and patched it a bit
;; to comply with the version of compile.el. I also discovered
;; that, due to the idiotic filesystem conventions those
;; dos-descendants, that the version of global replace doesn't work
;; without another patch! so... I have included below my version
;; of global-replace which works with v 19.34 on sun AND winnt
;; versions of emacs. In particular, the patch to
;; global-replace-lines marked 'win32' takes care of the extra ':'
;; character for drive letters in Windows pathnames, e.g.,
;;
;; g:/devtools.32/lisp/foo.el: 34: blah blah
;;
;; doesn't get completely chewed up!
;;; DESCRIPTION AND USAGE
;;;
;; This program can be used to search for and replace strings in
;; multiple files. The idea is to find, in a set of files, all the
;; lines that contain a string to be changed. Then, one can edit
;; the result of this search, and the program will take care of the
;; tedious task of splicing the changes back into the original files.
;;
;; The search operation can be done separately, before calling any
;; command in this file. The output of the search has be be in a
;; special format and, indeed, there is an emacs command that does
;; that for you; see the description of the grep command (C-h f grep
;; [RET]).
;;
;; Once you have the list of interesting lines in a "grep" buffer, you
;; can edit it in any way you like, under the following conditions:
;;
;; 1. The file names and line numbers are not edited.
;; 2. Lines can be deleted altogether, in which case
;; the program will not attempt to put them back in
;; the original files.
;; 3. A single line can be replaced by multiple lines if carriage
;; returns (^M) are used to delimit the end of a line. In
;; other words, the program replaces all '\r' by '\n' just
;; before putting the edited lines back in place.
;;
;; The last step is to call global-replace-lines. This command will
;; prompt you for every line in the "grep" buffer -- even those that
;; you didn't edit, asking if you want to carry to the original file
;; the change you made on that line. You will also be offerred to
;; quit or to perform all replacements without confirmation. Since a
;; large number of files may be involved, the command will also ask
;; you if you want to dispose of a buffer after all processing has
;; been completed on that buffer.
;;
;; Finally, the command can also present a preformatted "ChangeLog"
;; entry, saving you some time in keeping the ChangeLog file up to
;; date.
;;
;; If you prefer, there are two commands that can call grep for you,
;; and then proceed to carry out the changes. global-grep-and-replace
;; will prompt you for original and replacement strings, and for a
;; list of files. It will then call grep, allow you to inspect and
;; modify the result, and then will preform the actual replacements.
;; global-replace is similar, but will skip the inspection stage.
;;
;; You may wish to examine the documentation of the query.el library.
;; But if you don't, here's the short story: whenever you are prompted
;; and a set of possible answers appear between [brackets], the first
;; is always the default.
;;
;;
;; This program differs from tags-query-replace in the following ways:
;;
;; 1. tags-query-replace requires a TAGS file, and not all files can
;; be processed by etags.
;;
;; 2. Shell wildcards are more flexible that TAGS files to specify
;; which files to process. Anyhow, some variants of grep can take
;; a list a files from a file, so the TAGS functionality can be
;; duplicated.
;;
;; 3. Mainly, global-replace is more flexible in the way the
;; replacements are done; the list of lines to be changed, along
;; with the name of the file where they appear, is directly
;; available in an emacs buffer. From then on, the user can elect
;; not to change lines from certain files, or to process certain
;; files differently, etc.
;;
;; 4. Finally, I find it faster and easier to use, since I specify all
;; the replacements in one operation (editing the result from
;; grep), and then tell emacs to do the tedious job of actually
;; putting the changes back in the files.
;;; INSTALLATION
;;;
;; Put this file somewhere in your load-path and byte-compile it.
;; Retrieve and install query.el in the same fashion.
;; Put the following lines in your .emacs:
;;
;; (autoload 'global-replace-lines "globrep"
;; "Put back grepped lines" t)
;; (autoload 'global-replace "globrep"
;; "query-replace across files" t)
;; (autoload 'global-grep-and-replace "globrep"
;; "grep and query-replace across files" t)
;;; LCD Archive Entry:
;;;
;; global-replace|Martin Boyer|gamin@videotron.ca|
;; Search and replace strings in multiple files|
;; 26-jun-97|2.2|~/packages/globrep.el.Z|
;;; BUGS
;;;
;; `global-replace-lines' should try to preserve attributes
;; of the files it visits:
;; don't offer to kill the buffer if it existed before the global-replace
;; don't call vc-checkin if the checkout wasn't done by global-replace
;;
;; There should be a way to say "replace all occurrences in this buffer" or
;; "skip the rest of this buffer". Perhaps it should even be the default.
;;
;; I don't understand very well the code in vc.el. Consequently, VC
;; support is probably buggy.
(require 'compile)
(require 'query)
(require 'cl)
(require 'vc)
(provide 'globrep)
(autoload 'change-log-fill-paragraph "add-log")
(defconst global-replace-version "2.2"
"The version number of the `global-replace' library.")
(defvar global-replace-use-vc nil
"If non-nil, global-replace checks files out if they are managed by vc.")
(defvar globrep-xemacs (string-match "XEmacs" (emacs-version))
"Non-nil if we are running XEmacs. Otherwise, assume we are running Emacs.")
(defvar globrep-win32 (eq system-type 'windows-nt)
"Non-nil if we are in Win95, Windows NT 3.5+, Windows NT 4.0+.")
(defvar globrep-chglog-prompt "Do you want to create a ChangeLog entry? "
"prompt string used several places within globrep.el. don't touch!")
(defmacro globrep-confirm (prompt)
(` (if globrep-win32
(yes-or-no-p (, prompt))
(confirm (, prompt)))))
(defmacro globrep-read-event ()
(` (if globrep-xemacs
(next-command-event)
(read-event))))
(defun global-replace-lines (&optional rescan)
"Splice back to their original files the (possibly edited) output of grep.
This is a type of query-replace in which the original lines (in the
files) are replaced by those currently in the grep-output buffer. You
will be asked to replace (y), don't replace (n), quit (q), or replace all.
When all changes are done in a file, you will be asked to
either save and kill the buffer, simply save it, or leave it as is.
In the replacement text, all ^M are changed to newlines, to allow a
single line to be replaced by multiple lines.
Global-replace will check out files automatically when needed (and
will also offer to steal a lock as a last resort). Files checked out
this way are automatically checked in."
(interactive "P")
(let ((comment nil)
(continue 'start)
(loops -1)
next-error
(count 0)
action
(modified nil) ;did we modify the current file?
(modified-files '()) ;the list of modified files
buf b e
next-buf
file-under-vc ;nil or the name of the current file
file-editable
replacement-line
(message
(substitute-command-keys
"Globally replacing lines: (\\\\[help] for help) "))
)
(if global-replace-use-vc
(if (and (interactive-p)
(globrep-confirm "Do you want to create a VC comment? "))
(setq comment (g-r-format-vc-comment))))
(or comment
(setq comment "")) ;VC gets very sick if there's no comment
(while continue
(incf loops)
(setq next-error (compilation-next-error-locus nil rescan t))
(setq rescan nil) ;We are not just starting anymore
(if (or (eq continue 'exit) (null next-error))
(setq continue nil)
(setq next-buf (marker-buffer (cdr next-error))))
(if (null next-error)
(setq next-buf nil))
(setq action
(if (or (eq continue 'start) (eq next-buf buf))
"no"
(switch-to-buffer buf)
(if (not modified)
;; Was it modified by something other than global-replace?
(if (buffer-modified-p)
(query-string "Done with this buffer (no replacements), dispose?"
'("save" "no"))
(query-string "Done with this buffer (unmodified), dispose?"
'("kill" "no")))
(setq modified nil)
(setq modified-files
(nconc modified-files (list (buffer-file-name buf))))
(query-string "Done with this buffer, dispose?"
'("kill (after saving)" "save" "no")))))
;; Dispose of the buffer according to user's choice.
;; Do nothing if action is "no".
;; Because global replaces in multiple files may create a large
;; number of uninteresting buffers, it is useful to remove them
;; after the replace.
(when (or (string= action "save")
(string= action "kill (after saving)"))
(set-buffer buf)
(save-buffer nil))
(when (string-match "^kill" action)
;; Files automatically checked out by global-replace are
;; automatically checked in.
(if (and global-replace-use-vc file-under-vc)
(vc-checkin file-under-vc nil comment))
(kill-buffer buf))
(when continue
(if (eq continue 'start)
(setq continue t)) ;We are not just starting anymore
(compilation-goto-locus next-error)
(unless (eq next-buf buf)
;; This is a new file. See if it's under VC
(setq file-under-vc (buffer-file-name))
(if (or (not global-replace-use-vc)
(not (vc-name file-under-vc)))
(setq file-under-vc nil
file-editable t)
(let ((owner (vc-locking-user file-under-vc)))
(if (and owner
(not (string-equal owner (user-login-name))))
;; I can't see how I can wait for the lock to be stolen
;; without using a recursive edit. vc-steal-lock doesn't
;; block.
(if (not (globrep-confirm
"Do you wish to steal the lock on the current file? "))
(setq file-editable nil)
(message
(substitute-command-keys
"Type \\[exit-recursive-edit] after taking the lock."))
(recursive-edit)
;; Verify that the lock was indeed granted.
(setq action
(not (string-equal (vc-locking-user file-under-vc)
(user-login-name)))))
;; The file is registered under VC, but either
;; there is no lock on it or we own the lock
(setq file-editable t))
(if (and file-editable buffer-read-only)
(vc-checkout-writable-buffer file-under-vc)
;; Remember that we don't need to check it in
(setq file-under-vc nil))))
(setq buf (current-buffer)) ;Where the grep hit occurred
)
(let ((done nil) ;when done with a line
(replaced nil) ;when replacement did take place
key)
;; Given that buffers may be killed, is this reasonable?
(unless (eq continue 'automatic)
(undo-boundary))
;; Loop reading commands until one of them sets done,
;; which means it has finished handling this occurrence.
(while (not done)
(if file-editable
(unless (eq continue 'automatic)
(message message)
(setq key (vector (globrep-read-event)))
(setq action (lookup-key query-replace-map key)))
(message
"According to VC, you can't edit this file. Skipping this line. (Hit RET)")
(setq key (vector (globrep-read-event)))
(setq action 'skip))
;; Answers that affect the main loop
(cond ((eq action 'automatic)
(setq continue 'automatic))
((or (eq action 'exit)
(eq action 'act-and-exit))
(setq continue 'exit)))
(cond ((or (eq action 'act)
(eq action 'act-and-exit)
(eq action 'act-and-show)
(eq continue 'automatic))
(unless replaced
(incf count)
(setq modified t)
(beginning-of-line) (setq b (point))
(end-of-line) (setq e (point))
(delete-region b e)
;; Go to the error message
(set-buffer (marker-buffer (car next-error)))
(goto-char (car next-error))
(end-of-line) (setq e (point))
(beginning-of-line)
;; If we're in win32, there might be an extra
;; colon after the drive letter, as in "drive:/path".
;; So we have to skip the colon if it's present.
(when (and (eq system-type 'windows-nt)
(looking-at "[a-zA-Z]:/"))
(forward-char 2))
(skip-chars-forward "^:" e) (forward-char)
(skip-chars-forward "^:" e) (forward-char)
(setq b (point))
(narrow-to-region b e)
(replace-string "\r" "\n")
(widen)
(setq replacement-line (buffer-substring b e))
(set-buffer buf)
(insert replacement-line)
(setq replaced t))
(setq done (not (eq action 'act-and-show))))
((eq action 'skip)
(setq done t)) ;don't replace this occurrence
((eq action 'exit)
(setq done t))
((eq action 'edit)
(save-excursion (recursive-edit)))
((eq action 'delete-and-edit)
(save-excursion
(beginning-of-line)
(setq b (point))
(end-of-line)
(delete-region b (point))
(recursive-edit))
(setq done t))
((eq action 'recenter)
(recenter nil))
((eq action 'help)
(with-output-to-temp-buffer "*Help*"
(princ
(concat "Globally replacing lines.\n\n"
(substitute-command-keys
query-replace-help)))))
((eq action 'backup)
(message "It is not possible to backup in global-replace.")
(sit-for 1))
(t
(setq continue 'exit)
(and globrep-xemacs
(setq unread-command-events
(append (character-to-event key)
unread-command-events)))
(setq done t))
)
)
)
)
)
(if (not (interactive-p))
(list count loops modified-files)
(if (and (> count 0)
(globrep-confirm globrep-chglog-prompt))
(g-r-format-changelog-entry modified-files))
(message "%d replacements (of %d) done." count loops))
)
)
(defun g-r-format-vc-comment (&optional text)
"Prepare a comment that will be used in VC log entries. Return it as a string.
Optional argument is the description of the change."
(let ((buffer (get-buffer-create "*GR-log*"))
comment)
(pop-to-buffer buffer)
(setq mode-name (substitute-command-keys
"Type \\[exit-recursive-edit] when done"))
(erase-buffer)
(if text (insert text))
(message "Enter VC comment describing this global-replace.")
(recursive-edit)
(set-buffer buffer)
(setq comment (buffer-substring (point-min) (point-max)))
(kill-buffer buffer)
comment))
(defun g-r-format-changelog-entry (files &optional text)
"Format and present to the user a ChangeLog entry after a global-replace.
First argument is the list of files where the change occurred.
Second optional argument is the description of the change."
(add-change-log-entry)
(beginning-of-line)
(skip-chars-forward " \t")
(delete-region (point) (progn (end-of-line) (point)))
(insert "* ")
(let ((logfile-dir
(concat "^" (regexp-quote (file-name-directory (buffer-file-name))))))
(dolist (file files)
(setq file (if (string-match logfile-dir (file-name-directory file))
(substring file (match-end 0))
file))
(insert " " file)))
(change-log-fill-paragraph nil)
(insert ": ")
(when text (insert text)
(change-log-fill-paragraph nil)))
(defun global-replace (string replacement &optional rescan)
"From a grep output buffer, query replace STRING by REPLACEMENT.
Prefix argument or optional RESCAN forces rescanning of the *compilation*
buffer. See also global-replace-lines for a more flexible approach."
(interactive "sGlobal replace: \nsGlobal replace %s by: \nP")
(set-buffer (compilation-find-buffer))
(if (not (string-match "grep" mode-name))
(error "The last compilation was not a grep!"))
(goto-char (point-min))
(setq buffer-read-only nil)
(replace-string string replacement)
(let ((ret (global-replace-lines rescan)))
(if (not (interactive-p))
ret
(if (globrep-confirm globrep-chglog-prompt)
(g-r-format-changelog-entry
(nth 2 ret)
(format "Replaced `%s' by `%s'." string replacement)))
(message "%d replacements (of %d) done." (nth 0 ret) (nth 1 ret)))))
(defun global-grep-and-replace (string replacement files grp-cmd)
"Query replace STRING by REPLACEMENT in FILES, using GRP-CMD to find STRING.
global-replace-lines is used to perform the actual replacement.
Before that, however, the user is given a chance to edit the grep output."
(interactive
(let* ((s (read-string "Global replace: "))
(r (read-string (concat "Global replace " s " by: ")))
(f (read-string (concat "Searching for " s " in files: ")))
(l (read-string "grep command: " grep-command)))
(list s r f l)))
(grep (concat grp-cmd " " string " " files))
(let* ((status 'run)
(compilation-buffer (compilation-find-buffer))
(compilation-process (get-buffer-process compilation-buffer))
action)
(while (eq status 'run)
(message "running...") (sit-for 0)
(sleep-for 1)
(message "") (sit-for 0)
(sleep-for 1)
(setq status (process-status compilation-process)))
(if (not (eq status 'exit))
(error "Grep process exited abnormally"))
(setq action
(query-string "Do you want to"
'("replace" "edit search" "quit")))
(cond ((string= action "replace")
(message "On with the replace!")
(sit-for 0)
(set-buffer compilation-buffer)
(goto-char (point-min))
(setq buffer-read-only nil)
(replace-string string replacement))
((string= action "edit search")
(message "Entering recursive edit, exit with C-M-c, abort with C-]")
(recursive-edit))
((string= action "quit")
(error "Aborted."))
)
)
(let ((ret (global-replace-lines)))
(if (not (interactive-p))
ret
(if (globrep-confirm globrep-chglog-prompt)
(g-r-format-changelog-entry
(nth 2 ret)
(format "Replaced `%s' by `%s'." string replacement)))
(message "%d replacements (of %d) done." (nth 0 ret) (nth 1 ret)))))
               (
geocities.com/kensanata)