summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/emacs-lisp')
-rw-r--r--lisp/emacs-lisp/authors.el269
1 files changed, 269 insertions, 0 deletions
diff --git a/lisp/emacs-lisp/authors.el b/lisp/emacs-lisp/authors.el
new file mode 100644
index 00000000000..49a111657fe
--- /dev/null
+++ b/lisp/emacs-lisp/authors.el
@@ -0,0 +1,269 @@
+;;; authors.el --- utility for maintaining Emacs' AUTHORS file
+
+;; Copyright (C) 2000 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Use M-x authors RET to create an *Authors* buffer that can used as
+;; or merged with Emacs' AUTHORS file.
+
+;;; Code:
+
+(defconst authors-many-files 20
+ "Maximum number of files for which to print individual information.
+If an author has modified more files, only a single entry is
+printed telling how many files he changed, instead of listing each
+file individually.")
+
+(defconst authors-aliases
+ '(("eliz" . "Eli Zaretskii")
+ ("Richard Stallman" . "Richard M. Stallman")
+ ("Richard M. Stallman,,," . "Richard M. Stallman")
+ ("Richard Stallmao" . "Richard M. Stallman")
+ ("rms@gnu.org" . "Richard M. Stallman")
+ ("NIIBE Yutaka" . "Yutaka NIIBE")
+ ("(saw@cebaf.gov)" . "Stephen A. Wood")
+ ("(pmr@legacy.pajato.com)" . "Paul Reilly")
+ ("(Eric Youngdale at youngdale@v6550c.nrl.navy.mil)" . "Eric Youngdale")
+ ("<Daniel.Pfeiffer@Informatik.START.db.de>" . "Daniel Pfeiffer")
+ ("<Daniel.Pfeiffer@Informatik.START.dbp.de>" . "Daniel Pfeiffer")
+ ("(afs@hplb.hpl.hp.com)" . "ignore")
+ ("<Use-Author-Address-Header@\\[127.1\\]>" . "ignore")
+ ("Code Extracted" . "ignore")
+ ("Fsf" . "ignore")
+ ("David M. Koppelman, Koppel@Ee.Lsu.Edu" . "David M. Koppelman")
+ ("jka@ece.cmu.edu" . "Jay K. Adams")
+ ("Per Abhiddenware; you can redistribute it and/or modify" . "Per Abrahamsen")
+ ("Andrw Innes" . "Andrew Innes")
+ ("Barry Warsaw" . "Barry A. Warsaw")
+ ("Barry A. Warsaw, Century Computing, Inc." . "Barry A. Warsaw")
+ ("Barry A. Warsaw, ITB" . "Barry A. Warsaw")
+ ("Ken'ichi Handa" . "Kenichi Handa")
+ ("Bob Chassell" . "Robert J. Chassell")
+ ("SL Baur" . "Steven L. Baur")
+ ("Steven L Baur" . "Steven L. Baur")
+ ("eggert" . "Paul Eggert")
+ ("voelker" . "Geoff Voelker")
+ ("rms" . "Richard M. Stallman")
+ ("Edward M Reingold" . "Edward M. Reingold")
+ ("Eric Ludlam" . "Eric M. Ludlam")
+ ("Eric Raymond" . "Eric S. Raymond")
+ ("Francois Pinard" . "François Pinard")
+ ("Fred Pierresteguy" . "Frederic Pierresteguy")
+ ("Hallvard B Furuseth" . "Hallvard B. Furuseth")
+ ("ISO-2022-JP" . "ignore")
+ ("Jens-Ulrik Petersen" . "Jens-Ulrik Holger Petersen")
+ ("Christoph.Wedler@sap.com" . "Christoph Wedler")
+ ("Jonathan Kamens" . "Jonathan I. Kamens")
+ ("Kim Storm" . "Kim F. Storm")
+ ("Marcus Daniels" . "Marcus G. Daniels")
+ ("Michael I Bushnell" . "Michael I. Bushnell")
+ ("Michael I. Bushnell, P/Bsg" . "Michael I. Bushnell")
+ ("Reingold Edward M" . "Edward M. Reingold")
+ ("Roland B Roberts" . "Roland B. Roberts")
+ ("Sam Shteingold" . "Sam Steingold")
+ ("Kenichi HANDA" . "Kenichi Handa"))
+ "Alist of author aliases.
+
+Each entry is of the form (REGEXP . ALIAS). If an author's name
+matches REGEXP, use ALIAS instead. The special alias \"ignore\" means
+ignore that author.")
+
+
+(defun authors-add (author file action table)
+ "Record that AUTHOR worked on FILE.
+ACTION is a keyword symbol describing what he did. Record file,
+author and what he did in hash table TABLE. See the description of
+`authors-scan-change-log' for the structure of the hash table."
+ (let* ((value (gethash author table))
+ (entry (assoc file value)))
+ (if (null entry)
+ (puthash author (cons (list file action) value) table)
+ (unless (memq action entry)
+ (append entry (list action))))))
+
+
+(defun authors-process-lines (program &rest args)
+ "Execute PROGRAM with ARGS, returning its output as a list of lines.
+Signal an error if the program returns with a non-zero exit status."
+ (with-temp-buffer
+ (let ((status (apply 'call-process program nil (current-buffer) nil args)))
+ (unless (eq status 0)
+ (error "%s exited with status %s" program status))
+ (goto-char (point-min))
+ (let (lines)
+ (while (not (eobp))
+ (setq lines (cons (buffer-substring-no-properties
+ (line-beginning-position)
+ (line-end-position))
+ lines))
+ (forward-line 1))
+ (nreverse lines)))))
+
+
+(defun authors-canonical-author-name (author)
+ "Return a canonicalized form of AUTHOR, an author name.
+If AUTHOR has an alias, use that. Remove email addresses. Capitalize
+words in the author's name."
+ (let ((aliases authors-aliases))
+ (while aliases
+ (when (string-match (car (car aliases)) author)
+ (setq author (cdr (car aliases))
+ aliases nil))
+ (setq aliases (cdr aliases))))
+ (setq author (replace-regexp-in-string "[ \t]*[(<].*$" "" author))
+ (setq author (replace-regexp-in-string "^[ \t]+" "" author))
+ (setq author (replace-regexp-in-string "[ \t]+$" "" author))
+ (capitalize author))
+
+
+(defun authors-scan-change-log (file table)
+ "Scan change log FILE for author information.
+
+For each change mentioned in the log, add an entry to hash table TABLE
+under the author's canonical name.
+
+Keys of TABLE are author names. Values are alists of entries (FILE
+ACTION...). FILE is one file the author worked on. The rest of the
+entry is a list of keyword symbols describing what he did with the
+file.
+
+:wrote means the author wrote the file
+:changed means he changed the file."
+
+ (let* ((enable-local-variables t)
+ (enable-local-eval t)
+ (existing-buffer (get-file-buffer file))
+ (buffer (find-file-noselect file))
+ author)
+ (save-excursion
+ (set-buffer buffer)
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (while (re-search-forward "^[0-9]\\|^[ \t]+\\* " nil t)
+ (beginning-of-line)
+ (cond ((looking-at "^[0-9]+-[0-9]+-[0-9]+")
+ (skip-chars-forward " \t+:0-9-")
+ (setq author (buffer-substring-no-properties
+ (point) (line-end-position)))
+ (setq author (authors-canonical-author-name author))
+ (forward-line 1))
+ ((looking-at "^[ \t]+\\*")
+ (let ((line (buffer-substring-no-properties
+ (match-end 0) (line-end-position))))
+ (while (and (not (string-match ":" line))
+ (forward-line 1)
+ (not (looking-at ":\\|^[ \t]*$")))
+ (setq line (concat line
+ (buffer-substring-no-properties
+ (line-beginning-position)
+ (line-end-position)))))
+ (when (string-match ":" line)
+ (setq line (substring line 0 (match-beginning 0)))
+ (setq line (replace-regexp-in-string "[[(<{].*$" "" line))
+ (setq line (replace-regexp-in-string "," "" line))
+ (dolist (file (split-string line))
+ (setq file (file-name-nondirectory file))
+ ;(message "%s changed %s" author file)
+ (authors-add author file :changed table)))
+ (forward-line 1)))))))
+ (unless existing-buffer
+ (kill-buffer buffer))))
+
+
+(defun authors-scan-el (file table)
+ "Scan Lisp file FILE for author information.
+TABLE is a hash table to add author information to."
+ (let* ((existing-buffer (get-file-buffer file))
+ (enable-local-variables t)
+ (enable-local-eval t)
+ (buffer (find-file-noselect file)))
+ (save-excursion
+ (set-buffer buffer)
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (while (and (re-search-forward
+ "^;+[ \t]*\\(Author\\|Commentary\\):[ \t]*" nil t)
+ (not (string= (match-string 1) "Commentary")))
+ ;; Some entries contain a year range in front of the
+ ;; author's name.
+ (skip-chars-forward "-0-9 \t")
+ (let ((author (buffer-substring-no-properties
+ (point) (line-end-position))))
+ (setq author (authors-canonical-author-name author))
+ (setq file (file-name-nondirectory file))
+ (authors-add author file :wrote table)))))
+ (unless existing-buffer
+ (kill-buffer buffer))))
+
+
+(defun authors-print (author changes)
+ "Insert information about AUTHOR's work on Emacs into the current buffer.
+CHANGES is an alist of entries (FILE ACTION...), as produced by
+`authors-scan-change-log'."
+ (unless (equal author "Ignore")
+ (let ((nchanged 0))
+ (dolist (change changes)
+ (let ((actions (cdr change))
+ (file (car change)))
+ (if (memq :wrote actions)
+ (insert author " (wrote) " file "\n")
+ (setq nchanged (1+ nchanged)))))
+ (if (> nchanged authors-many-files)
+ (insert author " (changed) [changes in more than "
+ (int-to-string authors-many-files) " files]\n")
+ (dolist (change changes)
+ (let ((actions (cdr change))
+ (file (car change)))
+ (unless (memq :wrote actions)
+ (insert author " (changed) " file "\n"))))))))
+
+
+;;;###autoload
+(defun authors (root)
+ "Extract author information from change logs and Lisp source files.
+ROOT is the root directory under which to find the files. If called
+interactively, ROOT is read from the minibuffer. Result is a
+buffer *Authors* containing authorship information."
+ (interactive "DEmacs source directory: ")
+ (let ((logs (authors-process-lines "find" root "-name" "ChangeLog*"))
+ (table (make-hash-table :test 'equal))
+ (buffer-name "*Authors*"))
+ (setq root (expand-file-name root))
+ (unless (file-exists-p (expand-file-name "src/emacs.c" root))
+ (error "Not the root directory of Emacs: %s" root))
+ (dolist (log logs)
+ (when (string-match "ChangeLog\\(.[0-9]+\\)?$" log)
+ (message "Scanning %s..." log)
+ (authors-scan-change-log log table)))
+ (let ((els (authors-process-lines "find" root "-name" "*.el")))
+ (dolist (file els)
+ (message "Scanning %s..." file)
+ (authors-scan-el file table)))
+ (set-buffer (get-buffer-create buffer-name))
+ (erase-buffer)
+ (maphash #'authors-print table)
+ (sort-lines nil (point-min) (point-max))
+ (pop-to-buffer buffer-name)))
+
+
+;; authors.el ends here