summaryrefslogtreecommitdiff
path: root/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp')
-rw-r--r--lisp/case-table.el101
-rw-r--r--lisp/disp-table.el115
-rw-r--r--lisp/ehelp.el338
-rw-r--r--lisp/emacs-lisp/helper.el147
-rw-r--r--lisp/emulation/mlconvert.el272
-rw-r--r--lisp/float-sup.el53
-rw-r--r--lisp/gosmacs.el102
-rw-r--r--lisp/hexl.el668
-rw-r--r--lisp/ledit.el138
-rw-r--r--lisp/macros.el103
-rw-r--r--lisp/mail/emacsbug.el38
-rw-r--r--lisp/mail/mail-utils.el195
-rw-r--r--lisp/mail/rmailedit.el105
-rw-r--r--lisp/mail/rmailkwd.el260
-rw-r--r--lisp/makesum.el100
-rw-r--r--lisp/novice.el105
-rw-r--r--lisp/play/dissociate.el87
-rw-r--r--lisp/play/gomoku.el1166
-rw-r--r--lisp/play/spook.el109
-rw-r--r--lisp/progmodes/icon.el550
-rw-r--r--lisp/rect.el205
-rw-r--r--lisp/tabify.el51
-rw-r--r--lisp/textmodes/nroff-mode.el203
-rw-r--r--lisp/textmodes/page.el123
-rw-r--r--lisp/textmodes/paragraphs.el205
-rw-r--r--lisp/textmodes/refbib.el715
-rw-r--r--lisp/textmodes/spell.el132
-rw-r--r--lisp/textmodes/text-mode.el147
-rw-r--r--lisp/textmodes/underline.el46
-rw-r--r--lisp/userlock.el124
-rw-r--r--lisp/vms-patch.el99
-rw-r--r--lisp/window.el98
32 files changed, 6900 insertions, 0 deletions
diff --git a/lisp/case-table.el b/lisp/case-table.el
new file mode 100644
index 00000000000..f10580fe575
--- /dev/null
+++ b/lisp/case-table.el
@@ -0,0 +1,101 @@
+;; Functions for extending the character set and dealing with case tables.
+;; Copyright (C) 1988 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;; Written by:
+;; TN/ETX/TX/UMG Howard Gayle UUCP : seismo!enea!erix!howard
+;; Telefonaktiebolaget L M Ericsson Phone: +46 8 719 55 65
+;; Ericsson Telecom Telex: 14910 ERIC S
+;; S-126 25 Stockholm FAX : +46 8 719 64 82
+;; Sweden
+
+(defun describe-buffer-case-table ()
+ "Describe the case table of the current buffer."
+ (interactive)
+ (let ((vector (make-vector 256 nil))
+ (case-table (current-case-table))
+ (i 0))
+ (while (< i 256)
+ (aset vector i
+ (cond ((/= ch (downcase ch))
+ (concat "uppercase, matches "
+ (text-char-description (downcase ch))))
+ ((/= ch (upcase ch))
+ (concat "lowercase, matches "
+ (text-char-description (upcase ch))))
+ (t "case-invariant")))
+ (setq i (1+ i))))
+ (with-output-to-temp-buffer "*Help*"
+ (describe-vector vector)))
+
+(defun invert-case (count)
+ "Change the case of the character just after point and move over it.
+With arg, applies to that many chars.
+Negative arg inverts characters before point but does not move."
+ (interactive "p")
+ (if (< count 0)
+ (progn (setq count (min (1- (point)) (- count)))
+ (forward-char (- count))))
+ (while (> count 0)
+ (let ((oc (following-char))) ; Old character.
+ (cond ((/= (upcase ch) ch)
+ (replace-char (upcase ch)))
+ ((/= (downcase ch) ch)
+ (replace-char (downcase ch)))))
+ (forward-char 1)
+ (setq count (1- count))))
+
+(defun set-case-syntax-delims (l r table)
+ "Make characters L and R a matching pair of non-case-converting delimiters.
+Sets the entries for L and R in standard-case-table,
+standard-syntax-table, and text-mode-syntax-table to indicate
+left and right delimiters."
+ (aset (car table) l l)
+ (aset (car table) r r)
+ (modify-syntax-entry l (concat "(" (char-to-string r) " ")
+ (standard-syntax-table))
+ (modify-syntax-entry l (concat "(" (char-to-string r) " ")
+ text-mode-syntax-table)
+ (modify-syntax-entry r (concat ")" (char-to-string l) " ")
+ (standard-syntax-table))
+ (modify-syntax-entry r (concat ")" (char-to-string l) " ")
+ text-mode-syntax-table))
+
+(defun set-case-syntax-pair (uc lc table)
+ "Make characters UC and LC a pair of inter-case-converting letters.
+Sets the entries for characters UC and LC in
+standard-case-table, standard-syntax-table, and
+text-mode-syntax-table to indicate an (uppercase, lowercase)
+pair of letters."
+ (aset (car table) uc lc)
+ (modify-syntax-entry lc "w " (standard-syntax-table))
+ (modify-syntax-entry lc "w " text-mode-syntax-table)
+ (modify-syntax-entry uc "w " (standard-syntax-table))
+ (modify-syntax-entry uc "w " text-mode-syntax-table))
+
+(defun set-case-syntax (c syntax table)
+ "Make characters C case-invariant with syntax SYNTAX.
+Sets the entries for character C in standard-case-table,
+standard-syntax-table, and text-mode-syntax-table to indicate this.
+SYNTAX should be \" \", \"w\", \".\" or \"_\"."
+ (aset (car table) c c)
+ (modify-syntax-entry c syntax (standard-syntax-table))
+ (modify-syntax-entry c syntax text-mode-syntax-table))
+
+(provide 'case-table)
diff --git a/lisp/disp-table.el b/lisp/disp-table.el
new file mode 100644
index 00000000000..c0fe4dfe8af
--- /dev/null
+++ b/lisp/disp-table.el
@@ -0,0 +1,115 @@
+;; Functions for dealing with char tables.
+;; Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;; Written by Howard Gayle. See case-table.el for details.
+
+(require 'case-table)
+
+(defun rope-to-vector (rope)
+ (let* ((len (/ (length rope) 2))
+ (vector (make-vector len nil))
+ (i 0))
+ (while (< i len)
+ (aset vector i (rope-elt rope i))
+ (setq i (1+ i)))))
+
+(defun describe-display-table (DT)
+ "Describe the display-table DT in a help buffer."
+ (with-output-to-temp-buffer "*Help*"
+ (princ "\nTruncation glyf: ")
+ (prin1 (aref dt 256))
+ (princ "\nWrap glyf: ")
+ (prin1 (aref dt 257))
+ (princ "\nEscape glyf: ")
+ (prin1 (aref dt 258))
+ (princ "\nCtrl glyf: ")
+ (prin1 (aref dt 259))
+ (princ "\nSelective display rope: ")
+ (prin1 (rope-to-vector (aref dt 260)))
+ (princ "\nCharacter display ropes:\n")
+ (let ((vector (make-vector 256 nil))
+ (i 0))
+ (while (< i 256)
+ (aset vector i
+ (if (stringp (aref dt i))
+ (rope-to-vector (aref dt i))
+ (aref dt i)))
+ (setq i (1+ i)))
+ (describe-vector vector))
+ (print-help-return-message)))
+
+(defun describe-current-display-table ()
+ "Describe the display-table in use in the selected window and buffer."
+ (interactive)
+ (describe-display-table
+ (or (window-display-table (selected-window))
+ buffer-display-table
+ standard-display-table)))
+
+(defun make-display-table ()
+ (make-vector 261 nil))
+
+(defun standard-display-8bit (l h)
+ "Display characters in the range [L, H] literally."
+ (while (<= l h)
+ (if (and (>= l ?\ ) (< l 127))
+ (if standard-display-table (aset standard-display-table l nil))
+ (or standard-display-table
+ (setq standard-display-table (make-vector 261 nil)))
+ (aset standard-display-table l l))
+ (setq l (1+ l))))
+
+(defun standard-display-ascii (c s)
+ "Display character C using string S."
+ (or standard-display-table
+ (setq standard-display-table (make-vector 261 nil)))
+ (aset standard-display-table c (apply 'make-rope (append s nil))))
+
+(defun standard-display-g1 (c sc)
+ "Display character C as character SC in the g1 character set."
+ (or standard-display-table
+ (setq standard-display-table (make-vector 261 nil)))
+ (aset standard-display-table c
+ (make-rope (create-glyf (concat "\016" (char-to-string sc) "\017")))))
+
+(defun standard-display-graphic (c gc)
+ "Display character C as character GC in graphics character set."
+ (or standard-display-table
+ (setq standard-display-table (make-vector 261 nil)))
+ (aset standard-display-table c
+ (make-rope (create-glyf (concat "\e(0" (char-to-string gc) "\e(B")))))
+
+(defun standard-display-underline (c uc)
+ "Display character C as character UC plus underlining."
+ (or standard-display-table
+ (setq standard-display-table (make-vector 261 nil)))
+ (aset standard-display-table c
+ (make-rope (create-glyf (concat "\e[4m" (char-to-string uc) "\e[m")))))
+
+(defun create-glyf (string)
+ (let ((i 256))
+ (while (and (< i 65536) (aref glyf-table i)
+ (not (string= (aref glyf-table i) string)))
+ (setq i (1+ i)))
+ (if (= i 65536)
+ (error "No free glyf codes remain"))
+ (aset glyf-table i string)))
+
+(provide 'disp-table)
diff --git a/lisp/ehelp.el b/lisp/ehelp.el
new file mode 100644
index 00000000000..48c6c5b1692
--- /dev/null
+++ b/lisp/ehelp.el
@@ -0,0 +1,338 @@
+;; Copyright (C) 1986 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+(require 'electric)
+(provide 'ehelp)
+
+(defvar electric-help-map ()
+ "Keymap defining commands available whilst scrolling
+through a buffer in electric-help-mode")
+
+(put 'electric-help-undefined 'suppress-keymap t)
+(if electric-help-map
+ ()
+ (let ((map (make-keymap)))
+ (fillarray map 'electric-help-undefined)
+ (define-key map (char-to-string meta-prefix-char) (copy-keymap map))
+ (define-key map (char-to-string help-char) 'electric-help-help)
+ (define-key map "?" 'electric-help-help)
+ (define-key map " " 'scroll-up)
+ (define-key map "\^?" 'scroll-down)
+ (define-key map "." 'beginning-of-buffer)
+ (define-key map "<" 'beginning-of-buffer)
+ (define-key map ">" 'end-of-buffer)
+ ;(define-key map "\C-g" 'electric-help-exit)
+ (define-key map "q" 'electric-help-exit)
+ (define-key map "Q" 'electric-help-exit)
+ ;;a better key than this?
+ (define-key map "r" 'electric-help-retain)
+
+ (setq electric-help-map map)))
+
+(defun electric-help-mode ()
+ "with-electric-help temporarily places its buffer in this mode
+\(On exit from with-electric-help, the buffer is put in default-major-mode)"
+ (setq buffer-read-only t)
+ (setq mode-name "Help")
+ (setq major-mode 'help)
+ (setq mode-line-buffer-identification '(" Help: %b"))
+ (use-local-map electric-help-map)
+ ;; this is done below in with-electric-help
+ ;(run-hooks 'electric-help-mode-hook)
+ )
+
+(defun with-electric-help (thunk &optional buffer noerase)
+ "Arguments are THUNK &optional BUFFER NOERASE.
+BUFFER defaults to \"*Help*\"
+THUNK is a function of no arguments which is called to initialise
+ the contents of BUFFER. BUFFER will be erased before THUNK is called unless
+ NOERASE is non-nil. THUNK will be called with standard-output bound to
+ the buffer specified by BUFFER
+
+After THUNK has been called, this function \"electrically\" pops up a window
+in which BUFFER is displayed and allows the user to scroll through that buffer
+in electric-help-mode.
+When the user exits (with electric-help-exit, or otherwise) the help
+buffer's window disappears (ie we use save-window-excursion)
+BUFFER is put into default-major-mode (or fundamental-mode) when we exit"
+ (setq buffer (get-buffer-create (or buffer "*Help*")))
+ (let ((one (one-window-p t))
+ (two nil))
+ (save-window-excursion
+ (save-excursion
+ (if one (goto-char (window-start (selected-window))))
+ (let ((pop-up-windows t))
+ (pop-to-buffer buffer))
+ (unwind-protect
+ (progn
+ (save-excursion
+ (set-buffer buffer)
+ (electric-help-mode)
+ (setq buffer-read-only nil)
+ (or noerase (erase-buffer)))
+ (let ((standard-output buffer))
+ (if (funcall thunk)
+ ()
+ (set-buffer buffer)
+ (set-buffer-modified-p nil)
+ (goto-char (point-min))
+ (if one (shrink-window-if-larger-than-buffer (selected-window)))))
+ (set-buffer buffer)
+ (run-hooks 'electric-help-mode-hook)
+ (setq two (electric-help-command-loop))
+ (cond ((eq (car-safe two) 'retain)
+ (setq two (vector (window-height (selected-window))
+ (window-start (selected-window))
+ (window-hscroll (selected-window))
+ (point))))
+ (t (setq two nil))))
+
+ (message "")
+ (set-buffer buffer)
+ (setq buffer-read-only nil)
+ (condition-case ()
+ (funcall (or default-major-mode 'fundamental-mode))
+ (error nil)))))
+ (if two
+ (let ((pop-up-windows t)
+ tem)
+ (pop-to-buffer buffer)
+ (setq tem (- (window-height (selected-window)) (elt two 0)))
+ (if (> tem 0) (shrink-window tem))
+ (set-window-start (selected-window) (elt two 1) t)
+ (set-window-hscroll (selected-window) (elt two 2))
+ (goto-char (elt two 3)))
+ ;;>> Perhaps this shouldn't be done.
+ ;; so that when we say "Press space to bury" we mean it
+ (replace-buffer-in-windows buffer)
+ ;; must do this outside of save-window-excursion
+ (bury-buffer buffer))))
+
+(defun electric-help-command-loop ()
+ (catch 'exit
+ (if (pos-visible-in-window-p (point-max))
+ (progn (message "<<< Press Space to bury the help buffer >>>")
+ (if (= (setq unread-command-char (read-char)) ?\ )
+ (progn (setq unread-command-char -1)
+ (throw 'exit t)))))
+ (let (up down both neither
+ (standard (and (eq (key-binding " ")
+ 'scroll-up)
+ (eq (key-binding "\^?")
+ 'scroll-down)
+ (eq (key-binding "Q")
+ 'electric-help-exit)
+ (eq (key-binding "q")
+ 'electric-help-exit))))
+ (Electric-command-loop
+ 'exit
+ (function (lambda ()
+ (let ((min (pos-visible-in-window-p (point-min)))
+ (max (pos-visible-in-window-p (point-max))))
+ (cond ((and min max)
+ (cond (standard "Press Q to exit ")
+ (neither)
+ (t (setq neither (substitute-command-keys "Press \\[scroll-up] to exit ")))))
+ (min
+ (cond (standard "Press SPC to scroll, Q to exit ")
+ (up)
+ (t (setq up (substitute-command-keys "Press \\[scroll-up] to scroll; \\[electric-help-exit] to exit ")))))
+ (max
+ (cond (standard "Press DEL to scroll back, Q to exit ")
+ (down)
+ (t (setq down (substitute-command-keys "Press \\[scroll-down] to scroll back, \\[scroll-up] to exit ")))))
+ (t
+ (cond (standard "Press SPC to scroll, DEL to scroll back, Q to exit ")
+ (both)
+ (t (setq both (substitute-command-keys "Press \\[scroll-up] to scroll, \\[scroll-down] to scroll back, \\[electric-help-exit] to exit ")))))))))
+ t))))
+
+
+
+;(defun electric-help-scroll-up (arg)
+; ">>>Doc"
+; (interactive "P")
+; (if (and (null arg) (pos-visible-in-window-p (point-max)))
+; (electric-help-exit)
+; (scroll-up arg)))
+
+(defun electric-help-exit ()
+ ">>>Doc"
+ (interactive)
+ (throw 'exit t))
+
+(defun electric-help-retain ()
+ "Exit electric-help, retaining the current window/buffer conifiguration.
+\(The *Help* buffer will not be selected, but \\[switch-to-buffer-other-window] RET
+will select it.)"
+ (interactive)
+ (throw 'exit '(retain)))
+
+
+;(defun electric-help-undefined ()
+; (interactive)
+; (let* ((keys (this-command-keys))
+; (n (length keys)))
+; (if (or (= n 1)
+; (and (= n 2)
+; meta-flag
+; (eq (aref keys 0) meta-prefix-char)))
+; (setq unread-command-char last-input-char
+; current-prefix-arg prefix-arg)
+; ;;>>> I don't care.
+; ;;>>> The emacs command-loop is too much pure pain to
+; ;;>>> duplicate
+; ))
+; (throw 'exit t))
+
+(defun electric-help-undefined ()
+ (interactive)
+ (error "%s is undefined -- Press %s to exit"
+ (mapconcat 'single-key-description (this-command-keys) " ")
+ (if (eq (key-binding "Q") 'electric-help-exit)
+ "Q"
+ (substitute-command-keys "\\[electric-help-exit]"))))
+
+
+;>>> this needs to be hairified (recursive help, anybody?)
+(defun electric-help-help ()
+ (interactive)
+ (if (and (eq (key-binding "Q") 'electric-help-exit)
+ (eq (key-binding " ") 'scroll-up)
+ (eq (key-binding "\^?") 'scroll-down))
+ (message "SPC scrolls forward, DEL scrolls back, Q exits and burys help buffer")
+ ;; to give something for user to look at while slow substitute-cmd-keys
+ ;; grinds away
+ (message "Help...")
+ (message "%s" (substitute-command-keys "\\[scroll-up] scrolls forward, \\[scroll-down] scrolls back, \\[electric-help-exit] exits.")))
+ (sit-for 2))
+
+
+(defun electric-helpify (fun)
+ (let ((name "*Help*"))
+ (if (save-window-excursion
+ ;; kludge-o-rama
+ (let* ((p (symbol-function 'print-help-return-message))
+ (b (get-buffer name))
+ (m (buffer-modified-p b)))
+ (and b (not (get-buffer-window b))
+ (setq b nil))
+ (unwind-protect
+ (progn
+ (message "%s..." (capitalize (symbol-name fun)))
+ ;; with-output-to-temp-buffer marks the buffer as unmodified.
+ ;; kludging excessively and relying on that as some sort
+ ;; of indication leads to the following abomination...
+ ;;>> This would be doable without such icky kludges if either
+ ;;>> (a) there were a function to read the interactive
+ ;;>> args for a command and return a list of those args.
+ ;;>> (To which one would then just apply the command)
+ ;;>> (The only problem with this is that interactive-p
+ ;;>> would break, but that is such a misfeature in
+ ;;>> any case that I don't care)
+ ;;>> It is easy to do this for emacs-lisp functions;
+ ;;>> the only problem is getting the interactive spec
+ ;;>> for subrs
+ ;;>> (b) there were a function which returned a
+ ;;>> modification-tick for a buffer. One could tell
+ ;;>> whether a buffer had changed by whether the
+ ;;>> modification-tick were different.
+ ;;>> (Presumably there would have to be a way to either
+ ;;>> restore the tick to some previous value, or to
+ ;;>> suspend updating of the tick in order to allow
+ ;;>> things like momentary-string-display)
+ (and b
+ (save-excursion
+ (set-buffer b)
+ (set-buffer-modified-p t)))
+ (fset 'print-help-return-message 'ignore)
+ (call-interactively fun)
+ (and (get-buffer name)
+ (get-buffer-window (get-buffer name))
+ (or (not b)
+ (not (eq b (get-buffer name)))
+ (not (buffer-modified-p b)))))
+ (fset 'print-help-return-message p)
+ (and b (buffer-name b)
+ (save-excursion
+ (set-buffer b)
+ (set-buffer-modified-p m))))))
+ (with-electric-help 'ignore name t))))
+
+
+(defun electric-describe-key ()
+ (interactive)
+ (electric-helpify 'describe-key))
+
+(defun electric-describe-mode ()
+ (interactive)
+ (electric-helpify 'describe-mode))
+
+(defun electric-view-lossage ()
+ (interactive)
+ (electric-helpify 'view-lossage))
+
+;(defun electric-help-for-help ()
+; "See help-for-help"
+; (interactive)
+; )
+
+(defun electric-describe-function ()
+ (interactive)
+ (electric-helpify 'describe-function))
+
+(defun electric-describe-variable ()
+ (interactive)
+ (electric-helpify 'describe-variable))
+
+(defun electric-describe-bindings ()
+ (interactive)
+ (electric-helpify 'describe-bindings))
+
+(defun electric-describe-syntax ()
+ (interactive)
+ (electric-helpify 'describe-syntax))
+
+(defun electric-command-apropos ()
+ (interactive)
+ (electric-helpify 'command-apropos))
+
+;(define-key help-map "a" 'electric-command-apropos)
+
+
+
+
+;;;; ehelp-map
+
+(defvar ehelp-map ())
+(if ehelp-map
+ nil
+ (let ((map (copy-keymap help-map)))
+ (substitute-key-definition 'describe-key 'electric-describe-key map)
+ (substitute-key-definition 'describe-mode 'electric-describe-mode map)
+ (substitute-key-definition 'view-lossage 'electric-view-lossage map)
+ (substitute-key-definition 'describe-function 'electric-describe-function map)
+ (substitute-key-definition 'describe-variable 'electric-describe-variable map)
+ (substitute-key-definition 'describe-bindings 'electric-describe-bindings map)
+ (substitute-key-definition 'describe-syntax 'electric-describe-syntax map)
+
+ (setq ehelp-map map)
+ (fset 'ehelp-command map)))
+
+;; Do (define-key global-map "\C-h" 'ehelp-command) if you want to win
+
diff --git a/lisp/emacs-lisp/helper.el b/lisp/emacs-lisp/helper.el
new file mode 100644
index 00000000000..aa7253eab6c
--- /dev/null
+++ b/lisp/emacs-lisp/helper.el
@@ -0,0 +1,147 @@
+;; helper - utility help package for modes which want to provide help
+;; without relinquishing control, e.g. `electric' modes.
+
+;; Copyright (C) 1985 Free Software Foundation, Inc.
+;; Principal author K. Shane Hartman
+
+;; 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(provide 'helper) ; hey, here's a helping hand.
+
+;; Bind this to a string for <blank> in "... Other keys <blank>".
+;; Helper-help uses this to construct help string when scrolling.
+;; Defaults to "return"
+(defvar Helper-return-blurb nil)
+
+;; Keymap implementation doesn't work too well for non-standard loops.
+;; But define it anyway for those who can use it. Non-standard loops
+;; will probably have to use Helper-help. You can't autoload the
+;; keymap either.
+
+
+(defvar Helper-help-map nil)
+(if Helper-help-map
+ nil
+ (setq Helper-help-map (make-keymap))
+ ;(fillarray Helper-help-map 'undefined)
+ (define-key Helper-help-map "m" 'Helper-describe-mode)
+ (define-key Helper-help-map "b" 'Helper-describe-bindings)
+ (define-key Helper-help-map "c" 'Helper-describe-key-briefly)
+ (define-key Helper-help-map "k" 'Helper-describe-key)
+ ;(define-key Helper-help-map "f" 'Helper-describe-function)
+ ;(define-key Helper-help-map "v" 'Helper-describe-variable)
+ (define-key Helper-help-map "?" 'Helper-help-options)
+ (define-key Helper-help-map (char-to-string help-char) 'Helper-help-options)
+ (fset 'Helper-help-map Helper-help-map))
+
+(defun Helper-help-scroller ()
+ (let ((blurb (or (and (boundp 'Helper-return-blurb)
+ Helper-return-blurb)
+ "return")))
+ (save-window-excursion
+ (goto-char (window-start (selected-window)))
+ (if (get-buffer-window "*Help*")
+ (pop-to-buffer "*Help*")
+ (switch-to-buffer "*Help*"))
+ (goto-char (point-min))
+ (let ((continue t) state)
+ (while continue
+ (setq state (+ (* 2 (if (pos-visible-in-window-p (point-max)) 1 0))
+ (if (pos-visible-in-window-p (point-min)) 1 0)))
+ (message
+ (nth state
+ '("Space forward, Delete back. Other keys %s"
+ "Space scrolls forward. Other keys %s"
+ "Delete scrolls back. Other keys %s"
+ "Type anything to %s"))
+ blurb)
+ (setq continue (read-char))
+ (cond ((and (memq continue '(?\ ?\C-v)) (< state 2))
+ (scroll-up))
+ ((= continue ?\C-l)
+ (recenter))
+ ((and (= continue ?\177) (zerop (% state 2)))
+ (scroll-down))
+ (t (setq continue nil))))))))
+
+(defun Helper-help-options ()
+ "Describe help options."
+ (interactive)
+ (message "c (key briefly), m (mode), k (key), b (bindings)")
+ ;(message "c (key briefly), m (mode), k (key), v (variable), f (function)")
+ (sit-for 4))
+
+(defun Helper-describe-key-briefly (key)
+ "Briefly describe binding of KEYS."
+ (interactive "kDescribe key briefly: ")
+ (describe-key-briefly key)
+ (sit-for 4))
+
+(defun Helper-describe-key (key)
+ "Describe binding of KEYS."
+ (interactive "kDescribe key: ")
+ (save-window-excursion (describe-key key))
+ (Helper-help-scroller))
+
+(defun Helper-describe-function ()
+ "Describe a function. Name read interactively."
+ (interactive)
+ (save-window-excursion (call-interactively 'describe-function))
+ (Helper-help-scroller))
+
+(defun Helper-describe-variable ()
+ "Describe a variable. Name read interactively."
+ (interactive)
+ (save-window-excursion (call-interactively 'describe-variable))
+ (Helper-help-scroller))
+
+(defun Helper-describe-mode ()
+ "Describe the current mode."
+ (interactive)
+ (let ((name mode-name)
+ (documentation (documentation major-mode)))
+ (save-excursion
+ (set-buffer (get-buffer-create "*Help*"))
+ (erase-buffer)
+ (insert name " Mode\n" documentation)))
+ (Helper-help-scroller))
+
+(defun Helper-describe-bindings ()
+ "Describe local key bindings of current mode."
+ (interactive)
+ (message "Making binding list...")
+ (save-window-excursion (describe-bindings))
+ (Helper-help-scroller))
+
+(defun Helper-help ()
+ "Provide help for current mode."
+ (interactive)
+ (let ((continue t) c)
+ (while continue
+ (message "Help (Type ? for further options)")
+ (setq c (char-to-string (downcase (read-char))))
+ (setq c (lookup-key Helper-help-map c))
+ (cond ((eq c 'Helper-help-options)
+ (Helper-help-options))
+ ((commandp c)
+ (call-interactively c)
+ (setq continue nil))
+ (t
+ (ding)
+ (setq continue nil))))))
+
diff --git a/lisp/emulation/mlconvert.el b/lisp/emulation/mlconvert.el
new file mode 100644
index 00000000000..faf88e5ab32
--- /dev/null
+++ b/lisp/emulation/mlconvert.el
@@ -0,0 +1,272 @@
+;; Convert buffer of Mocklisp code to real lisp.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+(defun convert-mocklisp-buffer ()
+ "Convert buffer of Mocklisp code to real Lisp that GNU Emacs can run."
+ (interactive)
+ (emacs-lisp-mode)
+ (set-syntax-table (copy-sequence (syntax-table)))
+ (modify-syntax-entry ?\| "w")
+ (message "Converting mocklisp (ugh!)...")
+ (goto-char (point-min))
+ (fix-mlisp-syntax)
+
+ ;; Emulation of mocklisp is accurate only within a mocklisp-function
+ ;; so turn any non-function into a defun and then call it.
+ (goto-char (point-min))
+ (condition-case ignore
+ (while t
+ (let ((opt (point))
+ (form (read (current-buffer))))
+ (and (listp form)
+ (not (eq (car form) 'defun))
+ (progn (insert "))\n\n(ml-foo)\n\n")
+ (save-excursion
+ (goto-char opt)
+ (skip-chars-forward "\n")
+ (insert "(defun (ml-foo \n "))))))
+ (end-of-file nil))
+
+ (goto-char (point-min))
+ (insert ";;; GNU Emacs code converted from Mocklisp\n")
+ (insert "(require 'mlsupport)\n\n")
+ (fix-mlisp-symbols)
+
+ (goto-char (point-min))
+ (message "Converting mocklisp...done"))
+
+(defun fix-mlisp-syntax ()
+ (while (re-search-forward "['\"]" nil t)
+ (if (= (preceding-char) ?\")
+ (progn (forward-char -1)
+ (forward-sexp 1))
+ (delete-char -1)
+ (insert "?")
+ (if (or (= (following-char) ?\\) (= (following-char) ?^))
+ (forward-char 1)
+ (if (looking-at "[^a-zA-Z]")
+ (insert ?\\)))
+ (forward-char 1)
+ (delete-char 1))))
+
+(defun fix-mlisp-symbols ()
+ (while (progn
+ (skip-chars-forward " \t\n()")
+ (not (eobp)))
+ (cond ((or (= (following-char) ?\?)
+ (= (following-char) ?\"))
+ (forward-sexp 1))
+ ((= (following-char) ?\;)
+ (forward-line 1))
+ (t
+ (let ((start (point)) prop)
+ (forward-sexp 1)
+ (setq prop (get (intern-soft (buffer-substring start (point)))
+ 'mocklisp))
+ (cond ((null prop))
+ ((stringp prop)
+ (delete-region start (point))
+ (insert prop))
+ (t
+ (save-excursion
+ (goto-char start)
+ (funcall prop)))))))))
+
+(defun ml-expansion (ml-name lisp-string)
+ (put ml-name 'mocklisp lisp-string))
+
+(ml-expansion 'defun "ml-defun")
+(ml-expansion 'if "ml-if")
+(ml-expansion 'setq '(lambda ()
+ (if (looking-at "setq[ \t\n]+buffer-modified-p")
+ (replace-match "set-buffer-modified-p"))))
+
+(ml-expansion 'while '(lambda ()
+ (let ((end (progn (forward-sexp 2) (point-marker)))
+ (start (progn (forward-sexp -1) (point))))
+ (let ((cond (buffer-substring start end)))
+ (cond ((equal cond "1")
+ (delete-region (point) end)
+ (insert "t"))
+ (t
+ (insert "(not (zerop ")
+ (goto-char end)
+ (insert "))")))
+ (set-marker end nil)
+ (goto-char start)))))
+
+(ml-expansion 'arg "ml-arg")
+(ml-expansion 'nargs "ml-nargs")
+(ml-expansion 'interactive "ml-interactive")
+(ml-expansion 'message "ml-message")
+(ml-expansion 'print "ml-print")
+(ml-expansion 'set "ml-set")
+(ml-expansion 'set-default "ml-set-default")
+(ml-expansion 'provide-prefix-argument "ml-provide-prefix-argument")
+(ml-expansion 'prefix-argument-loop "ml-prefix-argument-loop")
+(ml-expansion 'prefix-argument "ml-prefix-arg")
+(ml-expansion 'use-local-map "ml-use-local-map")
+(ml-expansion 'use-global-map "ml-use-global-map")
+(ml-expansion 'modify-syntax-entry "ml-modify-syntax-entry")
+(ml-expansion 'error-message "error")
+
+(ml-expansion 'dot "point-marker")
+(ml-expansion 'mark "mark-marker")
+(ml-expansion 'beginning-of-file "beginning-of-buffer")
+(ml-expansion 'end-of-file "end-of-buffer")
+(ml-expansion 'exchange-dot-and-mark "exchange-point-and-mark")
+(ml-expansion 'set-mark "set-mark-command")
+(ml-expansion 'argument-prefix "universal-arg")
+
+(ml-expansion 'previous-page "ml-previous-page")
+(ml-expansion 'next-page "ml-next-page")
+(ml-expansion 'next-window "ml-next-window")
+(ml-expansion 'previous-window "ml-previous-window")
+
+(ml-expansion 'newline "ml-newline")
+(ml-expansion 'next-line "ml-next-line")
+(ml-expansion 'previous-line "ml-previous-line")
+(ml-expansion 'self-insert "self-insert-command")
+(ml-expansion 'meta-digit "digit-argument")
+(ml-expansion 'meta-minus "negative-argument")
+
+(ml-expansion 'newline-and-indent "ml-newline-and-indent")
+(ml-expansion 'yank-from-killbuffer "yank")
+(ml-expansion 'yank-buffer "insert-buffer")
+(ml-expansion 'copy-region "copy-region-as-kill")
+(ml-expansion 'delete-white-space "delete-horizontal-space")
+(ml-expansion 'widen-region "widen")
+
+(ml-expansion 'forward-word '(lambda ()
+ (if (looking-at "forward-word[ \t\n]*)")
+ (replace-match "forward-word 1)"))))
+(ml-expansion 'backward-word '(lambda ()
+ (if (looking-at "backward-word[ \t\n]*)")
+ (replace-match "backward-word 1)"))))
+
+(ml-expansion 'forward-paren "forward-list")
+(ml-expansion 'backward-paren "backward-list")
+(ml-expansion 'search-reverse "ml-search-backward")
+(ml-expansion 're-search-reverse "ml-re-search-backward")
+(ml-expansion 'search-forward "ml-search-forward")
+(ml-expansion 're-search-forward "ml-re-search-forward")
+(ml-expansion 'quote "regexp-quote")
+(ml-expansion 're-query-replace "query-replace-regexp")
+(ml-expansion 're-replace-string "replace-regexp")
+
+; forward-paren-bl, backward-paren-bl
+
+(ml-expansion 'get-tty-character "read-char")
+(ml-expansion 'get-tty-input "read-input")
+(ml-expansion 'get-tty-string "read-string")
+(ml-expansion 'get-tty-buffer "read-buffer")
+(ml-expansion 'get-tty-command "read-command")
+(ml-expansion 'get-tty-variable "read-variable")
+(ml-expansion 'get-tty-no-blanks-input "read-no-blanks-input")
+(ml-expansion 'get-tty-key "read-key")
+
+(ml-expansion 'c= "char-equal")
+(ml-expansion 'goto-character "goto-char")
+(ml-expansion 'substr "ml-substr")
+(ml-expansion 'variable-apropos "apropos")
+(ml-expansion 'execute-mlisp-buffer "eval-current-buffer")
+(ml-expansion 'execute-mlisp-file "load")
+(ml-expansion 'visit-file "find-file")
+(ml-expansion 'read-file "find-file")
+(ml-expansion 'write-modified-files "save-some-buffers")
+(ml-expansion 'backup-before-writing "make-backup-files")
+(ml-expansion 'write-file-exit "save-buffers-kill-emacs")
+(ml-expansion 'write-named-file "write-file")
+(ml-expansion 'change-file-name "set-visited-file-name")
+(ml-expansion 'change-buffer-name "rename-buffer")
+(ml-expansion 'buffer-exists "get-buffer")
+(ml-expansion 'delete-buffer "kill-buffer")
+(ml-expansion 'unlink-file "delete-file")
+(ml-expansion 'unlink-checkpoint-files "delete-auto-save-files")
+(ml-expansion 'file-exists "file-exists-p")
+(ml-expansion 'write-current-file "save-buffer")
+(ml-expansion 'change-directory "cd")
+(ml-expansion 'temp-use-buffer "set-buffer")
+(ml-expansion 'fast-filter-region "filter-region")
+
+(ml-expansion 'pending-input "input-pending-p")
+(ml-expansion 'execute-keyboard-macro "call-last-kbd-macro")
+(ml-expansion 'start-remembering "start-kbd-macro")
+(ml-expansion 'end-remembering "end-kbd-macro")
+(ml-expansion 'define-keyboard-macro "name-last-kbd-macro")
+(ml-expansion 'define-string-macro "ml-define-string-macro")
+
+(ml-expansion 'current-column "ml-current-column")
+(ml-expansion 'current-indent "ml-current-indent")
+(ml-expansion 'insert-character "insert")
+
+(ml-expansion 'users-login-name "user-login-name")
+(ml-expansion 'users-full-name "user-full-name")
+(ml-expansion 'current-time "current-time-string")
+(ml-expansion 'current-numeric-time "current-numeric-time-you-lose")
+(ml-expansion 'current-buffer-name "buffer-name")
+(ml-expansion 'current-file-name "buffer-file-name")
+
+(ml-expansion 'local-binding-of "local-key-binding")
+(ml-expansion 'global-binding-of "global-key-binding")
+
+;defproc (ProcedureType, "procedure-type");
+
+(ml-expansion 'remove-key-binding "global-unset-key")
+(ml-expansion 'remove-binding "global-unset-key")
+(ml-expansion 'remove-local-binding "local-unset-key")
+(ml-expansion 'remove-all-local-bindings "use-local-map nil")
+(ml-expansion 'autoload "ml-autoload")
+
+(ml-expansion 'checkpoint-frequency "auto-save-interval")
+
+(ml-expansion 'mode-string "mode-name")
+(ml-expansion 'right-margin "fill-column")
+(ml-expansion 'tab-size "tab-width")
+(ml-expansion 'default-right-margin "default-fill-column")
+(ml-expansion 'default-tab-size "default-tab-width")
+(ml-expansion 'buffer-is-modified "(buffer-modified-p)")
+
+(ml-expansion 'file-modified-time "you-lose-on-file-modified-time")
+(ml-expansion 'needs-checkpointing "you-lose-on-needs-checkpointing")
+
+(ml-expansion 'lines-on-screen "set-screen-height")
+(ml-expansion 'columns-on-screen "set-screen-width")
+
+(ml-expansion 'dumped-emacs "t")
+
+(ml-expansion 'buffer-size "ml-buffer-size")
+(ml-expansion 'dot-is-visible "pos-visible-in-window-p")
+
+(ml-expansion 'track-eol-on-^N-^P "track-eol")
+(ml-expansion 'ctlchar-with-^ "ctl-arrow")
+(ml-expansion 'help-on-command-completion-error "completion-auto-help")
+(ml-expansion 'dump-stack-trace "backtrace")
+(ml-expansion 'pause-emacs "suspend-emacs")
+(ml-expansion 'compile-it "compile")
+
+(ml-expansion '!= "/=")
+(ml-expansion '& "logand")
+(ml-expansion '| "logior")
+(ml-expansion '^ "logxor")
+(ml-expansion '! "ml-not")
+(ml-expansion '<< "lsh")
+
+;Variable pause-writes-files
+
diff --git a/lisp/float-sup.el b/lisp/float-sup.el
new file mode 100644
index 00000000000..bf95369fd82
--- /dev/null
+++ b/lisp/float-sup.el
@@ -0,0 +1,53 @@
+;; Basic editing commands for Emacs
+;; Copyright (C) 1985, 1986, 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;; Provide a meaningful error message if we are running on
+;; bare (non-float) emacs.
+;; Can't test for 'floatp since that may be defined by float-imitation
+;; packages like float.el in this very directory.
+
+(if (fboundp 'atan)
+ nil
+ (error "Floating point was disabled at compile time"))
+
+;; provide an easy hook to tell if we are running with floats or not.
+(provide 'lisp-float-type)
+
+;; define pi and e via math-lib calls. (much less prone to killer typos.)
+(defconst pi (* 4 (atan 1)) "The value of Pi (3.1415926...)")
+(defconst e (exp 1) "The value of e (2.7182818...)")
+
+;; Careful when editing this file ... typos here will be hard to spot.
+;; (defconst pi 3.14159265358979323846264338327
+;; "The value of Pi (3.14159265358979323846264338327...)")
+
+(defconst degrees-to-radians (/ pi 180.0)
+ "Degrees to radian conversion constant")
+(defconst radians-to-degrees (/ 180.0 pi)
+ "Radian to degree conversion constant")
+
+;; these expand to a single multiply by a float
+;; when byte compiled
+
+(defmacro degrees-to-radians (x)
+ "Convert ARG from degrees to radians."
+ (list '* (/ pi 180.0) x))
+(defmacro radians-to-degrees (x)
+ "Convert ARG from radians to degrees."
+ (list '* (/ 180.0 pi) x))
diff --git a/lisp/gosmacs.el b/lisp/gosmacs.el
new file mode 100644
index 00000000000..5ea2697eeb1
--- /dev/null
+++ b/lisp/gosmacs.el
@@ -0,0 +1,102 @@
+;; Rebindings to imitate Gosmacs.
+;; Copyright (C) 1986 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defvar non-gosmacs-binding-alist nil)
+
+(defun set-gosmacs-bindings ()
+ "Rebind some keys globally to make GNU Emacs resemble Gosling Emacs.
+Use \\[set-gnu-bindings] to restore previous global bindings."
+ (interactive)
+ (setq non-gosmacs-binding-alist
+ (rebind-and-record
+ '(("\C-x\C-e" compile)
+ ("\C-x\C-f" save-buffers-kill-emacs)
+ ("\C-x\C-i" insert-file)
+ ("\C-x\C-m" save-some-buffers)
+ ("\C-x\C-n" next-error)
+ ("\C-x\C-o" switch-to-buffer)
+ ("\C-x\C-r" insert-file)
+ ("\C-x\C-u" undo)
+ ("\C-x\C-v" find-file-other-window)
+ ("\C-x\C-z" shrink-window)
+ ("\C-x!" shell-command)
+ ("\C-xd" delete-window)
+ ("\C-xn" gosmacs-next-window)
+ ("\C-xp" gosmacs-previous-window)
+ ("\C-xz" enlarge-window)
+ ("\C-z" scroll-one-line-up)
+ ("\e\C-c" save-buffers-kill-emacs)
+ ("\e!" line-to-top-of-window)
+ ("\e(" backward-paragraph)
+ ("\e)" forward-paragraph)
+ ("\e?" apropos)
+ ("\eh" delete-previous-word)
+ ("\ej" indent-sexp)
+ ("\eq" query-replace)
+ ("\er" replace-string)
+ ("\ez" scroll-one-line-down)
+ ("\C-_" suspend-emacs)))))
+
+(defun rebind-and-record (bindings)
+ "Establish many new global bindings and record the bindings replaced.
+Arg is an alist whose elements are (KEY DEFINITION).
+Value is a similar alist whose elements describe the same KEYs
+but each with the old definition that was replaced,"
+ (let (old)
+ (while bindings
+ (let* ((this (car bindings))
+ (key (car this))
+ (newdef (nth 1 this)))
+ (setq old (cons (list key (lookup-key global-map key)) old))
+ (global-set-key key newdef))
+ (setq bindings (cdr bindings)))
+ (nreverse old)))
+
+(defun set-gnu-bindings ()
+ "Restore the global bindings that were changed by \\[set-gosmacs-bindings]."
+ (interactive)
+ (rebind-and-record non-gosmacs-binding-alist))
+
+(defun gosmacs-previous-window ()
+ "Select the window above or to the left of the window now selected.
+From the window at the upper left corner, select the one at the lower right."
+ (interactive)
+ (select-window (previous-window)))
+
+(defun gosmacs-next-window ()
+ "Select the window below or to the right of the window now selected.
+From the window at the lower right corner, select the one at the upper left."
+ (interactive)
+ (select-window (next-window)))
+
+(defun scroll-one-line-up (&optional arg)
+ "Scroll the selected window up (forward in the text) one line (or N lines)."
+ (interactive "p")
+ (scroll-up (or arg 1)))
+
+(defun scroll-one-line-down (&optional arg)
+ "Scroll the selected window down (backward in the text) one line (or N)."
+ (interactive "p")
+ (scroll-down (or arg 1)))
+
+(defun line-to-top-of-window ()
+ "Scroll the selected window up so that the current line is at the top."
+ (interactive)
+ (recenter 0))
diff --git a/lisp/hexl.el b/lisp/hexl.el
new file mode 100644
index 00000000000..3a7498c8317
--- /dev/null
+++ b/lisp/hexl.el
@@ -0,0 +1,668 @@
+;; -*-Emacs-Lisp-*-
+;; hexl-mode -- Edit a file in a hex dump format.
+;; Copyright (C) 1989 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;
+;; By: Keith Gabryelski (ag@wheaties.ai.mit.edu)
+;;
+;; This may be useful in your .emacs:
+;;
+;; (autoload 'hexl-find-file "hexl"
+;; "Edit file FILENAME in hexl-mode." t)
+;;
+;; (define-key global-map "\C-c\C-h" 'hexl-find-file)
+;;
+;; NOTE: Remember to change HEXL-PROGRAM or HEXL-OPTIONS if needed.
+;;
+;; Currently hexl only supports big endian hex output with 16 bit
+;; grouping.
+;;
+;; -iso in `hexl-options' will allow iso characters to display in the
+;; ASCII region of the screen (if your emacs supports this) instead of
+;; changing them to dots.
+
+;;
+;; vars here
+;;
+
+(defvar hexl-program "hexl"
+ "The program that will hexlify and de-hexlify its stdin. hexl-program
+will always be concated with hexl-options and "-de" when dehexlfying a
+buffer.")
+
+(defvar hexl-iso ""
+ "If your emacs can handle ISO characters, this should be set to
+\"-iso\" otherwise it should be \"\".")
+
+(defvar hexl-options (format "-hex %s" hexl-iso)
+ "Options to hexl-program that suit your needs.")
+
+(defvar hexlify-command (format "%s %s" hexl-program hexl-options)
+ "The command to use to hexlify a buffer. It is the concatination of
+`hexl-program' and `hexl-options'.")
+
+(defvar dehexlify-command (format "%s -de %s" hexl-program hexl-options)
+ "The command to use to unhexlify a buffer. It is the concatination of
+`hexl-program', the option \"-de\", and `hexl-options'.")
+
+(defvar hexl-max-address 0
+ "Maximum offset into hexl buffer.")
+
+(defvar hexl-mode-map nil)
+
+;; routines
+
+(defun hexl-mode (&optional arg)
+ "\\<hexl-mode-map>
+A major mode for editting binary files in hex dump format.
+
+This function automatically converts a buffer into the hexl format
+using the function `hexlify-buffer'.
+
+Each line in the buffer has an `address' (displayed in hexadecimal)
+representing the offset into the file that the characters on this line
+are at and 16 characters from the file (displayed as hexadecimal
+values grouped every 16 bits) and as their ASCII values.
+
+If any of the characters (displayed as ASCII characters) are
+unprintable (control or meta characters) they will be replaced as
+periods.
+
+If hexl-mode is invoked with an argument the buffer is assumed to be
+in hexl-format.
+
+A sample format:
+
+ HEX ADDR: 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f ASCII-TEXT
+ -------- ---- ---- ---- ---- ---- ---- ---- ---- ----------------
+ 00000000: 5468 6973 2069 7320 6865 786c 2d6d 6f64 This is hexl-mod
+ 00000010: 652e 2020 4561 6368 206c 696e 6520 7265 e. Each line re
+ 00000020: 7072 6573 656e 7473 2031 3620 6279 7465 presents 16 byte
+ 00000030: 7320 6173 2068 6578 6164 6563 696d 616c s as hexadecimal
+ 00000040: 2041 5343 4949 0a61 6e64 2070 7269 6e74 ASCII.and print
+ 00000050: 6162 6c65 2041 5343 4949 2063 6861 7261 able ASCII chara
+ 00000060: 6374 6572 732e 2020 416e 7920 636f 6e74 cters. Any cont
+ 00000070: 726f 6c20 6f72 206e 6f6e 2d41 5343 4949 rol or non-ASCII
+ 00000080: 2063 6861 7261 6374 6572 730a 6172 6520 characters.are
+ 00000090: 6469 7370 6c61 7965 6420 6173 2070 6572 displayed as per
+ 000000a0: 696f 6473 2069 6e20 7468 6520 7072 696e iods in the prin
+ 000000b0: 7461 626c 6520 6368 6172 6163 7465 7220 table character
+ 000000c0: 7265 6769 6f6e 2e0a region..
+
+Movement is as simple as movement in a normal emacs text buffer. Most
+cursor movement bindings are the same (ie. Use \\[hexl-backward-char], \\[hexl-forward-char], \\[hexl-next-line], and \\[hexl-previous-line]
+to move the cursor left, right, down, and up).
+
+Advanced cursor movement commands (ala \\[hexl-beginning-of-line], \\[hexl-end-of-line], \\[hexl-beginning-of-buffer], and \\[hexl-end-of-buffer]) are
+also supported.
+
+There are several ways to change text in hexl mode:
+
+ASCII characters (character between space (0x20) and tilde (0x7E)) are
+bound to self-insert so you can simply type the character and it will
+insert itself (actually overstrike) into the buffer.
+
+\\[hexl-quoted-insert] followed by another keystroke allows you to insert the key even if
+it isn't bound to self-insert. An octal number can be supplied in place
+of another key to insert the octal number's ASCII representation.
+
+\\[hexl-insert-hex-char] will insert a given hexadecimal value (if it is between 0 and 0xFF)
+into the buffer at the current point.
+
+\\[hexl-insert-octal-char] will insert a given octal value (if it is between 0 and 0377)
+into the buffer at the current point.
+
+\\[hexl-insert-decimal-char] will insert a given decimal value (if it is between 0 and 255)
+into the buffer at the current point.
+
+\\[hexl-save-buffer] will save the buffer in is binary format.
+
+\\[hexl-mode-exit] will exit hexl-mode.
+
+Note: \\[write-file] will write the file out in HEXL FORMAT.
+
+You can use \\[hexl-find-file] to visit a file in hexl-mode.
+
+\\[describe-bindings] for advanced commands."
+ (interactive "p")
+ (if (eq major-mode 'hexl-mode)
+ (error "You are already in hexl mode.")
+ (kill-all-local-variables)
+ (make-local-variable 'hexl-mode-old-local-map)
+ (setq hexl-mode-old-local-map (current-local-map))
+ (use-local-map hexl-mode-map)
+
+ (make-local-variable 'hexl-mode-old-mode-name)
+ (setq hexl-mode-old-mode-name mode-name)
+ (setq mode-name "Hexl")
+
+ (make-local-variable 'hexl-mode-old-major-mode)
+ (setq hexl-mode-old-major-mode major-mode)
+ (setq major-mode 'hexl-mode)
+
+ (let ((modified (buffer-modified-p))
+ (read-only buffer-read-only)
+ (original-point (1- (point))))
+ (if (not (or (eq arg 1) (not arg)))
+;; if no argument then we guess at hexl-max-address
+ (setq hexl-max-address (+ (* (/ (1- (buffer-size)) 68) 16) 15))
+ (setq buffer-read-only nil)
+ (setq hexl-max-address (1- (buffer-size)))
+ (hexlify-buffer)
+ (set-buffer-modified-p modified)
+ (setq buffer-read-only read-only)
+ (hexl-goto-address original-point)))))
+
+(defun hexl-save-buffer ()
+ "Save a hexl format buffer as binary in visited file if modified."
+ (interactive)
+ (set-buffer-modified-p (if (buffer-modified-p)
+ (save-excursion
+ (let ((buf (generate-new-buffer " hexl"))
+ (name (buffer-name))
+ (file-name (buffer-file-name))
+ (start (point-min))
+ (end (point-max))
+ modified)
+ (set-buffer buf)
+ (insert-buffer-substring name start end)
+ (set-buffer name)
+ (dehexlify-buffer)
+ (save-buffer)
+ (setq modified (buffer-modified-p))
+ (delete-region (point-min) (point-max))
+ (insert-buffer-substring buf start end)
+ (kill-buffer buf)
+ modified))
+ (message "(No changes need to be saved)")
+ nil)))
+
+(defun hexl-find-file (filename)
+ "Edit file FILENAME in hexl-mode.
+
+Switch to a buffer visiting file FILENAME, creating one in none exists."
+ (interactive "fFilename: ")
+ (find-file filename)
+ (if (not (eq major-mode 'hexl-mode))
+ (hexl-mode)))
+
+(defun hexl-mode-exit (&optional arg)
+ "Exit hexl-mode returning to previous mode.
+With arg, don't unhexlify buffer."
+ (interactive "p")
+ (if (or (eq arg 1) (not arg))
+ (let ((modified (buffer-modified-p))
+ (read-only buffer-read-only)
+ (original-point (1+ (hexl-current-address))))
+ (setq buffer-read-only nil)
+ (dehexlify-buffer)
+ (set-buffer-modified-p modified)
+ (setq buffer-read-only read-only)
+ (goto-char original-point)))
+ (setq mode-name hexl-mode-old-mode-name)
+ (use-local-map hexl-mode-old-local-map)
+ (setq major-mode hexl-mode-old-major-mode)
+;; Kludge to update mode-line
+ (switch-to-buffer (current-buffer))
+)
+
+(defun hexl-current-address ()
+ "Return current hexl-address."
+ (interactive)
+ (let ((current-column (- (% (point) 68) 11))
+ (hexl-address 0))
+ (setq hexl-address (+ (* (/ (point) 68) 16)
+ (/ (- current-column (/ current-column 5)) 2)))
+ hexl-address))
+
+(defun hexl-address-to-marker (address)
+ "Return marker for ADDRESS."
+ (interactive "nAddress: ")
+ (+ (* (/ address 16) 68) 11 (/ (* (% address 16) 5) 2)))
+
+(defun hexl-goto-address (address)
+ "Goto hexl-mode (decimal) address ADDRESS.
+
+Signal error if ADDRESS out of range."
+ (interactive "nAddress: ")
+ (if (or (< address 0) (> address hexl-max-address))
+ (error "Out of hexl region."))
+ (goto-char (hexl-address-to-marker address)))
+
+(defun hexl-goto-hex-address (hex-address)
+ "Goto hexl-mode address (hex string) HEX-ADDRESS.
+
+Signal error if HEX-ADDRESS is out of range."
+ (interactive "sHex Address: ")
+ (hexl-goto-address (hexl-hex-string-to-integer hex-address)))
+
+(defun hexl-hex-string-to-integer (hex-string)
+ "Return decimal integer for HEX-STRING."
+ (interactive "sHex number: ")
+ (let ((hex-num 0))
+ (while (not (equal hex-string ""))
+ (setq hex-num (+ (* hex-num 16)
+ (hexl-hex-char-to-integer (string-to-char hex-string))))
+ (setq hex-string (substring hex-string 1)))
+ hex-num))
+
+(defun hexl-octal-string-to-integer (octal-string)
+ "Return decimal integer for OCTAL-STRING."
+ (interactive "sOctal number: ")
+ (let ((oct-num 0))
+ (while (not (equal octal-string ""))
+ (setq oct-num (+ (* oct-num 8)
+ (hexl-oct-char-to-integer
+ (string-to-char octal-string))))
+ (setq octal-string (substring octal-string 1)))
+ oct-num))
+
+;; move point functions
+
+(defun hexl-backward-char (arg)
+ "Move to left ARG bytes (right if ARG negative) in hexl-mode."
+ (interactive "p")
+ (hexl-goto-address (- (hexl-current-address) arg)))
+
+(defun hexl-forward-char (arg)
+ "Move right ARG bytes (left if ARG negative) in hexl-mode."
+ (interactive "p")
+ (hexl-goto-address (+ (hexl-current-address) arg)))
+
+(defun hexl-backward-short (arg)
+ "Move to left ARG shorts (right if ARG negative) in hexl-mode."
+ (interactive "p")
+ (hexl-goto-address (let ((address (hexl-current-address)))
+ (if (< arg 0)
+ (progn
+ (setq arg (- arg))
+ (while (> arg 0)
+ (if (not (equal address (logior address 3)))
+ (if (> address hexl-max-address)
+ (progn
+ (message "End of buffer.")
+ (setq address hexl-max-address))
+ (setq address (logior address 3)))
+ (if (> address hexl-max-address)
+ (progn
+ (message "End of buffer.")
+ (setq address hexl-max-address))
+ (setq address (+ address 4))))
+ (setq arg (1- arg)))
+ (if (> address hexl-max-address)
+ (progn
+ (message "End of buffer.")
+ (setq address hexl-max-address))
+ (setq address (logior address 3))))
+ (while (> arg 0)
+ (if (not (equal address (logand address -4)))
+ (setq address (logand address -4))
+ (if (not (equal address 0))
+ (setq address (- address 4))
+ (message "Beginning of buffer.")))
+ (setq arg (1- arg))))
+ address)))
+
+(defun hexl-forward-short (arg)
+ "Move right ARG shorts (left if ARG negative) in hexl-mode."
+ (interactive "p")
+ (hexl-backward-short (- arg)))
+
+(defun hexl-backward-word (arg)
+ "Move to left ARG words (right if ARG negative) in hexl-mode."
+ (interactive "p")
+ (hexl-goto-address (let ((address (hexl-current-address)))
+ (if (< arg 0)
+ (progn
+ (setq arg (- arg))
+ (while (> arg 0)
+ (if (not (equal address (logior address 7)))
+ (if (> address hexl-max-address)
+ (progn
+ (message "End of buffer.")
+ (setq address hexl-max-address))
+ (setq address (logior address 7)))
+ (if (> address hexl-max-address)
+ (progn
+ (message "End of buffer.")
+ (setq address hexl-max-address))
+ (setq address (+ address 8))))
+ (setq arg (1- arg)))
+ (if (> address hexl-max-address)
+ (progn
+ (message "End of buffer.")
+ (setq address hexl-max-address))
+ (setq address (logior address 7))))
+ (while (> arg 0)
+ (if (not (equal address (logand address -8)))
+ (setq address (logand address -8))
+ (if (not (equal address 0))
+ (setq address (- address 8))
+ (message "Beginning of buffer.")))
+ (setq arg (1- arg))))
+ address)))
+
+(defun hexl-forward-word (arg)
+ "Move right ARG words (left if ARG negative) in hexl-mode."
+ (interactive "p")
+ (hexl-backward-word (- arg)))
+
+(defun hexl-previous-line (arg)
+ "Move vertically up ARG lines [16 bytes] (down if ARG negative) in
+hexl-mode.
+
+If there is byte at the target address move to the last byte in that
+line."
+ (interactive "p")
+ (hexl-next-line (- arg)))
+
+(defun hexl-next-line (arg)
+ "Move vertically down ARG lines [16 bytes] (up if ARG negative) in
+hexl-mode.
+
+If there is no byte at the target address move to the last byte in that
+line."
+ (interactive "p")
+ (hexl-goto-address (let ((address (+ (hexl-current-address) (* arg 16)) t))
+ (if (and (< arg 0) (< address 0))
+ (progn (message "Out of hexl region.")
+ (setq address
+ (% (hexl-current-address) 16)))
+ (if (and (> address hexl-max-address)
+ (< (% hexl-max-address 16) (% address 16)))
+ (setq address hexl-max-address)
+ (if (> address hexl-max-address)
+ (progn (message "Out of hexl region.")
+ (setq
+ address
+ (+ (logand hexl-max-address -16)
+ (% (hexl-current-address) 16)))))))
+ address)))
+
+(defun hexl-beginning-of-buffer (arg)
+ "Move to the beginning of the hexl buffer; leave hexl-mark at previous
+posistion.
+
+With arg N, put point N bytes of the way from the true beginning."
+ (interactive "p")
+ (push-mark (point))
+ (hexl-goto-address (+ 0 (1- arg))))
+
+(defun hexl-end-of-buffer (arg)
+ "Goto hexl-max-address minus ARG."
+ (interactive "p")
+ (push-mark (point))
+ (hexl-goto-address (- hexl-max-address (1- arg))))
+
+(defun hexl-beginning-of-line ()
+ "Goto beginning of line in hexl mode."
+ (interactive)
+ (goto-char (+ (* (/ (point) 68) 68) 11)))
+
+(defun hexl-end-of-line ()
+ "Goto end of line in hexl mode."
+ (interactive)
+ (hexl-goto-address (let ((address (logior (hexl-current-address) 15)))
+ (if (> address hexl-max-address)
+ (setq address hexl-max-address))
+ address)))
+
+(defun hexl-scroll-down (arg)
+ "Scroll hexl buffer window upward ARG lines; or near full window if no ARG."
+ (interactive "P")
+ (if (null arg)
+ (setq arg (1- (window-height)))
+ (setq arg (prefix-numeric-value arg)))
+ (hexl-scroll-up (- arg)))
+
+(defun hexl-scroll-up (arg)
+ "Scroll hexl buffer window upward ARG lines; or near full window if no ARG."
+ (interactive "P")
+ (if (null arg)
+ (setq arg (1- (window-height)))
+ (setq arg (prefix-numeric-value arg)))
+ (let ((movement (* arg 16))
+ (address (hexl-current-address)))
+ (if (or (> (+ address movement) hexl-max-address)
+ (< (+ address movement) 0))
+ (message "Out of hexl region.")
+ (hexl-goto-address (+ address movement))
+ (recenter 0))))
+
+(defun hexl-beginning-of-1k-page ()
+ "Goto to beginning of 1k boundry."
+ (interactive)
+ (hexl-goto-address (logand (hexl-current-address) -1024)))
+
+(defun hexl-end-of-1k-page ()
+ "Goto to end of 1k boundry."
+ (interactive)
+ (hexl-goto-address (let ((address (logior (hexl-current-address) 1023)))
+ (if (> address hexl-max-address)
+ (setq address hexl-max-address))
+ address)))
+
+(defun hexl-beginning-of-512b-page ()
+ "Goto to beginning of 512 byte boundry."
+ (interactive)
+ (hexl-goto-address (logand (hexl-current-address) -512)))
+
+(defun hexl-end-of-512b-page ()
+ "Goto to end of 512 byte boundry."
+ (interactive)
+ (hexl-goto-address (let ((address (logior (hexl-current-address) 511)))
+ (if (> address hexl-max-address)
+ (setq address hexl-max-address))
+ address)))
+
+(defun hexl-quoted-insert (arg)
+ "Read next input character and insert it.
+Useful for inserting control characters.
+You may also type up to 3 octal digits, to insert a character with that code"
+ (interactive "p")
+ (hexl-insert-char (read-quoted-char) arg))
+
+;00000000: 0011 2233 4455 6677 8899 aabb ccdd eeff 0123456789ABCDEF
+
+(defun hexlify-buffer ()
+ "Convert a binary buffer to hexl format"
+ (interactive)
+ (shell-command-on-region (point-min) (point-max) hexlify-command t))
+
+(defun dehexlify-buffer ()
+ "Convert a hexl format buffer to binary."
+ (interactive)
+ (shell-command-on-region (point-min) (point-max) dehexlify-command t))
+
+(defun hexl-char-after-point ()
+ "Return char for ASCII hex digits at point."
+ (setq lh (char-after (point)))
+ (setq rh (char-after (1+ (point))))
+ (hexl-htoi lh rh))
+
+(defun hexl-htoi (lh rh)
+ "Hex (char) LH (char) RH to integer."
+ (+ (* (hexl-hex-char-to-integer lh) 16)
+ (hexl-hex-char-to-integer rh)))
+
+(defun hexl-hex-char-to-integer (character)
+ "Take a char and return its value as if it was a hex digit."
+ (if (and (>= character ?0) (<= character ?9))
+ (- character ?0)
+ (let ((ch (logior character 32)))
+ (if (and (>= ch ?a) (<= ch ?f))
+ (- ch (- ?a 10))
+ (error (format "Invalid hex digit `%c'." ch))))))
+
+(defun hexl-oct-char-to-integer (character)
+ "Take a char and return its value as if it was a octal digit."
+ (if (and (>= character ?0) (<= character ?7))
+ (- character ?0)
+ (error (format "Invalid octal digit `%c'." character))))
+
+(defun hexl-printable-character (ch)
+ "Return a displayable string for character CH."
+ (format "%c" (if hexl-iso
+ (if (or (< ch 32) (and (>= ch 127) (< ch 160)))
+ 46
+ ch)
+ (if (or (< ch 32) (>= ch 127))
+ 46
+ ch))))
+
+(defun hexl-self-insert-command (arg)
+ "Insert this character."
+ (interactive "p")
+ (hexl-insert-char last-command-char arg))
+
+(defun hexl-insert-char (ch num)
+ "Insert a character in a hexl buffer."
+ (let ((address (hexl-current-address)))
+ (while (> num 0)
+ (delete-char 2)
+ (insert (format "%02x" ch))
+ (goto-char
+ (+ (* (/ address 16) 68) 52 (% address 16)))
+ (delete-char 1)
+ (insert (hexl-printable-character ch))
+ (if (eq address hexl-max-address)
+ (hexl-goto-address address)
+ (hexl-goto-address (1+ address)))
+ (setq num (1- num)))))
+
+;; hex conversion
+
+(defun hexl-insert-hex-char (arg)
+ "Insert a ASCII char ARG times at point for a given hexadecimal number."
+ (interactive "p")
+ (let ((num (hexl-hex-string-to-integer (read-string "Hex number: "))))
+ (if (or (> num 255) (< num 0))
+ (error "Hex number out of range.")
+ (hexl-insert-char num arg))))
+
+(defun hexl-insert-decimal-char (arg)
+ "Insert a ASCII char ARG times at point for a given decimal number."
+ (interactive "p")
+ (let ((num (string-to-int (read-string "Decimal Number: "))))
+ (if (or (> num 255) (< num 0))
+ (error "Decimal number out of range.")
+ (hexl-insert-char num arg))))
+
+(defun hexl-insert-octal-char (arg)
+ "Insert a ASCII char ARG times at point for a given octal number."
+ (interactive "p")
+ (let ((num (hexl-octal-string-to-integer (read-string "Octal Number: "))))
+ (if (or (> num 255) (< num 0))
+ (error "Decimal number out of range.")
+ (hexl-insert-char num arg))))
+
+;; startup stuff.
+
+(if hexl-mode-map
+ nil
+ (setq hexl-mode-map (make-sparse-keymap))
+
+ (define-key hexl-mode-map "\C-a" 'hexl-beginning-of-line)
+ (define-key hexl-mode-map "\C-b" 'hexl-backward-char)
+ (define-key hexl-mode-map "\C-d" 'undefined)
+ (define-key hexl-mode-map "\C-e" 'hexl-end-of-line)
+ (define-key hexl-mode-map "\C-f" 'hexl-forward-char)
+
+ (if (not (eq (key-binding "\C-h") 'help-command))
+ (define-key hexl-mode-map "\C-h" 'undefined))
+
+ (define-key hexl-mode-map "\C-i" 'hexl-self-insert-command)
+ (define-key hexl-mode-map "\C-j" 'hexl-self-insert-command)
+ (define-key hexl-mode-map "\C-k" 'undefined)
+ (define-key hexl-mode-map "\C-m" 'hexl-self-insert-command)
+ (define-key hexl-mode-map "\C-n" 'hexl-next-line)
+ (define-key hexl-mode-map "\C-o" 'undefined)
+ (define-key hexl-mode-map "\C-p" 'hexl-previous-line)
+ (define-key hexl-mode-map "\C-q" 'hexl-quoted-insert)
+ (define-key hexl-mode-map "\C-t" 'undefined)
+ (define-key hexl-mode-map "\C-v" 'hexl-scroll-up)
+ (define-key hexl-mode-map "\C-w" 'undefined)
+ (define-key hexl-mode-map "\C-y" 'undefined)
+
+ (let ((ch 32))
+ (while (< ch 127)
+ (define-key hexl-mode-map (format "%c" ch) 'hexl-self-insert-command)
+ (setq ch (1+ ch))))
+
+ (define-key hexl-mode-map "\e\C-a" 'hexl-beginning-of-512b-page)
+ (define-key hexl-mode-map "\e\C-b" 'hexl-backward-short)
+ (define-key hexl-mode-map "\e\C-c" 'undefined)
+ (define-key hexl-mode-map "\e\C-d" 'hexl-insert-decimal-char)
+ (define-key hexl-mode-map "\e\C-e" 'hexl-end-of-512b-page)
+ (define-key hexl-mode-map "\e\C-f" 'hexl-forward-short)
+ (define-key hexl-mode-map "\e\C-g" 'undefined)
+ (define-key hexl-mode-map "\e\C-h" 'undefined)
+ (define-key hexl-mode-map "\e\C-i" 'undefined)
+ (define-key hexl-mode-map "\e\C-j" 'undefined)
+ (define-key hexl-mode-map "\e\C-k" 'undefined)
+ (define-key hexl-mode-map "\e\C-l" 'undefined)
+ (define-key hexl-mode-map "\e\C-m" 'undefined)
+ (define-key hexl-mode-map "\e\C-n" 'undefined)
+ (define-key hexl-mode-map "\e\C-o" 'hexl-insert-octal-char)
+ (define-key hexl-mode-map "\e\C-p" 'undefined)
+ (define-key hexl-mode-map "\e\C-q" 'undefined)
+ (define-key hexl-mode-map "\e\C-r" 'undefined)
+ (define-key hexl-mode-map "\e\C-s" 'undefined)
+ (define-key hexl-mode-map "\e\C-t" 'undefined)
+ (define-key hexl-mode-map "\e\C-u" 'undefined)
+
+ (define-key hexl-mode-map "\e\C-w" 'undefined)
+ (define-key hexl-mode-map "\e\C-x" 'hexl-insert-hex-char)
+ (define-key hexl-mode-map "\e\C-y" 'undefined)
+
+
+ (define-key hexl-mode-map "\ea" 'hexl-beginning-of-1k-page)
+ (define-key hexl-mode-map "\eb" 'hexl-backward-word)
+ (define-key hexl-mode-map "\ec" 'undefined)
+ (define-key hexl-mode-map "\ed" 'undefined)
+ (define-key hexl-mode-map "\ee" 'hexl-end-of-1k-page)
+ (define-key hexl-mode-map "\ef" 'hexl-forward-word)
+ (define-key hexl-mode-map "\eg" 'hexl-goto-hex-address)
+ (define-key hexl-mode-map "\eh" 'undefined)
+ (define-key hexl-mode-map "\ei" 'undefined)
+ (define-key hexl-mode-map "\ej" 'hexl-goto-address)
+ (define-key hexl-mode-map "\ek" 'undefined)
+ (define-key hexl-mode-map "\el" 'undefined)
+ (define-key hexl-mode-map "\em" 'undefined)
+ (define-key hexl-mode-map "\en" 'undefined)
+ (define-key hexl-mode-map "\eo" 'undefined)
+ (define-key hexl-mode-map "\ep" 'undefined)
+ (define-key hexl-mode-map "\eq" 'undefined)
+ (define-key hexl-mode-map "\er" 'undefined)
+ (define-key hexl-mode-map "\es" 'undefined)
+ (define-key hexl-mode-map "\et" 'undefined)
+ (define-key hexl-mode-map "\eu" 'undefined)
+ (define-key hexl-mode-map "\ev" 'hexl-scroll-down)
+ (define-key hexl-mode-map "\ey" 'undefined)
+ (define-key hexl-mode-map "\ez" 'undefined)
+ (define-key hexl-mode-map "\e<" 'hexl-beginning-of-buffer)
+ (define-key hexl-mode-map "\e>" 'hexl-end-of-buffer)
+
+ (define-key hexl-mode-map "\C-c\C-c" 'hexl-mode-exit)
+
+ (define-key hexl-mode-map "\C-x\C-p" 'undefined)
+ (define-key hexl-mode-map "\C-x\C-s" 'hexl-save-buffer)
+ (define-key hexl-mode-map "\C-x\C-t" 'undefined))
+
+;; The End.
diff --git a/lisp/ledit.el b/lisp/ledit.el
new file mode 100644
index 00000000000..1ab35d5bfb2
--- /dev/null
+++ b/lisp/ledit.el
@@ -0,0 +1,138 @@
+;; Emacs side of ledit interface
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;;; To do:
+;;; o lisp -> emacs side of things (grind-definition and find-definition)
+
+(defvar ledit-mode-map nil)
+
+(defconst ledit-zap-file (concat "/tmp/" (user-login-name) ".l1")
+ "File name for data sent to Lisp by Ledit.")
+(defconst ledit-read-file (concat "/tmp/" (user-login-name) ".l2")
+ "File name for data sent to Ledit by Lisp.")
+(defconst ledit-compile-file
+ (concat "/tmp/" (user-login-name) ".l4")
+ "File name for data sent to Lisp compiler by Ledit.")
+(defconst ledit-buffer "*LEDIT*"
+ "Name of buffer in which Ledit accumulates data to send to Lisp.")
+;These are now in loaddefs.el
+;(defconst ledit-save-files t
+; "*Non-nil means Ledit should save files before transferring to Lisp.")
+;(defconst ledit-go-to-lisp-string "%?lisp"
+; "*Shell commands to execute to resume Lisp job.")
+;(defconst ledit-go-to-liszt-string "%?liszt"
+; "*Shell commands to execute to resume Lisp compiler job.")
+
+(defun ledit-save-defun ()
+ "Save the current defun in the ledit buffer"
+ (interactive)
+ (save-excursion
+ (end-of-defun)
+ (let ((end (point)))
+ (beginning-of-defun)
+ (append-to-buffer ledit-buffer (point) end))
+ (message "Current defun saved for Lisp")))
+
+(defun ledit-save-region (beg end)
+ "Save the current region in the ledit buffer"
+ (interactive "r")
+ (append-to-buffer ledit-buffer beg end)
+ (message "Region saved for Lisp"))
+
+(defun ledit-zap-defun-to-lisp ()
+ "Carry the current defun to lisp"
+ (interactive)
+ (ledit-save-defun)
+ (ledit-go-to-lisp))
+
+(defun ledit-zap-defun-to-liszt ()
+ "Carry the current defun to liszt"
+ (interactive)
+ (ledit-save-defun)
+ (ledit-go-to-liszt))
+
+(defun ledit-zap-region-to-lisp (beg end)
+ "Carry the current region to lisp"
+ (interactive "r")
+ (ledit-save-region beg end)
+ (ledit-go-to-lisp))
+
+(defun ledit-go-to-lisp ()
+ "Suspend Emacs and restart a waiting Lisp job."
+ (interactive)
+ (if ledit-save-files
+ (save-some-buffers))
+ (if (get-buffer ledit-buffer)
+ (save-excursion
+ (set-buffer ledit-buffer)
+ (goto-char (point-min))
+ (write-region (point-min) (point-max) ledit-zap-file)
+ (erase-buffer)))
+ (suspend-emacs ledit-go-to-lisp-string)
+ (load ledit-read-file t t))
+
+(defun ledit-go-to-liszt ()
+ "Suspend Emacs and restart a waiting Liszt job."
+ (interactive)
+ (if ledit-save-files
+ (save-some-buffers))
+ (if (get-buffer ledit-buffer)
+ (save-excursion
+ (set-buffer ledit-buffer)
+ (goto-char (point-min))
+ (insert "(declare (macros t))\n")
+ (write-region (point-min) (point-max) ledit-compile-file)
+ (erase-buffer)))
+ (suspend-emacs ledit-go-to-liszt-string)
+ (load ledit-read-file t t))
+
+(defun ledit-setup ()
+ "Set up key bindings for the Lisp / Emacs interface"
+ (if (not ledit-mode-map)
+ (progn (setq ledit-mode-map (make-sparse-keymap))
+ (lisp-mode-commands ledit-mode-map)))
+ (define-key ledit-mode-map "\e\^d" 'ledit-save-defun)
+ (define-key ledit-mode-map "\e\^r" 'ledit-save-region)
+ (define-key ledit-mode-map "\^xz" 'ledit-go-to-lisp)
+ (define-key ledit-mode-map "\e\^c" 'ledit-go-to-liszt))
+
+(ledit-setup)
+
+(defun ledit-mode ()
+ "Major mode for editing text and stuffing it to a Lisp job.
+Like Lisp mode, plus these special commands:
+ M-C-d -- record defun at or after point
+ for later transmission to Lisp job.
+ M-C-r -- record region for later transmission to Lisp job.
+ C-x z -- transfer to Lisp job and transmit saved text.
+ M-C-c -- transfer to Liszt (Lisp compiler) job
+ and transmit saved text.
+\\{ledit-mode-map}
+To make Lisp mode automatically change to Ledit mode,
+do (setq lisp-mode-hook 'ledit-from-lisp-mode)"
+ (interactive)
+ (lisp-mode)
+ (ledit-from-lisp-mode))
+
+(defun ledit-from-lisp-mode ()
+ (use-local-map ledit-mode-map)
+ (setq mode-name "Ledit")
+ (setq major-mode 'ledit-mode)
+ (run-hooks 'ledit-mode-hook))
diff --git a/lisp/macros.el b/lisp/macros.el
new file mode 100644
index 00000000000..bd2bd9ce449
--- /dev/null
+++ b/lisp/macros.el
@@ -0,0 +1,103 @@
+;; Non-primitive commands for keyboard macros.
+;; Copyright (C) 1985, 1986, 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defun name-last-kbd-macro (symbol)
+ "Assign a name to the last keyboard macro defined.
+One arg, a symbol, which is the name to define.
+The symbol's function definition becomes the keyboard macro string.
+Such a \"function\" cannot be called from Lisp, but it is a valid command
+definition for the editor command loop."
+ (interactive "SName for last kbd macro: ")
+ (or last-kbd-macro
+ (error "No keyboard macro defined"))
+ (and (fboundp symbol)
+ (not (stringp (symbol-function symbol)))
+ (error "Function %s is already defined and not a keyboard macro."
+ symbol))
+ (fset symbol last-kbd-macro))
+
+(defun insert-kbd-macro (macroname &optional keys)
+ "Insert in buffer the definition of kbd macro NAME, as Lisp code.
+Second argument KEYS non-nil means also record the keys it is on.
+ (This is the prefix argument, when calling interactively.)
+
+This Lisp code will, when executed, define the kbd macro with the
+same definition it has now. If you say to record the keys,
+the Lisp code will also rebind those keys to the macro.
+Only global key bindings are recorded since executing this Lisp code
+always makes global bindings.
+
+To save a kbd macro, visit a file of Lisp code such as your ~/.emacs,
+use this command, and then save the file."
+ (interactive "CInsert kbd macro (name): \nP")
+ (insert "(fset '")
+ (prin1 macroname (current-buffer))
+ (insert "\n ")
+ (prin1 (symbol-function macroname) (current-buffer))
+ (insert ")\n")
+ (if keys
+ (let ((keys (where-is-internal macroname nil)))
+ (while keys
+ (insert "(global-set-key ")
+ (prin1 (car keys) (current-buffer))
+ (insert " '")
+ (prin1 macroname (current-buffer))
+ (insert ")\n")
+ (setq keys (cdr keys))))))
+
+(defun kbd-macro-query (flag)
+ "Query user during kbd macro execution.
+With prefix argument, enters recursive edit,
+ reading keyboard commands even within a kbd macro.
+ You can give different commands each time the macro executes.
+Without prefix argument, reads a character. Your options are:
+ Space -- execute the rest of the macro.
+ DEL -- skip the rest of the macro; start next repetition.
+ C-d -- skip rest of the macro and don't repeat it any more.
+ C-r -- enter a recursive edit, then on exit ask again for a character
+ C-l -- redisplay screen and ask again."
+ (interactive "P")
+ (or executing-macro
+ defining-kbd-macro
+ (error "Not defining or executing kbd macro"))
+ (if flag
+ (let (executing-macro defining-kbd-macro)
+ (recursive-edit))
+ (if (not executing-macro)
+ nil
+ (let ((loop t))
+ (while loop
+ (let ((char (let ((executing-macro nil)
+ (defining-kbd-macro nil))
+ (message "Proceed with macro? (Space, DEL, C-d, C-r or C-l) ")
+ (read-char))))
+ (cond ((= char ? )
+ (setq loop nil))
+ ((= char ?\177)
+ (setq loop nil)
+ (setq executing-macro ""))
+ ((= char ?\C-d)
+ (setq loop nil)
+ (setq executing-macro t))
+ ((= char ?\C-l)
+ (recenter nil))
+ ((= char ?\C-r)
+ (let (executing-macro defining-kbd-macro)
+ (recursive-edit))))))))))
diff --git a/lisp/mail/emacsbug.el b/lisp/mail/emacsbug.el
new file mode 100644
index 00000000000..cf9ef90e89d
--- /dev/null
+++ b/lisp/mail/emacsbug.el
@@ -0,0 +1,38 @@
+;; Command to report Emacs bugs to appropriate mailing list.
+;; Not fully installed because it can work only on Internet hosts.
+;; Copyright (C) 1985 Free Software Foundation, Inc.
+;; Principal author K. Shane Hartman
+
+;; 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;; >> This should be an address which is accessible to your machine,
+;; >> otherwise you can't use this file. It will only work on the
+;; >> internet with this address.
+
+(defvar bug-gnu-emacs "bug-gnu-emacs@prep.ai.mit.edu"
+ "Address of site maintaining mailing list for Gnu emacs bugs.")
+
+(defun report-emacs-bug (topic)
+ "Report a bug in Gnu emacs.
+Prompts for bug subject. Leaves you in a mail buffer."
+ (interactive "sBug Subject: ")
+ (mail nil bug-gnu-emacs topic)
+ (goto-char (point-max))
+ (insert "\nIn " (emacs-version) "\n\n")
+ (message (substitute-command-keys "Type \\[mail-send] to send bug report.")))
+
diff --git a/lisp/mail/mail-utils.el b/lisp/mail/mail-utils.el
new file mode 100644
index 00000000000..49c563d65b5
--- /dev/null
+++ b/lisp/mail/mail-utils.el
@@ -0,0 +1,195 @@
+;; Utility functions used both by rmail and rnews
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(provide 'mail-utils)
+
+;; should be in loaddefs
+(defvar mail-use-rfc822 nil
+ "*If non-nil, use a full, hairy RFC822 parser on mail addresses.
+Otherwise, (the default) use a smaller, somewhat faster and
+often-correct parser.")
+
+(defun mail-string-delete (string start end)
+ "Returns a string containing all of STRING except the part
+from START (inclusive) to END (exclusive)."
+ (if (null end) (substring string 0 start)
+ (concat (substring string 0 start)
+ (substring string end nil))))
+
+(defun mail-strip-quoted-names (address)
+ "Delete comments and quoted strings in an address list ADDRESS.
+Also delete leading/trailing whitespace and replace FOO <BAR> with just BAR.
+Return a modified address list."
+ (if mail-use-rfc822
+ (progn (require 'rfc822)
+ (mapconcat 'identity (rfc822-addresses address) ", "))
+ (let (pos)
+ (string-match "\\`[ \t\n]*" address)
+ ;; strip surrounding whitespace
+ (setq address (substring address
+ (match-end 0)
+ (string-match "[ \t\n]*\\'" address
+ (match-end 0))))
+
+ ;; Detect nested comments.
+ (if (string-match "[ \t]*(\\([^)\"\\]\\|\\\\.\\|\\\\\n\\)*(" address)
+ ;; Strip nested comments.
+ (save-excursion
+ (set-buffer (get-buffer-create " *temp*"))
+ (erase-buffer)
+ (insert address)
+ (set-syntax-table lisp-mode-syntax-table)
+ (goto-char 1)
+ (while (search-forward "(" nil t)
+ (forward-char -1)
+ (skip-chars-backward " \t")
+ (delete-region (point)
+ (save-excursion (forward-sexp 1) (point))))
+ (setq address (buffer-string))
+ (erase-buffer))
+ ;; Strip non-nested comments an easier way.
+ (while (setq pos (string-match
+ ;; This doesn't hack rfc822 nested comments
+ ;; `(xyzzy (foo) whinge)' properly. Big deal.
+ "[ \t]*(\\([^)\"\\]\\|\\\\.\\|\\\\\n\\)*)"
+ address))
+ (setq address
+ (mail-string-delete address
+ pos (match-end 0)))))
+
+ ;; strip `quoted' names (This is supposed to hack `"Foo Bar" <bar@host>')
+ (setq pos 0)
+ (while (setq pos (string-match
+ "[ \t]*\"\\([^\"\\]\\|\\\\.\\|\\\\\n\\)*\"[ \t\n]*"
+ address pos))
+ ;; If the next thing is "@", we have "foo bar"@host. Leave it.
+ (if (and (> (length address) (match-end 0))
+ (= (aref address (match-end 0)) ?@))
+ (setq pos (match-end 0))
+ (setq address
+ (mail-string-delete address
+ pos (match-end 0)))))
+ ;; Retain only part of address in <> delims, if there is such a thing.
+ (while (setq pos (string-match "\\(,\\|\\`\\)[^,]*<\\([^>,]*>\\)"
+ address))
+ (let ((junk-beg (match-end 1))
+ (junk-end (match-beginning 2))
+ (close (match-end 0)))
+ (setq address (mail-string-delete address (1- close) close))
+ (setq address (mail-string-delete address junk-beg junk-end))))
+ address)))
+
+(or (and (boundp 'rmail-default-dont-reply-to-names)
+ (not (null rmail-default-dont-reply-to-names)))
+ (setq rmail-default-dont-reply-to-names "info-"))
+
+; rmail-dont-reply-to-names is defined in loaddefs
+(defun rmail-dont-reply-to (userids)
+ "Returns string of mail addresses USERIDS sans any recipients
+that start with matches for rmail-dont-reply-to-names.
+Usenet paths ending in an element that matches are removed also."
+ (if (null rmail-dont-reply-to-names)
+ (setq rmail-dont-reply-to-names
+ (concat (if rmail-default-dont-reply-to-names
+ (concat rmail-default-dont-reply-to-names "\\|")
+ "")
+ (concat (regexp-quote (user-original-login-name))
+ "\\>"))))
+ (let ((match (concat "\\(^\\|,\\)[ \t\n]*\\([^,\n]*!\\|\\)\\("
+ rmail-dont-reply-to-names
+ "\\)"))
+ (case-fold-search t)
+ pos epos)
+ (while (setq pos (string-match match userids))
+ (if (> pos 0) (setq pos (1+ pos)))
+ (setq epos
+ (if (string-match "[ \t\n,]+" userids (match-end 0))
+ (match-end 0)
+ (length userids)))
+ (setq userids
+ (mail-string-delete
+ userids pos epos)))
+ ;; get rid of any trailing commas
+ (if (setq pos (string-match "[ ,\t\n]*\\'" userids))
+ (setq userids (substring userids 0 pos)))
+ ;; remove leading spaces. they bother me.
+ (if (string-match "\\s *" userids)
+ (substring userids (match-end 0))
+ userids)))
+
+(defun mail-fetch-field (field-name &optional last all)
+ "Return the value of the header field FIELD.
+The buffer is expected to be narrowed to just the headers of the message.
+If 2nd arg LAST is non-nil, use the last such field if there are several.
+If 3rd arg ALL is non-nil, concatenate all such fields, with commas between."
+ (save-excursion
+ (goto-char (point-min))
+ (let ((case-fold-search t)
+ (name (concat "^" (regexp-quote field-name) "[ \t]*:[ \t]*")))
+ (goto-char (point-min))
+ (if all
+ (let ((value ""))
+ (while (re-search-forward name nil t)
+ (let ((opoint (point)))
+ (while (progn (forward-line 1)
+ (looking-at "[ \t]")))
+ (setq value (concat value
+ (if (string= value "") "" ", ")
+ (buffer-substring opoint (1- (point)))))))
+ (and (not (string= value "")) value))
+ (if (re-search-forward name nil t)
+ (progn
+ (if last (while (re-search-forward name nil t)))
+ (let ((opoint (point)))
+ (while (progn (forward-line 1)
+ (looking-at "[ \t]")))
+ (buffer-substring opoint (1- (point))))))))))
+
+;; Parse a list of tokens separated by commas.
+;; It runs from point to the end of the visible part of the buffer.
+;; Whitespace before or after tokens is ignored,
+;; but whitespace within tokens is kept.
+(defun mail-parse-comma-list ()
+ (let (accumulated
+ beg)
+ (skip-chars-forward " ")
+ (while (not (eobp))
+ (setq beg (point))
+ (skip-chars-forward "^,")
+ (skip-chars-backward " ")
+ (setq accumulated
+ (cons (buffer-substring beg (point))
+ accumulated))
+ (skip-chars-forward "^,")
+ (skip-chars-forward ", "))
+ accumulated))
+
+(defun mail-comma-list-regexp (labels)
+ (let (pos)
+ (setq pos (or (string-match "[^ \t]" labels) 0))
+ ;; Remove leading and trailing whitespace.
+ (setq labels (substring labels pos (string-match "[ \t]*$" labels pos)))
+ ;; Change each comma to \|, and flush surrounding whitespace.
+ (while (setq pos (string-match "[ \t]*,[ \t]*" labels))
+ (setq labels
+ (concat (substring labels 0 pos)
+ "\\|"
+ (substring labels (match-end 0))))))
+ labels)
diff --git a/lisp/mail/rmailedit.el b/lisp/mail/rmailedit.el
new file mode 100644
index 00000000000..d5c3dfd3361
--- /dev/null
+++ b/lisp/mail/rmailedit.el
@@ -0,0 +1,105 @@
+;; "RMAIL edit mode" Edit the current message.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(require 'rmail)
+
+(defvar rmail-edit-map nil)
+(if rmail-edit-map
+ nil
+ (setq rmail-edit-map (nconc (make-sparse-keymap) text-mode-map))
+ (define-key rmail-edit-map "\C-c\C-c" 'rmail-cease-edit)
+ (define-key rmail-edit-map "\C-c\C-]" 'rmail-abort-edit))
+
+;; Rmail Edit mode is suitable only for specially formatted data.
+(put 'rmail-edit-mode 'mode-class 'special)
+
+(defun rmail-edit-mode ()
+ "Major mode for editing the contents of an RMAIL message.
+The editing commands are the same as in Text mode, together with two commands
+to return to regular RMAIL:
+ * rmail-abort-edit cancels the changes
+ you have made and returns to RMAIL
+ * rmail-cease-edit makes them permanent.
+\\{rmail-edit-map}"
+ (use-local-map rmail-edit-map)
+ (setq major-mode 'rmail-edit-mode)
+ (setq mode-name "RMAIL Edit")
+ (if (boundp 'mode-line-modified)
+ (setq mode-line-modified (default-value 'mode-line-modified))
+ (setq mode-line-format (default-value 'mode-line-format)))
+ (run-hooks 'text-mode-hook 'rmail-edit-mode-hook))
+
+(defun rmail-edit-current-message ()
+ "Edit the contents of this message."
+ (interactive)
+ (rmail-edit-mode)
+ (make-local-variable 'rmail-old-text)
+ (setq rmail-old-text (buffer-substring (point-min) (point-max)))
+ (setq buffer-read-only nil)
+ (set-buffer-modified-p (buffer-modified-p))
+ ;; Make mode line update.
+ (if (and (eq (key-binding "\C-c\C-c") 'rmail-cease-edit)
+ (eq (key-binding "\C-c\C-]") 'rmail-abort-edit))
+ (message "Editing: Type C-c C-c to return to Rmail, C-c C-] to abort")
+ (message (substitute-command-keys
+ "Editing: Type \\[rmail-cease-edit] to return to Rmail, \\[rmail-abort-edit] to abort"))))
+
+(defun rmail-cease-edit ()
+ "Finish editing message; switch back to Rmail proper."
+ (interactive)
+ ;; Make sure buffer ends with a newline.
+ (save-excursion
+ (goto-char (point-max))
+ (if (/= (preceding-char) ?\n)
+ (insert "\n"))
+ ;; Adjust the marker that points to the end of this message.
+ (set-marker (aref rmail-message-vector (1+ rmail-current-message))
+ (point)))
+ (let ((old rmail-old-text))
+ ;; Update the mode line.
+ (set-buffer-modified-p (buffer-modified-p))
+ (rmail-mode-1)
+ (if (and (= (length old) (- (point-max) (point-min)))
+ (string= old (buffer-substring (point-min) (point-max))))
+ ()
+ (setq old nil)
+ (rmail-set-attribute "edited" t)
+ (if (boundp 'rmail-summary-vector)
+ (progn
+ (aset rmail-summary-vector (1- rmail-current-message) nil)
+ (save-excursion
+ (rmail-widen-to-current-msgbeg
+ (function (lambda ()
+ (forward-line 2)
+ (if (looking-at "Summary-line: ")
+ (let ((buffer-read-only nil))
+ (delete-region (point)
+ (progn (forward-line 1)
+ (point))))))))
+ (rmail-show-message))))))
+ (setq buffer-read-only t))
+
+(defun rmail-abort-edit ()
+ "Abort edit of current message; restore original contents."
+ (interactive)
+ (delete-region (point-min) (point-max))
+ (insert rmail-old-text)
+ (rmail-cease-edit))
+
diff --git a/lisp/mail/rmailkwd.el b/lisp/mail/rmailkwd.el
new file mode 100644
index 00000000000..af48e0f7dec
--- /dev/null
+++ b/lisp/mail/rmailkwd.el
@@ -0,0 +1,260 @@
+;; "RMAIL" mail reader for Emacs.
+;; Copyright (C) 1985, 1988 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;; Global to all RMAIL buffers. It exists primarily for the sake of
+;; completion. It is better to use strings with the label functions
+;; and let them worry about making the label.
+
+(defvar rmail-label-obarray (make-vector 47 0))
+
+;; Named list of symbols representing valid message attributes in RMAIL.
+
+(defconst rmail-attributes
+ (cons 'rmail-keywords
+ (mapcar '(lambda (s) (intern s rmail-label-obarray))
+ '("deleted" "answered" "filed" "forwarded" "unseen" "edited"))))
+
+(defconst rmail-deleted-label (intern "deleted" rmail-label-obarray))
+
+;; Named list of symbols representing valid message keywords in RMAIL.
+
+(defvar rmail-keywords nil)
+
+(defun rmail-add-label (string)
+ "Add LABEL to labels associated with current RMAIL message.
+Completion is performed over known labels when reading."
+ (interactive (list (rmail-read-label "Add label")))
+ (rmail-set-label string t))
+
+(defun rmail-kill-label (string)
+ "Remove LABEL from labels associated with current RMAIL message.
+Completion is performed over known labels when reading."
+ (interactive (list (rmail-read-label "Remove label")))
+ (rmail-set-label string nil))
+
+(defun rmail-read-label (prompt)
+ (if (not rmail-keywords) (rmail-parse-file-keywords))
+ (let ((result
+ (completing-read (concat prompt
+ (if rmail-last-label
+ (concat " (default "
+ (symbol-name rmail-last-label)
+ "): ")
+ ": "))
+ rmail-label-obarray
+ nil
+ nil)))
+ (if (string= result "")
+ rmail-last-label
+ (setq rmail-last-label (rmail-make-label result t)))))
+
+(defun rmail-set-label (l state &optional n)
+ (rmail-maybe-set-message-counters)
+ (if (not n) (setq n rmail-current-message))
+ (aset rmail-summary-vector (1- n) nil)
+ (let* ((attribute (rmail-attribute-p l))
+ (keyword (and (not attribute)
+ (or (rmail-keyword-p l)
+ (rmail-install-keyword l))))
+ (label (or attribute keyword)))
+ (if label
+ (let ((omax (- (buffer-size) (point-max)))
+ (omin (- (buffer-size) (point-min)))
+ (buffer-read-only nil)
+ (case-fold-search t))
+ (unwind-protect
+ (save-excursion
+ (widen)
+ (goto-char (rmail-msgbeg n))
+ (forward-line 1)
+ (if (not (looking-at "[01],"))
+ nil
+ (let ((start (1+ (point)))
+ (bound))
+ (narrow-to-region (point) (progn (end-of-line) (point)))
+ (setq bound (point-max))
+ (search-backward ",," nil t)
+ (if attribute
+ (setq bound (1+ (point)))
+ (setq start (1+ (point))))
+ (goto-char start)
+; (while (re-search-forward "[ \t]*,[ \t]*" nil t)
+; (replace-match ","))
+; (goto-char start)
+ (if (re-search-forward
+ (concat ", " (rmail-quote-label-name label) ",")
+ bound
+ 'move)
+ (if (not state) (replace-match ","))
+ (if state (insert " " (symbol-name label) ",")))
+ (if (eq label rmail-deleted-label)
+ (rmail-set-message-deleted-p n state)))))
+ (narrow-to-region (- (buffer-size) omin) (- (buffer-size) omax))
+ (if (= n rmail-current-message) (rmail-display-labels)))))))
+
+;; Commented functions aren't used by RMAIL but might be nice for user
+;; packages that do stuff with RMAIL. Note that rmail-message-labels-p
+;; is in rmailsum now.
+
+;(defun rmail-message-attribute-p (attribute &optional n)
+; "Returns t if ATTRIBUTE on NTH or current message."
+; (rmail-message-labels-p (rmail-make-label attribute t) n))
+
+;(defun rmail-message-keyword-p (keyword &optional n)
+; "Returns t if KEYWORD on NTH or current message."
+; (rmail-message-labels-p (rmail-make-label keyword t) n t))
+
+;(defun rmail-message-label-p (label &optional n)
+; "Returns symbol if LABEL (attribute or keyword) on NTH or current message."
+; (rmail-message-labels-p (rmail-make-label label t) n 'all))
+
+;; Not used by RMAIL but might be nice for user package.
+
+;(defun rmail-parse-message-labels (&optional n)
+; "Returns labels associated with NTH or current RMAIL message.
+;Results is a list of two lists. The first is the message attributes
+;and the second is the message keywords. Labels are represented as symbols."
+; (let ((omin (- (buffer-size) (point-min)))
+; (omax (- (buffer-size) (point-max)))
+; (result))
+; (unwind-protect
+; (save-excursion
+; (let ((beg (rmail-msgbeg (or n rmail-current-message))))
+; (widen)
+; (goto-char beg)
+; (forward-line 1)
+; (if (looking-at "[01],")
+; (save-restriction
+; (narrow-to-region (point) (save-excursion (end-of-line) (point)))
+; (rmail-nuke-whitespace)
+; (goto-char (1+ (point-min)))
+; (list (mail-parse-comma-list) (mail-parse-comma-list))))))
+; (narrow-to-region (- (buffer-size) omin)
+; (- (buffer-size) omax))
+; nil)))
+
+(defun rmail-attribute-p (s)
+ (let ((symbol (rmail-make-label s)))
+ (if (memq symbol (cdr rmail-attributes)) symbol)))
+
+(defun rmail-keyword-p (s)
+ (let ((symbol (rmail-make-label s)))
+ (if (memq symbol (cdr (rmail-keywords))) symbol)))
+
+(defun rmail-make-label (s &optional forcep)
+ (cond ((symbolp s) s)
+ (forcep (intern (downcase s) rmail-label-obarray))
+ (t (intern-soft (downcase s) rmail-label-obarray))))
+
+(defun rmail-force-make-label (s)
+ (intern (downcase s) rmail-label-obarray))
+
+(defun rmail-quote-label-name (label)
+ (regexp-quote (symbol-name (rmail-make-label label t))))
+
+;; Motion on messages with keywords.
+
+(defun rmail-previous-labeled-message (n label)
+ "Show previous message with LABEL. Defaults to last labels used.
+With prefix argument N moves backward N messages with these labels."
+ (interactive "p\nsMove to previous msg with labels: ")
+ (rmail-next-labeled-message (- n) label))
+
+(defun rmail-next-labeled-message (n labels)
+ "Show next message with LABEL. Defaults to last labels used.
+With prefix argument N moves forward N messages with these labels."
+ (interactive "p\nsMove to next msg with labels: ")
+ (if (string= labels "")
+ (setq labels rmail-last-multi-labels))
+ (or labels
+ (error "No labels to find have been specified previously"))
+ (setq rmail-last-multi-labels labels)
+ (rmail-maybe-set-message-counters)
+ (let ((lastwin rmail-current-message)
+ (current rmail-current-message)
+ (regexp (concat ", ?\\("
+ (mail-comma-list-regexp labels)
+ "\\),")))
+ (save-restriction
+ (widen)
+ (while (and (> n 0) (< current rmail-total-messages))
+ (setq current (1+ current))
+ (if (rmail-message-labels-p current regexp)
+ (setq lastwin current n (1- n))))
+ (while (and (< n 0) (> current 1))
+ (setq current (1- current))
+ (if (rmail-message-labels-p current regexp)
+ (setq lastwin current n (1+ n)))))
+ (rmail-show-message lastwin)
+ (if (< n 0)
+ (message "No previous message with labels %s" labels))
+ (if (> n 0)
+ (message "No following message with labels %s" labels))))
+
+;;; Manipulate the file's Labels option.
+
+;; Return a list of symbols for all
+;; the keywords (labels) recorded in this file's Labels option.
+(defun rmail-keywords ()
+ (or rmail-keywords (rmail-parse-file-keywords)))
+
+;; Set rmail-keywords to a list of symbols for all
+;; the keywords (labels) recorded in this file's Labels option.
+(defun rmail-parse-file-keywords ()
+ (save-restriction
+ (save-excursion
+ (widen)
+ (goto-char 1)
+ (setq rmail-keywords
+ (if (search-forward "\nLabels:" (rmail-msgbeg 1) t)
+ (progn
+ (narrow-to-region (point) (progn (end-of-line) (point)))
+ (goto-char (point-min))
+ (cons 'rmail-keywords
+ (mapcar 'rmail-force-make-label
+ (mail-parse-comma-list)))))))))
+
+;; Add WORD to the list in the file's Labels option.
+;; Any keyword used for the first time needs this done.
+(defun rmail-install-keyword (word)
+ (let ((keyword (rmail-make-label word t))
+ (keywords (rmail-keywords)))
+ (if (not (or (rmail-attribute-p keyword)
+ (rmail-keyword-p keyword)))
+ (let ((omin (- (buffer-size) (point-min)))
+ (omax (- (buffer-size) (point-max))))
+ (unwind-protect
+ (save-excursion
+ (widen)
+ (goto-char 1)
+ (let ((case-fold-search t)
+ (buffer-read-only nil))
+ (or (search-forward "\nLabels:" nil t)
+ (progn
+ (end-of-line)
+ (insert "\nLabels:")))
+ (delete-region (point) (progn (end-of-line) (point)))
+ (setcdr keywords (cons keyword (cdr keywords)))
+ (while (setq keywords (cdr keywords))
+ (insert (symbol-name (car keywords)) ","))
+ (delete-char -1)))
+ (narrow-to-region (- (buffer-size) omin)
+ (- (buffer-size) omax)))))
+ keyword))
diff --git a/lisp/makesum.el b/lisp/makesum.el
new file mode 100644
index 00000000000..425895919af
--- /dev/null
+++ b/lisp/makesum.el
@@ -0,0 +1,100 @@
+;; Generate key binding summary for Emacs
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defun make-command-summary ()
+ "Make a summary of current key bindings in the buffer *Summary*.
+Previous contents of that buffer are killed first."
+ (interactive)
+ (message "Making command summary...")
+ ;; This puts a description of bindings in a buffer called *Help*.
+ (save-window-excursion
+ (describe-bindings))
+ (with-output-to-temp-buffer "*Summary*"
+ (save-excursion
+ (let ((cur-mode mode-name))
+ (set-buffer standard-output)
+ (erase-buffer)
+ (insert-buffer-substring "*Help*")
+ (goto-char (point-min))
+ (delete-region (point) (progn (forward-line 1) (point)))
+ (while (search-forward " " nil t)
+ (replace-match " "))
+ (goto-char (point-min))
+ (while (search-forward "-@ " nil t)
+ (replace-match "-SP"))
+ (goto-char (point-min))
+ (while (search-forward " .. ~ " nil t)
+ (replace-match "SP .. ~"))
+ (goto-char (point-min))
+ (while (search-forward "C-?" nil t)
+ (replace-match "DEL"))
+ (goto-char (point-min))
+ (while (search-forward "C-i" nil t)
+ (replace-match "TAB"))
+ (goto-char (point-min))
+ (if (re-search-forward "^Local Bindings:" nil t)
+ (progn
+ (forward-char -1)
+ (insert " for " cur-mode " Mode")
+ (while (search-forward "??\n" nil t)
+ (delete-region (point)
+ (progn
+ (forward-line -1)
+ (point))))))
+ (goto-char (point-min))
+ (insert "Emacs command summary, " (substring (current-time-string) 0 10)
+ ".\n")
+ ;; Delete "key binding" and underlining of dashes.
+ (delete-region (point) (progn (forward-line 2) (point)))
+ (forward-line 1) ;Skip blank line
+ (while (not (eobp))
+ (let ((beg (point)))
+ (or (re-search-forward "^$" nil t)
+ (goto-char (point-max)))
+ (double-column beg (point))
+ (forward-line 1)))
+ (goto-char (point-min)))))
+ (message "Making command summary...done"))
+
+(defun double-column (start end)
+ (interactive "r")
+ (let (half cnt
+ line lines nlines
+ (from-end (- (point-max) end)))
+ (setq nlines (count-lines start end))
+ (if (<= nlines 1)
+ nil
+ (setq half (/ (1+ nlines) 2))
+ (goto-char start)
+ (save-excursion
+ (forward-line half)
+ (while (< half nlines)
+ (setq half (1+ half))
+ (setq line (buffer-substring (point) (save-excursion (end-of-line) (point))))
+ (setq lines (cons line lines))
+ (delete-region (point) (progn (forward-line 1) (point)))))
+ (setq lines (nreverse lines))
+ (while lines
+ (end-of-line)
+ (indent-to 41)
+ (insert (car lines))
+ (forward-line 1)
+ (setq lines (cdr lines))))
+ (goto-char (- (point-max) from-end))))
diff --git a/lisp/novice.el b/lisp/novice.el
new file mode 100644
index 00000000000..a0417f14ef2
--- /dev/null
+++ b/lisp/novice.el
@@ -0,0 +1,105 @@
+;; Handling of disabled commands ("novice mode") for Emacs.
+;; Copyright (C) 1985, 1986, 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;; This function is called (by autoloading)
+;; to handle any disabled command.
+;; The command is found in this-command
+;; and the keys are returned by (this-command-keys).
+
+(defun disabled-command-hook (&rest ignore)
+ (let (char)
+ (save-window-excursion
+ (with-output-to-temp-buffer "*Help*"
+ (if (= (aref (this-command-keys) 0) ?\M-x)
+ (princ "You have invoked the disabled command ")
+ (princ "You have typed ")
+ (princ (key-description (this-command-keys)))
+ (princ ", invoking disabled command "))
+ (princ this-command)
+ (princ ":\n")
+ ;; Print any special message saying why the command is disabled.
+ (if (stringp (get this-command 'disabled))
+ (princ (get this-command 'disabled)))
+ (princ (or (condition-case ()
+ (documentation this-command)
+ (error nil))
+ "<< not documented >>"))
+ ;; Keep only the first paragraph of the documentation.
+ (save-excursion
+ (set-buffer "*Help*")
+ (goto-char (point-min))
+ (if (search-forward "\n\n" nil t)
+ (delete-region (1- (point)) (point-max))
+ (goto-char (point-max))))
+ (princ "\n\n")
+ (princ "You can now type
+Space to try the command just this once,
+ but leave it disabled,
+Y to try it and enable it (no questions if you use it again),
+N to do nothing (command remains disabled)."))
+ (message "Type y, n or Space: ")
+ (let ((cursor-in-echo-area t))
+ (while (not (memq (setq char (downcase (read-char)))
+ '(? ?y ?n)))
+ (ding)
+ (message "Please type y, n or Space: "))))
+ (if (= char ?y)
+ (if (y-or-n-p "Enable command for future editing sessions also? ")
+ (enable-command this-command)
+ (put this-command 'disabled nil)))
+ (if (/= char ?n)
+ (call-interactively this-command))))
+
+(defun enable-command (command)
+ "Allow COMMAND to be executed without special confirmation from now on.
+The user's .emacs file is altered so that this will apply
+to future sessions."
+ (interactive "CEnable command: ")
+ (put command 'disabled nil)
+ (save-excursion
+ (set-buffer (find-file-noselect (substitute-in-file-name "~/.emacs")))
+ (goto-char (point-min))
+ (if (search-forward (concat "(put '" (symbol-name command) " ") nil t)
+ (delete-region
+ (progn (beginning-of-line) (point))
+ (progn (forward-line 1) (point)))
+ ;; Must have been disabled by default.
+ (goto-char (point-max))
+ (insert "\n(put '" (symbol-name command) " 'disabled nil)\n"))
+ (setq foo (buffer-modified-p))
+ (save-buffer)))
+
+(defun disable-command (command)
+ "Require special confirmation to execute COMMAND from now on.
+The user's .emacs file is altered so that this will apply
+to future sessions."
+ (interactive "CDisable command: ")
+ (put command 'disabled t)
+ (save-excursion
+ (set-buffer (find-file-noselect (substitute-in-file-name "~/.emacs")))
+ (goto-char (point-min))
+ (if (search-forward (concat "(put '" (symbol-name command) " ") nil t)
+ (delete-region
+ (progn (beginning-of-line) (point))
+ (progn (forward-line 1) (point))))
+ (goto-char (point-max))
+ (insert "(put '" (symbol-name command) " 'disabled t)\n")
+ (save-buffer)))
+
diff --git a/lisp/play/dissociate.el b/lisp/play/dissociate.el
new file mode 100644
index 00000000000..b6ac2fa4ea8
--- /dev/null
+++ b/lisp/play/dissociate.el
@@ -0,0 +1,87 @@
+;; Scramble text amusingly for Emacs.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defun dissociated-press (&optional arg)
+ "Dissociate the text of the current buffer.
+Output goes in buffer named *Dissociation*,
+which is redisplayed each time text is added to it.
+Every so often the user must say whether to continue.
+If ARG is positive, require ARG chars of continuity.
+If ARG is negative, require -ARG words of continuity.
+Default is 2."
+ (interactive "P")
+ (setq arg (if arg (prefix-numeric-value arg) 2))
+ (let* ((inbuf (current-buffer))
+ (outbuf (get-buffer-create "*Dissociation*"))
+ (move-function (if (> arg 0) 'forward-char 'forward-word))
+ (move-amount (if (> arg 0) arg (- arg)))
+ (search-function (if (> arg 0) 'search-forward 'word-search-forward))
+ (last-query-point 0))
+ (switch-to-buffer outbuf)
+ (erase-buffer)
+ (while
+ (save-excursion
+ (goto-char last-query-point)
+ (vertical-motion (- (window-height) 4))
+ (or (= (point) (point-max))
+ (and (progn (goto-char (point-max))
+ (y-or-n-p "Continue dissociation? "))
+ (progn
+ (message "")
+ (recenter 1)
+ (setq last-query-point (point-max))
+ t))))
+ (let (start end)
+ (save-excursion
+ (set-buffer inbuf)
+ (setq start (point))
+ (if (eq move-function 'forward-char)
+ (progn
+ (setq end (+ start (+ move-amount (random 16))))
+ (if (> end (point-max))
+ (setq end (+ 1 move-amount (random 16))))
+ (goto-char end))
+ (funcall move-function
+ (+ move-amount (random 16))))
+ (setq end (point)))
+ (let ((opoint (point)))
+ (insert-buffer-substring inbuf start end)
+ (save-excursion
+ (goto-char opoint)
+ (end-of-line)
+ (and (> (current-column) fill-column)
+ (do-auto-fill)))))
+ (save-excursion
+ (set-buffer inbuf)
+ (if (eobp)
+ (goto-char (point-min))
+ (let ((overlap
+ (buffer-substring (prog1 (point)
+ (funcall move-function
+ (- move-amount)))
+ (point))))
+ (let (ranval)
+ (while (< (setq ranval (random)) 0))
+ (goto-char (1+ (% ranval (1- (point-max))))))
+ (or (funcall search-function overlap nil t)
+ (let ((opoint (point)))
+ (goto-char 1)
+ (funcall search-function overlap opoint t))))))
+ (sit-for 0))))
diff --git a/lisp/play/gomoku.el b/lisp/play/gomoku.el
new file mode 100644
index 00000000000..4c7620f5705
--- /dev/null
+++ b/lisp/play/gomoku.el
@@ -0,0 +1,1166 @@
+;; Gomoku game between you and Emacs
+;; Copyright (C) 1988 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Gomoku game between you and GNU Emacs. Last modified on 13 Sep 1988
+;;;
+;;; Written by Ph. Schnoebelen (phs@lifia.imag.fr), 1987, 1988
+;;; with precious advices from J.-F. Rit.
+;;; This has been tested with GNU Emacs 18.50.
+
+(provide 'gomoku)
+
+
+;; RULES:
+;;
+;; Gomoku is a game played between two players on a rectangular board. Each
+;; player, in turn, marks a free square of its choice. The winner is the first
+;; one to mark five contiguous squares in any direction (horizontally,
+;; vertically or diagonally).
+;;
+;; I have been told that, in "The TRUE Gomoku", some restrictions are made
+;; about the squares where one may play, or else there is a known forced win
+;; for the first player. This program has no such restriction, but it does not
+;; know about the forced win, nor do I. Furthermore, you probably do not know
+;; it yourself :-).
+
+
+;; HOW TO INSTALL:
+;;
+;; There is nothing specific w.r.t. installation: just put this file in the
+;; lisp directory and add an autoload for command gomoku in site-init.el. If
+;; you don't want to rebuild Emacs, then every single user interested in
+;; Gomoku will have to put the autoload command in its .emacs file. Another
+;; possibility is to define in your .emacs some command using (require
+;; 'gomoku).
+;;
+;; The most important thing is to BYTE-COMPILE gomoku.el because it is
+;; important that the code be as fast as possible.
+;;
+;; There are two main places where you may want to customize the program: key
+;; bindings and board display. These features are commented in the code. Go
+;; and see.
+
+
+;; HOW TO USE:
+;;
+;; Once this file has been installed, the command "M-x gomoku" will display a
+;; board, the size of which depends on the size of the current window. The
+;; size of the board is easily modified by giving numeric arguments to the
+;; gomoku command and/or by customizing the displaying parameters.
+;;
+;; Emacs plays when it is its turn. When it is your turn, just put the cursor
+;; on the square where you want to play and hit RET, or X, or whatever key you
+;; bind to the command gomoku-human-plays. When it is your turn, Emacs is
+;; idle: you may switch buffers, read your mail, ... Just come back to the
+;; *Gomoku* buffer and resume play.
+
+
+;; ALGORITHM:
+;;
+;; The algorithm is briefly described in section "THE SCORE TABLE". Some
+;; parameters may be modified if you want to change the style exhibited by the
+;; program.
+
+;;;
+;;; GOMOKU MODE AND KEYMAP.
+;;;
+(defvar gomoku-mode-hook nil
+ "If non-nil, its value is called on entry to Gomoku mode.")
+
+(defvar gomoku-mode-map nil
+ "Local keymap to use in Gomoku mode.")
+
+(if gomoku-mode-map nil
+ (setq gomoku-mode-map (make-sparse-keymap))
+
+ ;; Key bindings for cursor motion. Arrow keys are just "function"
+ ;; keys, see below.
+ (define-key gomoku-mode-map "y" 'gomoku-move-nw) ; Y
+ (define-key gomoku-mode-map "u" 'gomoku-move-ne) ; U
+ (define-key gomoku-mode-map "b" 'gomoku-move-sw) ; B
+ (define-key gomoku-mode-map "n" 'gomoku-move-se) ; N
+ (define-key gomoku-mode-map "h" 'gomoku-move-left) ; H
+ (define-key gomoku-mode-map "l" 'gomoku-move-right) ; L
+ (define-key gomoku-mode-map "j" 'gomoku-move-down) ; J
+ (define-key gomoku-mode-map "k" 'gomoku-move-up) ; K
+ (define-key gomoku-mode-map "\C-n" 'gomoku-move-down) ; C-N
+ (define-key gomoku-mode-map "\C-p" 'gomoku-move-up) ; C-P
+ (define-key gomoku-mode-map "\C-f" 'gomoku-move-right) ; C-F
+ (define-key gomoku-mode-map "\C-b" 'gomoku-move-left) ; C-B
+
+ ;; Key bindings for entering Human moves.
+ ;; If you have a mouse, you may also bind some mouse click ...
+ (define-key gomoku-mode-map "X" 'gomoku-human-plays) ; X
+ (define-key gomoku-mode-map "x" 'gomoku-human-plays) ; x
+ (define-key gomoku-mode-map "\C-m" 'gomoku-human-plays) ; RET
+ (define-key gomoku-mode-map "\C-cp" 'gomoku-human-plays) ; C-C P
+ (define-key gomoku-mode-map "\C-cb" 'gomoku-human-takes-back) ; C-C B
+ (define-key gomoku-mode-map "\C-cr" 'gomoku-human-resigns) ; C-C R
+ (define-key gomoku-mode-map "\C-ce" 'gomoku-emacs-plays) ; C-C E
+
+ ;; Key bindings for "function" keys. If your terminal has such
+ ;; keys, make sure they are declared through the function-keymap
+ ;; keymap (see file keypad.el).
+ ;; One problem with keypad.el is that the function-key-sequence
+ ;; function is really slow, so slow that you may want to comment out
+ ;; the following lines ...
+ (if (featurep 'keypad)
+ (let (keys)
+ (if (setq keys (function-key-sequence ?u)) ; Up Arrow
+ (define-key gomoku-mode-map keys 'gomoku-move-up))
+ (if (setq keys (function-key-sequence ?d)) ; Down Arrow
+ (define-key gomoku-mode-map keys 'gomoku-move-down))
+ (if (setq keys (function-key-sequence ?l)) ; Left Arrow
+ (define-key gomoku-mode-map keys 'gomoku-move-left))
+ (if (setq keys (function-key-sequence ?r)) ; Right Arrow
+ (define-key gomoku-mode-map keys 'gomoku-move-right))
+;; (if (setq keys (function-key-sequence ?e)) ; Enter
+;; (define-key gomoku-mode-map keys 'gomoku-human-plays))
+;; (if (setq keys (function-key-sequence ?I)) ; Insert
+;; (define-key gomoku-mode-map keys 'gomoku-human-plays))
+ )))
+
+
+
+(defun gomoku-mode ()
+ "Major mode for playing Gomoku against Emacs.
+You and Emacs play in turn by marking a free square. You mark it with X
+and Emacs marks it with O. The winner is the first to get five contiguous
+marks horizontally, vertically or in diagonal.
+You play by moving the cursor over the square you choose and hitting RET,
+x, .. or whatever has been set locally.
+
+Other useful commands:
+
+C-c r Indicate that you resign,
+C-c t Take back your last move,
+C-c e Ask for Emacs to play (thus passing).
+
+Commands:
+\\{gomoku-mode-map}
+Entry to this mode calls the value of gomoku-mode-hook
+if that value is non-nil."
+ (interactive)
+ (setq major-mode 'gomoku-mode
+ mode-name "Gomoku")
+ (gomoku-display-statistics)
+ (use-local-map gomoku-mode-map)
+ (run-hooks 'gomoku-mode-hook))
+
+;;;
+;;; THE BOARD.
+;;;
+
+;; The board is a rectangular grid. We code empty squares with 0, X's with 1
+;; and O's with 6. The rectangle is recorded in a one dimensional vector
+;; containing padding squares (coded with -1). These squares allow us to
+;; detect when we are trying to move out of the board. We denote a square by
+;; its (X,Y) coords, or by the INDEX corresponding to them in the vector. The
+;; leftmost topmost square has coords (1,1) and index gomoku-board-width + 2.
+;; Similarly, vectors between squares may be given by two DX, DY coords or by
+;; one DEPL (the difference between indexes).
+
+(defvar gomoku-board-width nil
+ "Number of columns on the Gomoku board.")
+
+(defvar gomoku-board-height nil
+ "Number of lines on the Gomoku board.")
+
+(defvar gomoku-board nil
+ "Vector recording the actual state of the Gomoku board.")
+
+(defvar gomoku-vector-length nil
+ "Length of gomoku-board vector.")
+
+(defvar gomoku-draw-limit nil
+ ;; This is usually set to 70% of the number of squares.
+ "After how many moves will Emacs offer a draw ?")
+
+
+(defun gomoku-xy-to-index (x y)
+ "Translate X, Y cartesian coords into the corresponding board index."
+ (+ (* y gomoku-board-width) x y))
+
+(defun gomoku-index-to-x (index)
+ "Return corresponding x-coord of board INDEX."
+ (% index (1+ gomoku-board-width)))
+
+(defun gomoku-index-to-y (index)
+ "Return corresponding y-coord of board INDEX."
+ (/ index (1+ gomoku-board-width)))
+
+(defun gomoku-init-board ()
+ "Create the gomoku-board vector and fill it with initial values."
+ (setq gomoku-board (make-vector gomoku-vector-length 0))
+ ;; Every square is 0 (i.e. empty) except padding squares:
+ (let ((i 0) (ii (1- gomoku-vector-length)))
+ (while (<= i gomoku-board-width) ; The squares in [0..width] and in
+ (aset gomoku-board i -1) ; [length - width - 1..length - 1]
+ (aset gomoku-board ii -1) ; are padding squares.
+ (setq i (1+ i)
+ ii (1- ii))))
+ (let ((i 0))
+ (while (< i gomoku-vector-length)
+ (aset gomoku-board i -1) ; and also all k*(width+1)
+ (setq i (+ i gomoku-board-width 1)))))
+
+;;;
+;;; THE SCORE TABLE.
+;;;
+
+;; Every (free) square has a score associated to it, recorded in the
+;; GOMOKU-SCORE-TABLE vector. The program always plays in the square having
+;; the highest score.
+
+(defvar gomoku-score-table nil
+ "Vector recording the actual score of the free squares.")
+
+
+;; The key point point about the algorithm is that, rather than considering
+;; the board as just a set of squares, we prefer to see it as a "space" of
+;; internested 5-tuples of contiguous squares (called qtuples).
+;;
+;; The aim of the program is to fill one qtuple with its O's while preventing
+;; you from filling another one with your X's. To that effect, it computes a
+;; score for every qtuple, with better qtuples having better scores. Of
+;; course, the score of a qtuple (taken in isolation) is just determined by
+;; its contents as a set, i.e. not considering the order of its elements. The
+;; highest score is given to the "OOOO" qtuples because playing in such a
+;; qtuple is winning the game. Just after this comes the "XXXX" qtuple because
+;; not playing in it is just loosing the game, and so on. Note that a
+;; "polluted" qtuple, i.e. one containing at least one X and at least one O,
+;; has score zero because there is no more any point in playing in it, from
+;; both an attacking and a defending point of view.
+;;
+;; Given the score of every qtuple, the score of a given free square on the
+;; board is just the sum of the scores of all the qtuples to which it belongs,
+;; because playing in that square is playing in all its containing qtuples at
+;; once. And it is that function which takes into account the internesting of
+;; the qtuples.
+;;
+;; This algorithm is rather simple but anyway it gives a not so dumb level of
+;; play. It easily extends to "n-dimensional Gomoku", where a win should not
+;; be obtained with as few as 5 contiguous marks: 6 or 7 (depending on n !)
+;; should be preferred.
+
+
+;; Here are the scores of the nine "non-polluted" configurations. Tuning
+;; these values will change (hopefully improve) the strength of the program
+;; and may change its style (rather aggressive here).
+
+(defconst nil-score 7 "Score of an empty qtuple.")
+(defconst Xscore 15 "Score of a qtuple containing one X.")
+(defconst XXscore 400 "Score of a qtuple containing two X's.")
+(defconst XXXscore 1800 "Score of a qtuple containing three X's.")
+(defconst XXXXscore 100000 "Score of a qtuple containing four X's.")
+(defconst Oscore 35 "Score of a qtuple containing one O.")
+(defconst OOscore 800 "Score of a qtuple containing two O's.")
+(defconst OOOscore 15000 "Score of a qtuple containing three O's.")
+(defconst OOOOscore 800000 "Score of a qtuple containing four O's.")
+
+;; These values are not just random: if, given the following situation:
+;;
+;; . . . . . . . O .
+;; . X X a . . . X .
+;; . . . X . . . X .
+;; . . . X . . . X .
+;; . . . . . . . b .
+;;
+;; you want Emacs to play in "a" and not in "b", then the parameters must
+;; satisfy the inequality:
+;;
+;; 6 * XXscore > XXXscore + XXscore
+;;
+;; because "a" mainly belongs to six "XX" qtuples (the others are less
+;; important) while "b" belongs to one "XXX" and one "XX" qtuples. Other
+;; conditions are required to obtain sensible moves, but the previous example
+;; should illustrate the point. If you manage to improve on these values,
+;; please send me a note. Thanks.
+
+
+;; As we choosed values 0, 1 and 6 to denote empty, X and O squares, the
+;; contents of a qtuple is uniquely determined by the sum of its elements and
+;; we just have to set up a translation table.
+
+(defconst gomoku-score-trans-table
+ (vector nil-score Xscore XXscore XXXscore XXXXscore 0
+ Oscore 0 0 0 0 0
+ OOscore 0 0 0 0 0
+ OOOscore 0 0 0 0 0
+ OOOOscore 0 0 0 0 0
+ 0)
+ "Vector associating qtuple contents to their score.")
+
+
+;; If you do not modify drastically the previous constants, the only way for a
+;; square to have a score higher than OOOOscore is to belong to a "OOOO"
+;; qtuple, thus to be a winning move. Similarly, the only way for a square to
+;; have a score between XXXXscore and OOOOscore is to belong to a "XXXX"
+;; qtuple. We may use these considerations to detect when a given move is
+;; winning or loosing.
+
+(defconst gomoku-winning-threshold OOOOscore
+ "Threshold score beyond which an emacs move is winning.")
+
+(defconst gomoku-loosing-threshold XXXXscore
+ "Threshold score beyond which a human move is winning.")
+
+
+(defun gomoku-strongest-square ()
+ "Compute index of free square with highest score, or nil if none."
+ ;; We just have to loop other all squares. However there are two problems:
+ ;; 1/ The SCORE-TABLE only gives correct scores to free squares. To speed
+ ;; up future searches, we set the score of padding or occupied squares
+ ;; to -1 whenever we meet them.
+ ;; 2/ We want to choose randomly between equally good moves.
+ (let ((score-max 0)
+ (count 0) ; Number of equally good moves
+ (square (gomoku-xy-to-index 1 1)) ; First square
+ (end (gomoku-xy-to-index gomoku-board-width gomoku-board-height))
+ best-square score)
+ (while (<= square end)
+ (cond
+ ;; If score is lower (i.e. most of the time), skip to next:
+ ((< (aref gomoku-score-table square) score-max))
+ ;; If score is better, beware of non free squares:
+ ((> (setq score (aref gomoku-score-table square)) score-max)
+ (if (zerop (aref gomoku-board square)) ; is it free ?
+ (setq count 1 ; yes: take it !
+ best-square square
+ score-max score)
+ (aset gomoku-score-table square -1))) ; no: kill it !
+ ;; If score is equally good, choose randomly. But first check freeness:
+ ((not (zerop (aref gomoku-board square)))
+ (aset gomoku-score-table square -1))
+ ((= count (random-number (setq count (1+ count))))
+ (setq best-square square
+ score-max score)))
+ (setq square (1+ square))) ; try next square
+ best-square))
+
+(defun random-number (n)
+ "Return a random integer between 0 and N-1 inclusive."
+ (setq n (% (random) n))
+ (if (< n 0) (- n) n))
+
+;;;
+;;; INITIALIZING THE SCORE TABLE.
+;;;
+
+;; At initialization the board is empty so that every qtuple amounts for
+;; nil-score. Therefore, the score of any square is nil-score times the number
+;; of qtuples that pass through it. This number is 3 in a corner and 20 if you
+;; are sufficiently far from the sides. As computing the number is time
+;; consuming, we initialize every square with 20*nil-score and then only
+;; consider squares at less than 5 squares from one side. We speed this up by
+;; taking symmetry into account.
+;; Also, as it is likely that successive games will be played on a board with
+;; same size, it is a good idea to save the initial SCORE-TABLE configuration.
+
+(defvar gomoku-saved-score-table nil
+ "Recorded initial value of previous score table.")
+
+(defvar gomoku-saved-board-width nil
+ "Recorded value of previous board width.")
+
+(defvar gomoku-saved-board-height nil
+ "Recorded value of previous board height.")
+
+
+(defun gomoku-init-score-table ()
+ "Create the score table vector and fill it with initial values."
+ (if (and gomoku-saved-score-table ; Has it been stored last time ?
+ (= gomoku-board-width gomoku-saved-board-width)
+ (= gomoku-board-height gomoku-saved-board-height))
+ (setq gomoku-score-table (copy-sequence gomoku-saved-score-table))
+ ;; No, compute it:
+ (setq gomoku-score-table
+ (make-vector gomoku-vector-length (* 20 nil-score)))
+ (let (i j maxi maxj maxi2 maxj2)
+ (setq maxi (/ (1+ gomoku-board-width) 2)
+ maxj (/ (1+ gomoku-board-height) 2)
+ maxi2 (min 4 maxi)
+ maxj2 (min 4 maxj))
+ ;; We took symmetry into account and could use it more if the board
+ ;; would have been square and not rectangular !
+ ;; In our case we deal with all (i,j) in the set [1..maxi2]*[1..maxj] U
+ ;; [maxi2+1..maxi]*[1..maxj2]. Maxi2 and maxj2 are used because the
+ ;; board may well be less than 8 by 8 !
+ (setq i 1)
+ (while (<= i maxi2)
+ (setq j 1)
+ (while (<= j maxj)
+ (gomoku-init-square-score i j)
+ (setq j (1+ j)))
+ (setq i (1+ i)))
+ (while (<= i maxi)
+ (setq j 1)
+ (while (<= j maxj2)
+ (gomoku-init-square-score i j)
+ (setq j (1+ j)))
+ (setq i (1+ i))))
+ (setq gomoku-saved-score-table (copy-sequence gomoku-score-table)
+ gomoku-saved-board-width gomoku-board-width
+ gomoku-saved-board-height gomoku-board-height)))
+
+(defun gomoku-nb-qtuples (i j)
+ "Return the number of qtuples containing square I,J."
+ ;; This fonction is complicated because we have to deal
+ ;; with ugly cases like 3 by 6 boards, but it works.
+ ;; If you have a simpler (and correct) solution, send it to me. Thanks !
+ (let ((left (min 4 (1- i)))
+ (right (min 4 (- gomoku-board-width i)))
+ (up (min 4 (1- j)))
+ (down (min 4 (- gomoku-board-height j))))
+ (+ -12
+ (min (max (+ left right) 3) 8)
+ (min (max (+ up down) 3) 8)
+ (min (max (+ (min left up) (min right down)) 3) 8)
+ (min (max (+ (min right up) (min left down)) 3) 8))))
+
+(defun gomoku-init-square-score (i j)
+ "Give initial score to square I,J and to its mirror images."
+ (let ((ii (1+ (- gomoku-board-width i)))
+ (jj (1+ (- gomoku-board-height j)))
+ (sc (* (gomoku-nb-qtuples i j) (aref gomoku-score-trans-table 0))))
+ (aset gomoku-score-table (gomoku-xy-to-index i j) sc)
+ (aset gomoku-score-table (gomoku-xy-to-index ii j) sc)
+ (aset gomoku-score-table (gomoku-xy-to-index i jj) sc)
+ (aset gomoku-score-table (gomoku-xy-to-index ii jj) sc)))
+
+;;;
+;;; MAINTAINING THE SCORE TABLE.
+;;;
+
+;; We do not provide functions for computing the SCORE-TABLE given the
+;; contents of the BOARD. This would involve heavy nested loops, with time
+;; proportional to the size of the board. It is better to update the
+;; SCORE-TABLE after each move. Updating needs not modify more than 36
+;; squares: it is done in constant time.
+
+(defun gomoku-update-score-table (square dval)
+ "Update score table after SQUARE received a DVAL increment."
+ ;; The board has already been updated when this function is called.
+ ;; Updating scores is done by looking for qtuples boundaries in all four
+ ;; directions and then calling update-score-in-direction.
+ ;; Finally all squares received the right increment, and then are up to
+ ;; date, except possibly for SQUARE itself if we are taking a move back for
+ ;; its score had been set to -1 at the time.
+ (let* ((x (gomoku-index-to-x square))
+ (y (gomoku-index-to-y square))
+ (imin (max -4 (- 1 x)))
+ (jmin (max -4 (- 1 y)))
+ (imax (min 0 (- gomoku-board-width x 4)))
+ (jmax (min 0 (- gomoku-board-height y 4))))
+ (gomoku-update-score-in-direction imin imax
+ square 1 0 dval)
+ (gomoku-update-score-in-direction jmin jmax
+ square 0 1 dval)
+ (gomoku-update-score-in-direction (max imin jmin) (min imax jmax)
+ square 1 1 dval)
+ (gomoku-update-score-in-direction (max (- 1 y) -4
+ (- x gomoku-board-width))
+ (min 0 (- x 5)
+ (- gomoku-board-height y 4))
+ square -1 1 dval)))
+
+(defun gomoku-update-score-in-direction (left right square dx dy dval)
+ "Update scores for all squares in the qtuples starting between the LEFTth
+square and the RIGHTth after SQUARE, along the DX, DY direction, considering
+that DVAL has been added on SQUARE."
+ ;; We always have LEFT <= 0, RIGHT <= 0 and DEPL > 0 but we may very well
+ ;; have LEFT > RIGHT, indicating that no qtuple contains SQUARE along that
+ ;; DX,DY direction.
+ (cond
+ ((> left right)) ; Quit
+ (t ; Else ..
+ (let (depl square0 square1 square2 count delta)
+ (setq depl (gomoku-xy-to-index dx dy)
+ square0 (+ square (* left depl))
+ square1 (+ square (* right depl))
+ square2 (+ square0 (* 4 depl)))
+ ;; Compute the contents of the first qtuple:
+ (setq square square0
+ count 0)
+ (while (<= square square2)
+ (setq count (+ count (aref gomoku-board square))
+ square (+ square depl)))
+ (while (<= square0 square1)
+ ;; Update the squares of the qtuple beginning in SQUARE0 and ending
+ ;; in SQUARE2.
+ (setq delta (- (aref gomoku-score-trans-table count)
+ (aref gomoku-score-trans-table (- count dval))))
+ (cond ((not (zerop delta)) ; or else nothing to update
+ (setq square square0)
+ (while (<= square square2)
+ (if (zerop (aref gomoku-board square)) ; only for free squares
+ (aset gomoku-score-table square
+ (+ (aref gomoku-score-table square) delta)))
+ (setq square (+ square depl)))))
+ ;; Then shift the qtuple one square along DEPL, this only requires
+ ;; modifying SQUARE0 and SQUARE2.
+ (setq square2 (+ square2 depl)
+ count (+ count (- (aref gomoku-board square0))
+ (aref gomoku-board square2))
+ square0 (+ square0 depl)))))))
+
+;;;
+;;; GAME CONTROL.
+;;;
+
+;; Several variables are used to monitor a game, including a GAME-HISTORY (the
+;; list of all (SQUARE . PREVSCORE) played) that allows to take moves back
+;; (anti-updating the score table) and to compute the table from scratch in
+;; case of an interruption.
+
+(defvar gomoku-game-in-progress nil
+ "Non-nil if a game is in progress.")
+
+(defvar gomoku-game-history nil
+ "A record of all moves that have been played during current game.")
+
+(defvar gomoku-number-of-moves nil
+ "Number of moves already played in current game.")
+
+(defvar gomoku-number-of-human-moves nil
+ "Number of moves already played by human in current game.")
+
+(defvar gomoku-emacs-played-first nil
+ "Non-nil if Emacs played first.")
+
+(defvar gomoku-human-took-back nil
+ "Non-nil if Human took back a move during the game.")
+
+(defvar gomoku-human-refused-draw nil
+ "Non-nil if Human refused Emacs offer of a draw.")
+
+(defvar gomoku-emacs-is-computing nil
+ ;; This is used to detect interruptions. Hopefully, it should not be needed.
+ "Non-nil if Emacs is in the middle of a computation.")
+
+
+(defun gomoku-start-game (n m)
+ "Initialize a new game on an N by M board."
+ (setq gomoku-emacs-is-computing t) ; Raise flag
+ (setq gomoku-game-in-progress t)
+ (setq gomoku-board-width n
+ gomoku-board-height m
+ gomoku-vector-length (1+ (* (+ m 2) (1+ n)))
+ gomoku-draw-limit (/ (* 7 n m) 10))
+ (setq gomoku-game-history nil
+ gomoku-number-of-moves 0
+ gomoku-number-of-human-moves 0
+ gomoku-emacs-played-first nil
+ gomoku-human-took-back nil
+ gomoku-human-refused-draw nil)
+ (gomoku-init-display n m) ; Display first: the rest takes time
+ (gomoku-init-score-table) ; INIT-BOARD requires that the score
+ (gomoku-init-board) ; table be already created.
+ (setq gomoku-emacs-is-computing nil))
+
+(defun gomoku-play-move (square val &optional dont-update-score)
+ "Go to SQUARE, play VAL and update everything."
+ (setq gomoku-emacs-is-computing t) ; Raise flag
+ (cond ((= 1 val) ; a Human move
+ (setq gomoku-number-of-human-moves (1+ gomoku-number-of-human-moves)))
+ ((zerop gomoku-number-of-moves) ; an Emacs move. Is it first ?
+ (setq gomoku-emacs-played-first t)))
+ (setq gomoku-game-history
+ (cons (cons square (aref gomoku-score-table square))
+ gomoku-game-history)
+ gomoku-number-of-moves (1+ gomoku-number-of-moves))
+ (gomoku-plot-square square val)
+ (aset gomoku-board square val) ; *BEFORE* UPDATE-SCORE !
+ (if dont-update-score nil
+ (gomoku-update-score-table square val) ; previous val was 0: dval = val
+ (aset gomoku-score-table square -1))
+ (setq gomoku-emacs-is-computing nil))
+
+(defun gomoku-take-back ()
+ "Take back last move and update everything."
+ (setq gomoku-emacs-is-computing t)
+ (let* ((last-move (car gomoku-game-history))
+ (square (car last-move))
+ (oldval (aref gomoku-board square)))
+ (if (= 1 oldval)
+ (setq gomoku-number-of-human-moves (1- gomoku-number-of-human-moves)))
+ (setq gomoku-game-history (cdr gomoku-game-history)
+ gomoku-number-of-moves (1- gomoku-number-of-moves))
+ (gomoku-plot-square square 0)
+ (aset gomoku-board square 0) ; *BEFORE* UPDATE-SCORE !
+ (gomoku-update-score-table square (- oldval))
+ (aset gomoku-score-table square (cdr last-move)))
+ (setq gomoku-emacs-is-computing nil))
+
+;;;
+;;; SESSION CONTROL.
+;;;
+
+(defvar gomoku-number-of-wins 0
+ "Number of games already won in this session.")
+
+(defvar gomoku-number-of-losses 0
+ "Number of games already lost in this session.")
+
+(defvar gomoku-number-of-draws 0
+ "Number of games already drawn in this session.")
+
+
+(defun gomoku-terminate-game (result)
+ "Terminate the current game with RESULT."
+ (let (message)
+ (cond
+ ((eq result 'emacs-won)
+ (setq gomoku-number-of-wins (1+ gomoku-number-of-wins))
+ (setq message
+ (cond ((< gomoku-number-of-moves 20)
+ "This was a REALLY QUICK win.")
+ (gomoku-human-refused-draw
+ "I won... Too bad you refused my offer of a draw !")
+ (gomoku-human-took-back
+ "I won... Taking moves back will not help you !")
+ ((not gomoku-emacs-played-first)
+ "I won... Playing first did not help you much !")
+ ((and (zerop gomoku-number-of-losses)
+ (zerop gomoku-number-of-draws)
+ (> gomoku-number-of-wins 1))
+ "I'm becoming tired of winning...")
+ (t
+ "I won."))))
+ ((eq result 'human-won)
+ (setq gomoku-number-of-losses (1+ gomoku-number-of-losses))
+ (setq message
+ (cond
+ (gomoku-human-took-back
+ "OK, you won this one. I, for one, never take my moves back...")
+ (gomoku-emacs-played-first
+ "OK, you won this one... so what ?")
+ (t
+ "OK, you won this one. Now, let me play first just once."))))
+ ((eq result 'human-resigned)
+ (setq gomoku-number-of-wins (1+ gomoku-number-of-wins))
+ (setq message "So you resign... That's just one more win for me."))
+ ((eq result 'nobody-won)
+ (setq gomoku-number-of-draws (1+ gomoku-number-of-draws))
+ (setq message
+ (cond
+ (gomoku-human-took-back
+ "This is a draw. I, for one, never take my moves back...")
+ (gomoku-emacs-played-first
+ "This is a draw... Just chance, I guess.")
+ (t
+ "This is a draw. Now, let me play first just once."))))
+ ((eq result 'draw-agreed)
+ (setq gomoku-number-of-draws (1+ gomoku-number-of-draws))
+ (setq message
+ (cond
+ (gomoku-human-took-back
+ "Draw agreed. I, for one, never take my moves back...")
+ (gomoku-emacs-played-first
+ "Draw agreed. You were lucky.")
+ (t
+ "Draw agreed. Now, let me play first just once."))))
+ ((eq result 'crash-game)
+ (setq message
+ "Sorry, I have been interrupted and cannot resume that game...")))
+
+ (gomoku-display-statistics)
+ (if message (message message))
+ (ding)
+ (setq gomoku-game-in-progress nil)))
+
+(defun gomoku-crash-game ()
+ "What to do when Emacs detects it has been interrupted."
+ (setq gomoku-emacs-is-computing nil)
+ (gomoku-terminate-game 'crash-game)
+ (sit-for 4) ; Let's see the message
+ (gomoku-prompt-for-other-game))
+
+;;;
+;;; INTERACTIVE COMMANDS.
+;;;
+
+(defun gomoku (&optional n m)
+ "Start a Gomoku game between you and Emacs.
+If a game is in progress, this command allow you to resume it.
+If optional arguments N and M are given, an N by M board is used.
+
+You and Emacs play in turn by marking a free square. You mark it with X
+and Emacs marks it with O. The winner is the first to get five contiguous
+marks horizontally, vertically or in diagonal.
+You play by moving the cursor over the square you choose and hitting RET,
+x, .. or whatever has been set locally.
+Use C-h m for more info."
+ (interactive)
+ (gomoku-switch-to-window)
+ (cond
+ (gomoku-emacs-is-computing
+ (gomoku-crash-game))
+ ((not gomoku-game-in-progress)
+ (let ((max-width (gomoku-max-width))
+ (max-height (gomoku-max-height)))
+ (or n (setq n max-width))
+ (or m (setq m max-height))
+ (cond ((< n 1)
+ (error "I need at least 1 column"))
+ ((< m 1)
+ (error "I need at least 1 row"))
+ ((> n max-width)
+ (error "I cannot display %d columns in that window" n)))
+ (if (and (> m max-height)
+ (not (equal m gomoku-saved-board-height))
+ ;; Use EQUAL because SAVED-BOARD-HEIGHT may be nil
+ (not (y-or-n-p (format "Do you really want %d rows " m))))
+ (setq m max-height)))
+ (message "One moment, please...")
+ (gomoku-start-game n m)
+ (if (y-or-n-p "Do you allow me to play first ")
+ (gomoku-emacs-plays)
+ (gomoku-prompt-for-move)))
+ ((y-or-n-p "Shall we continue our game ")
+ (gomoku-prompt-for-move))
+ (t
+ (gomoku-human-resigns))))
+
+(defun gomoku-emacs-plays ()
+ "Compute Emacs next move and play it."
+ (interactive)
+ (gomoku-switch-to-window)
+ (cond
+ (gomoku-emacs-is-computing
+ (gomoku-crash-game))
+ ((not gomoku-game-in-progress)
+ (gomoku-prompt-for-other-game))
+ (t
+ (message "Let me think...")
+ (let (square score)
+ (setq square (gomoku-strongest-square))
+ (cond ((null square)
+ (gomoku-terminate-game 'nobody-won))
+ (t
+ (setq score (aref gomoku-score-table square))
+ (gomoku-play-move square 6)
+ (cond ((>= score gomoku-winning-threshold)
+ (gomoku-find-filled-qtuple square 6)
+ (gomoku-cross-winning-qtuple)
+ (gomoku-terminate-game 'emacs-won))
+ ((zerop score)
+ (gomoku-terminate-game 'nobody-won))
+ ((and (> gomoku-number-of-moves gomoku-draw-limit)
+ (not gomoku-human-refused-draw)
+ (gomoku-offer-a-draw))
+ (gomoku-terminate-game 'draw-agreed))
+ (t
+ (gomoku-prompt-for-move)))))))))
+
+(defun gomoku-human-plays ()
+ "Signal to the Gomoku program that you have played.
+You must have put the cursor on the square where you want to play.
+If the game is finished, this command requests for another game."
+ (interactive)
+ (gomoku-switch-to-window)
+ (cond
+ (gomoku-emacs-is-computing
+ (gomoku-crash-game))
+ ((not gomoku-game-in-progress)
+ (gomoku-prompt-for-other-game))
+ (t
+ (let (square score)
+ (setq square (gomoku-point-square))
+ (cond ((null square)
+ (error "Your point is not on a square. Retry !"))
+ ((not (zerop (aref gomoku-board square)))
+ (error "Your point is not on a free square. Retry !"))
+ (t
+ (setq score (aref gomoku-score-table square))
+ (gomoku-play-move square 1)
+ (cond ((and (>= score gomoku-loosing-threshold)
+ ;; Just testing SCORE > THRESHOLD is not enough for
+ ;; detecting wins, it just gives an indication that
+ ;; we confirm with GOMOKU-FIND-FILLED-QTUPLE.
+ (gomoku-find-filled-qtuple square 1))
+ (gomoku-cross-winning-qtuple)
+ (gomoku-terminate-game 'human-won))
+ (t
+ (gomoku-emacs-plays)))))))))
+
+(defun gomoku-human-takes-back ()
+ "Signal to the Gomoku program that you wish to take back your last move."
+ (interactive)
+ (gomoku-switch-to-window)
+ (cond
+ (gomoku-emacs-is-computing
+ (gomoku-crash-game))
+ ((not gomoku-game-in-progress)
+ (message "Too late for taking back...")
+ (sit-for 4)
+ (gomoku-prompt-for-other-game))
+ ((zerop gomoku-number-of-human-moves)
+ (message "You have not played yet... Your move ?"))
+ (t
+ (message "One moment, please...")
+ ;; It is possible for the user to let Emacs play several consecutive
+ ;; moves, so that the best way to know when to stop taking back moves is
+ ;; to count the number of human moves:
+ (setq gomoku-human-took-back t)
+ (let ((number gomoku-number-of-human-moves))
+ (while (= number gomoku-number-of-human-moves)
+ (gomoku-take-back)))
+ (gomoku-prompt-for-move))))
+
+(defun gomoku-human-resigns ()
+ "Signal to the Gomoku program that you may want to resign."
+ (interactive)
+ (gomoku-switch-to-window)
+ (cond
+ (gomoku-emacs-is-computing
+ (gomoku-crash-game))
+ ((not gomoku-game-in-progress)
+ (message "There is no game in progress"))
+ ((y-or-n-p "You mean, you resign ")
+ (gomoku-terminate-game 'human-resigned))
+ ((y-or-n-p "You mean, we continue ")
+ (gomoku-prompt-for-move))
+ (t
+ (gomoku-terminate-game 'human-resigned)))) ; OK. Accept it
+
+;;;
+;;; PROMPTING THE HUMAN PLAYER.
+;;;
+
+(defun gomoku-prompt-for-move ()
+ "Display a message asking for Human's move."
+ (message (if (zerop gomoku-number-of-human-moves)
+ "Your move ? (move to a free square and hit X, RET ...)"
+ "Your move ?"))
+ ;; This may seem silly, but if one omits the following line (or a similar
+ ;; one), the cursor may very well go to some place where POINT is not.
+ (save-excursion (set-buffer (other-buffer))))
+
+(defun gomoku-prompt-for-other-game ()
+ "Ask for another game, and start it."
+ (if (y-or-n-p "Another game ")
+ (gomoku gomoku-board-width gomoku-board-height)
+ (message "Chicken !")))
+
+(defun gomoku-offer-a-draw ()
+ "Offer a draw and return T if Human accepted it."
+ (or (y-or-n-p "I offer you a draw. Do you accept it ")
+ (prog1 (setq gomoku-human-refused-draw t)
+ nil)))
+
+;;;
+;;; DISPLAYING THE BOARD.
+;;;
+
+;; You may change these values if you have a small screen or if the squares
+;; look rectangular, but spacings SHOULD be at least 2 (MUST BE at least 1).
+
+(defconst gomoku-square-width 4
+ "*Horizontal spacing between squares on the Gomoku board.")
+
+(defconst gomoku-square-height 2
+ "*Vertical spacing between squares on the Gomoku board.")
+
+(defconst gomoku-x-offset 3
+ "*Number of columns between the Gomoku board and the side of the window.")
+
+(defconst gomoku-y-offset 1
+ "*Number of lines between the Gomoku board and the top of the window.")
+
+
+(defun gomoku-max-width ()
+ "Largest possible board width for the current window."
+ (1+ (/ (- (window-width (selected-window))
+ gomoku-x-offset gomoku-x-offset 1)
+ gomoku-square-width)))
+
+(defun gomoku-max-height ()
+ "Largest possible board height for the current window."
+ (1+ (/ (- (window-height (selected-window))
+ gomoku-y-offset gomoku-y-offset 2)
+ ;; 2 instead of 1 because WINDOW-HEIGHT includes the mode line !
+ gomoku-square-height)))
+
+(defun gomoku-point-x ()
+ "Return the board column where point is, or nil if it is not a board column."
+ (let ((col (- (current-column) gomoku-x-offset)))
+ (if (and (>= col 0)
+ (zerop (% col gomoku-square-width))
+ (<= (setq col (1+ (/ col gomoku-square-width)))
+ gomoku-board-width))
+ col)))
+
+(defun gomoku-point-y ()
+ "Return the board row where point is, or nil if it is not a board row."
+ (let ((row (- (count-lines 1 (point)) gomoku-y-offset 1)))
+ (if (and (>= row 0)
+ (zerop (% row gomoku-square-height))
+ (<= (setq row (1+ (/ row gomoku-square-height)))
+ gomoku-board-height))
+ row)))
+
+(defun gomoku-point-square ()
+ "Return the index of the square point is on, or nil if not on the board."
+ (let (x y)
+ (and (setq x (gomoku-point-x))
+ (setq y (gomoku-point-y))
+ (gomoku-xy-to-index x y))))
+
+(defun gomoku-goto-square (index)
+ "Move point to square number INDEX."
+ (gomoku-goto-xy (gomoku-index-to-x index) (gomoku-index-to-y index)))
+
+(defun gomoku-goto-xy (x y)
+ "Move point to square at X, Y coords."
+ (goto-line (+ 1 gomoku-y-offset (* gomoku-square-height (1- y))))
+ (move-to-column (+ gomoku-x-offset (* gomoku-square-width (1- x)))))
+
+(defun gomoku-plot-square (square value)
+ "Draw 'X', 'O' or '.' on SQUARE (depending on VALUE), leave point there."
+ (gomoku-goto-square square)
+ (gomoku-put-char (cond ((= value 1) ?X)
+ ((= value 6) ?O)
+ (t ?.)))
+ (sit-for 0)) ; Display NOW
+
+(defun gomoku-put-char (char)
+ "Draw CHAR on the Gomoku screen."
+ (if buffer-read-only (toggle-read-only))
+ (insert char)
+ (delete-char 1)
+ (backward-char 1)
+ (toggle-read-only))
+
+(defun gomoku-init-display (n m)
+ "Display an N by M Gomoku board."
+ (buffer-flush-undo (current-buffer))
+ (if buffer-read-only (toggle-read-only))
+ (erase-buffer)
+ (let (string1 string2 string3 string4)
+ ;; We do not use gomoku-plot-square which would be too slow for
+ ;; initializing the display. Rather we build STRING1 for lines where
+ ;; board squares are to be found, and STRING2 for empty lines. STRING1 is
+ ;; like STRING2 except for dots every DX squares. Empty lines are filled
+ ;; with spaces so that cursor moving up and down remains on the same
+ ;; column.
+ (setq string1 (concat (make-string (1- gomoku-square-width) ? ) ".")
+ string1 (apply 'concat
+ (make-list (1- n) string1))
+ string1 (concat (make-string gomoku-x-offset ? ) "." string1 "\n")
+ string2 (make-string (+ 1 gomoku-x-offset
+ (* (1- n) gomoku-square-width))
+ ? )
+ string2 (concat string2 "\n")
+ string3 (apply 'concat
+ (make-list (1- gomoku-square-height) string2))
+ string3 (concat string3 string1)
+ string3 (apply 'concat
+ (make-list (1- m) string3))
+ string4 (apply 'concat
+ (make-list gomoku-y-offset string2)))
+ (insert string4 string1 string3))
+ (toggle-read-only)
+ (gomoku-goto-xy (/ (1+ n) 2) (/ (1+ m) 2)) ; center of the board
+ (sit-for 0)) ; Display NOW
+
+(defun gomoku-display-statistics ()
+ "Obnoxiously display some statistics about previous games in mode line."
+ ;; We store this string in the mode-line-process local variable.
+ ;; This is certainly not the cleanest way out ...
+ (setq mode-line-process
+ (cond
+ ((not (zerop gomoku-number-of-draws))
+ (format ": Won %d, lost %d, drew %d"
+ gomoku-number-of-wins
+ gomoku-number-of-losses
+ gomoku-number-of-draws))
+ ((not (zerop gomoku-number-of-losses))
+ (format ": Won %d, lost %d"
+ gomoku-number-of-wins
+ gomoku-number-of-losses))
+ ((zerop gomoku-number-of-wins)
+ "")
+ ((= 1 gomoku-number-of-wins)
+ ": Already won one")
+ (t
+ (format ": Won %d in a row"
+ gomoku-number-of-wins))))
+ ;; Then a (standard) kludgy line will force update of mode line.
+ (set-buffer-modified-p (buffer-modified-p)))
+
+(defun gomoku-switch-to-window ()
+ "Find or create the Gomoku buffer, and display it."
+ (interactive)
+ (let ((buff (get-buffer "*Gomoku*")))
+ (if buff ; Buffer exists:
+ (switch-to-buffer buff) ; no problem.
+ (if gomoku-game-in-progress
+ (gomoku-crash-game)) ; buffer has been killed or something
+ (switch-to-buffer "*Gomoku*") ; Anyway, start anew.
+ (gomoku-mode))))
+
+;;;
+;;; CROSSING WINNING QTUPLES.
+;;;
+
+;; When someone succeeds in filling a qtuple, we draw a line over the five
+;; corresponding squares. One problem is that the program does not know which
+;; squares ! It only knows the square where the last move has been played and
+;; who won. The solution is to scan the board along all four directions.
+
+(defvar gomoku-winning-qtuple-beg nil
+ "First square of the winning qtuple.")
+
+(defvar gomoku-winning-qtuple-end nil
+ "Last square of the winning qtuple.")
+
+(defvar gomoku-winning-qtuple-dx nil
+ "Direction of the winning qtuple (along the X axis).")
+
+(defvar gomoku-winning-qtuple-dy nil
+ "Direction of the winning qtuple (along the Y axis).")
+
+
+(defun gomoku-find-filled-qtuple (square value)
+ "Return T if SQUARE belongs to a qtuple filled with VALUEs."
+ (or (gomoku-check-filled-qtuple square value 1 0)
+ (gomoku-check-filled-qtuple square value 0 1)
+ (gomoku-check-filled-qtuple square value 1 1)
+ (gomoku-check-filled-qtuple square value -1 1)))
+
+(defun gomoku-check-filled-qtuple (square value dx dy)
+ "Return T if SQUARE belongs to a qtuple filled with VALUEs along DX, DY."
+ ;; And record it in the WINNING-QTUPLE-... variables.
+ (let ((a 0) (b 0)
+ (left square) (right square)
+ (depl (gomoku-xy-to-index dx dy))
+ a+4)
+ (while (and (> a -4) ; stretch tuple left
+ (= value (aref gomoku-board (setq left (- left depl)))))
+ (setq a (1- a)))
+ (setq a+4 (+ a 4))
+ (while (and (< b a+4) ; stretch tuple right
+ (= value (aref gomoku-board (setq right (+ right depl)))))
+ (setq b (1+ b)))
+ (cond ((= b a+4) ; tuple length = 5 ?
+ (setq gomoku-winning-qtuple-beg (+ square (* a depl))
+ gomoku-winning-qtuple-end (+ square (* b depl))
+ gomoku-winning-qtuple-dx dx
+ gomoku-winning-qtuple-dy dy)
+ t))))
+
+(defun gomoku-cross-winning-qtuple ()
+ "Cross winning qtuple, as found by gomoku-find-filled-qtuple."
+ (gomoku-cross-qtuple gomoku-winning-qtuple-beg
+ gomoku-winning-qtuple-end
+ gomoku-winning-qtuple-dx
+ gomoku-winning-qtuple-dy))
+
+(defun gomoku-cross-qtuple (square1 square2 dx dy)
+ "Cross every square between SQUARE1 and SQUARE2 in the DX, DY direction."
+ (save-excursion ; Not moving point from last square
+ (let ((depl (gomoku-xy-to-index dx dy)))
+ ;; WARNING: this function assumes DEPL > 0 and SQUARE2 > SQUARE1
+ (while (not (= square1 square2))
+ (gomoku-goto-square square1)
+ (setq square1 (+ square1 depl))
+ (cond
+ ((and (= dx 1) (= dy 0)) ; Horizontal
+ (let ((n 1))
+ (while (< n gomoku-square-width)
+ (setq n (1+ n))
+ (forward-char 1)
+ (gomoku-put-char ?-))))
+ ((and (= dx 0) (= dy 1)) ; Vertical
+ (let ((n 1))
+ (while (< n gomoku-square-height)
+ (setq n (1+ n))
+ (next-line 1)
+ (gomoku-put-char ?|))))
+ ((and (= dx -1) (= dy 1)) ; 1st Diagonal
+ (backward-char (/ gomoku-square-width 2))
+ (next-line (/ gomoku-square-height 2))
+ (gomoku-put-char ?/))
+ ((and (= dx 1) (= dy 1)) ; 2nd Diagonal
+ (forward-char (/ gomoku-square-width 2))
+ (next-line (/ gomoku-square-height 2))
+ (gomoku-put-char ?\\))))))
+ (sit-for 0)) ; Display NOW
+
+;;;
+;;; CURSOR MOTION.
+;;;
+(defun gomoku-move-left ()
+ "Move point backward one column on the Gomoku board."
+ (interactive)
+ (let ((x (gomoku-point-x)))
+ (backward-char (cond ((null x) 1)
+ ((> x 1) gomoku-square-width)
+ (t 0)))))
+
+(defun gomoku-move-right ()
+ "Move point forward one column on the Gomoku board."
+ (interactive)
+ (let ((x (gomoku-point-x)))
+ (forward-char (cond ((null x) 1)
+ ((< x gomoku-board-width) gomoku-square-width)
+ (t 0)))))
+
+(defun gomoku-move-down ()
+ "Move point down one row on the Gomoku board."
+ (interactive)
+ (let ((y (gomoku-point-y)))
+ (next-line (cond ((null y) 1)
+ ((< y gomoku-board-height) gomoku-square-height)
+ (t 0)))))
+
+(defun gomoku-move-up ()
+ "Move point up one row on the Gomoku board."
+ (interactive)
+ (let ((y (gomoku-point-y)))
+ (previous-line (cond ((null y) 1)
+ ((> y 1) gomoku-square-height)
+ (t 0)))))
+
+(defun gomoku-move-ne ()
+ "Move point North East on the Gomoku board."
+ (interactive)
+ (gomoku-move-up)
+ (gomoku-move-right))
+
+(defun gomoku-move-se ()
+ "Move point South East on the Gomoku board."
+ (interactive)
+ (gomoku-move-down)
+ (gomoku-move-right))
+
+(defun gomoku-move-nw ()
+ "Move point North West on the Gomoku board."
+ (interactive)
+ (gomoku-move-up)
+ (gomoku-move-left))
+
+(defun gomoku-move-sw ()
+ "Move point South West on the Gomoku board."
+ (interactive)
+ (gomoku-move-down)
+ (gomoku-move-left))
+
+
diff --git a/lisp/play/spook.el b/lisp/play/spook.el
new file mode 100644
index 00000000000..84fffceeaa1
--- /dev/null
+++ b/lisp/play/spook.el
@@ -0,0 +1,109 @@
+;; Spook phrase utility
+;; Copyright (C) 1988 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+; Steve Strassmann (straz@media-lab.media.mit.edu) didn't write
+; this, and even if he did, he really didn't mean for you to use it
+; in an anarchistic way.
+; May 1987
+
+; To use this:
+; Make sure you have the variable SPOOK-PHRASES-FILE pointing to
+; a valid phrase file. Phrase files are in the same format as
+; zippy's yow.lines (ITS-style LINS format).
+; Strings are terminated by ascii 0 characters. Leading whitespace ignored.
+; Everything up to the first \000 is a comment.
+;
+; Just before sending mail, do M-x spook.
+; A number of phrases will be inserted into your buffer, to help
+; give your message that extra bit of attractiveness for automated
+; keyword scanners.
+
+; Variables
+(defvar spook-phrases-file (concat exec-directory "spook.lines")
+ "Keep your favorite phrases here.")
+
+(defvar spook-phrase-default-count 15
+ "Default number of phrases to insert")
+
+(defvar spook-vector nil
+ "Important phrases for NSA mail-watchers")
+
+; Randomize the seed in the random number generator.
+(random t)
+
+; Call this with M-x spook.
+(defun spook ()
+ "Adds that special touch of class to your outgoing mail."
+ (interactive)
+ (if (null spook-vector)
+ (setq spook-vector (snarf-spooks)))
+ (shuffle-vector spook-vector)
+ (let ((start (point)))
+ (insert ?\n)
+ (spook1 (min (- (length spook-vector) 1) spook-phrase-default-count))
+ (insert ?\n)
+ (fill-region-as-paragraph start (point) nil)))
+
+(defun spook1 (arg)
+ "Inserts a spook phrase ARG times."
+ (cond ((zerop arg) t)
+ (t (insert (aref spook-vector arg))
+ (insert " ")
+ (spook1 (1- arg)))))
+
+(defun snarf-spooks ()
+ "Reads in the phrase file"
+ (message "Checking authorization...")
+ (save-excursion
+ (let ((buf (generate-new-buffer "*spook*"))
+ (result '()))
+ (set-buffer buf)
+ (insert-file-contents (expand-file-name spook-phrases-file))
+ (search-forward "\0")
+ (while (progn (skip-chars-forward " \t\n\r\f") (not (eobp)))
+ (let ((beg (point)))
+ (search-forward "\0")
+ (setq result (cons (buffer-substring beg (1- (point)))
+ result))))
+ (kill-buffer buf)
+ (message "Checking authorization... Approved.")
+ (setq spook-vector (apply 'vector result)))))
+
+(defun pick-random (n)
+ "Returns a random number from 0 to N-1 inclusive."
+ (% (logand 0777777 (random)) n))
+
+; Thanks to Ian G Batten <BattenIG@CS.BHAM.AC.UK>
+; [of the University of Birmingham Computer Science Department]
+; for the iterative version of this shuffle.
+;
+(defun shuffle-vector (vector)
+ "Randomly permute the elements of VECTOR (all permutations equally likely)"
+ (let ((i 0)
+ j
+ temp
+ (len (length vector)))
+ (while (< i len)
+ (setq j (+ i (pick-random (- len i))))
+ (setq temp (aref vector i))
+ (aset vector i (aref vector j))
+ (aset vector j temp)
+ (setq i (1+ i))))
+ vector)
diff --git a/lisp/progmodes/icon.el b/lisp/progmodes/icon.el
new file mode 100644
index 00000000000..2215f84d795
--- /dev/null
+++ b/lisp/progmodes/icon.el
@@ -0,0 +1,550 @@
+;; Note: use
+;; (autoload 'icon-mode "icon" nil t)
+;; (setq auto-mode-alist (cons '("\\.icn$" . icon-mode) auto-mode-alist))
+;; if not permanently installed in your emacs
+
+;; Icon code editing commands for Emacs
+;; Derived from c-mode.el 15-Feb-89 Chris Smith convex!csmith
+;; Copyright (C) 1989 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defvar icon-mode-abbrev-table nil
+ "Abbrev table in use in Icon-mode buffers.")
+(define-abbrev-table 'icon-mode-abbrev-table ())
+
+(defvar icon-mode-map ()
+ "Keymap used in Icon mode.")
+(if icon-mode-map
+ ()
+ (setq icon-mode-map (make-sparse-keymap))
+ (define-key icon-mode-map "{" 'electric-icon-brace)
+ (define-key icon-mode-map "}" 'electric-icon-brace)
+ (define-key icon-mode-map "\e\C-h" 'mark-icon-function)
+ (define-key icon-mode-map "\e\C-a" 'beginning-of-icon-defun)
+ (define-key icon-mode-map "\e\C-e" 'end-of-icon-defun)
+ (define-key icon-mode-map "\e\C-q" 'indent-icon-exp)
+ (define-key icon-mode-map "\177" 'backward-delete-char-untabify)
+ (define-key icon-mode-map "\t" 'icon-indent-command))
+
+(defvar icon-mode-syntax-table nil
+ "Syntax table in use in Icon-mode buffers.")
+
+(if icon-mode-syntax-table
+ ()
+ (setq icon-mode-syntax-table (make-syntax-table))
+ (modify-syntax-entry ?\\ "\\" icon-mode-syntax-table)
+ (modify-syntax-entry ?# "<" icon-mode-syntax-table)
+ (modify-syntax-entry ?\n ">" icon-mode-syntax-table)
+ (modify-syntax-entry ?$ "." icon-mode-syntax-table)
+ (modify-syntax-entry ?/ "." icon-mode-syntax-table)
+ (modify-syntax-entry ?* "." icon-mode-syntax-table)
+ (modify-syntax-entry ?+ "." icon-mode-syntax-table)
+ (modify-syntax-entry ?- "." icon-mode-syntax-table)
+ (modify-syntax-entry ?= "." icon-mode-syntax-table)
+ (modify-syntax-entry ?% "." icon-mode-syntax-table)
+ (modify-syntax-entry ?< "." icon-mode-syntax-table)
+ (modify-syntax-entry ?> "." icon-mode-syntax-table)
+ (modify-syntax-entry ?& "." icon-mode-syntax-table)
+ (modify-syntax-entry ?| "." icon-mode-syntax-table)
+ (modify-syntax-entry ?\' "\"" icon-mode-syntax-table))
+
+(defconst icon-indent-level 4
+ "*Indentation of Icon statements with respect to containing block.")
+(defconst icon-brace-imaginary-offset 0
+ "*Imagined indentation of a Icon open brace that actually follows a statement.")
+(defconst icon-brace-offset 0
+ "*Extra indentation for braces, compared with other text in same context.")
+(defconst icon-continued-statement-offset 4
+ "*Extra indent for lines not starting new statements.")
+(defconst icon-continued-brace-offset 0
+ "*Extra indent for substatements that start with open-braces.
+This is in addition to icon-continued-statement-offset.")
+
+(defconst icon-auto-newline nil
+ "*Non-nil means automatically newline before and after braces
+inserted in Icon code.")
+
+(defconst icon-tab-always-indent t
+ "*Non-nil means TAB in Icon mode should always reindent the current line,
+regardless of where in the line point is when the TAB command is used.")
+
+(defun icon-mode ()
+ "Major mode for editing Icon code.
+Expression and list commands understand all Icon brackets.
+Tab indents for Icon code.
+Paragraphs are separated by blank lines only.
+Delete converts tabs to spaces as it moves back.
+\\{icon-mode-map}
+Variables controlling indentation style:
+ icon-tab-always-indent
+ Non-nil means TAB in Icon mode should always reindent the current line,
+ regardless of where in the line point is when the TAB command is used.
+ icon-auto-newline
+ Non-nil means automatically newline before and after braces
+ inserted in Icon code.
+ icon-indent-level
+ Indentation of Icon statements within surrounding block.
+ The surrounding block's indentation is the indentation
+ of the line on which the open-brace appears.
+ icon-continued-statement-offset
+ Extra indentation given to a substatement, such as the
+ then-clause of an if or body of a while.
+ icon-continued-brace-offset
+ Extra indentation given to a brace that starts a substatement.
+ This is in addition to icon-continued-statement-offset.
+ icon-brace-offset
+ Extra indentation for line if it starts with an open brace.
+ icon-brace-imaginary-offset
+ An open brace following other text is treated as if it were
+ this far to the right of the start of its line.
+
+Turning on Icon mode calls the value of the variable icon-mode-hook with no args,
+if that value is non-nil."
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map icon-mode-map)
+ (setq major-mode 'icon-mode)
+ (setq mode-name "Icon")
+ (setq local-abbrev-table icon-mode-abbrev-table)
+ (set-syntax-table icon-mode-syntax-table)
+ (make-local-variable 'paragraph-start)
+ (setq paragraph-start (concat "^$\\|" page-delimiter))
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-separate paragraph-start)
+ (make-local-variable 'indent-line-function)
+ (setq indent-line-function 'icon-indent-line)
+ (make-local-variable 'require-final-newline)
+ (setq require-final-newline t)
+ (make-local-variable 'comment-start)
+ (setq comment-start "# ")
+ (make-local-variable 'comment-end)
+ (setq comment-end "")
+ (make-local-variable 'comment-column)
+ (setq comment-column 32)
+ (make-local-variable 'comment-start-skip)
+ (setq comment-start-skip "# *")
+ (make-local-variable 'comment-indent-hook)
+ (setq comment-indent-hook 'icon-comment-indent)
+ (run-hooks 'icon-mode-hook))
+
+;; This is used by indent-for-comment
+;; to decide how much to indent a comment in Icon code
+;; based on its context.
+(defun icon-comment-indent ()
+ (if (looking-at "^#")
+ 0
+ (save-excursion
+ (skip-chars-backward " \t")
+ (max (if (bolp) 0 (1+ (current-column)))
+ comment-column))))
+
+(defun electric-icon-brace (arg)
+ "Insert character and correct line's indentation."
+ (interactive "P")
+ (let (insertpos)
+ (if (and (not arg)
+ (eolp)
+ (or (save-excursion
+ (skip-chars-backward " \t")
+ (bolp))
+ (if icon-auto-newline
+ (progn (icon-indent-line) (newline) t)
+ nil)))
+ (progn
+ (insert last-command-char)
+ (icon-indent-line)
+ (if icon-auto-newline
+ (progn
+ (newline)
+ ;; (newline) may have done auto-fill
+ (setq insertpos (- (point) 2))
+ (icon-indent-line)))
+ (save-excursion
+ (if insertpos (goto-char (1+ insertpos)))
+ (delete-char -1))))
+ (if insertpos
+ (save-excursion
+ (goto-char insertpos)
+ (self-insert-command (prefix-numeric-value arg)))
+ (self-insert-command (prefix-numeric-value arg)))))
+
+(defun icon-indent-command (&optional whole-exp)
+ (interactive "P")
+ "Indent current line as Icon code, or in some cases insert a tab character.
+If icon-tab-always-indent is non-nil (the default), always indent current line.
+Otherwise, indent the current line only if point is at the left margin
+or in the line's indentation; otherwise insert a tab.
+
+A numeric argument, regardless of its value,
+means indent rigidly all the lines of the expression starting after point
+so that this line becomes properly indented.
+The relative indentation among the lines of the expression are preserved."
+ (if whole-exp
+ ;; If arg, always indent this line as Icon
+ ;; and shift remaining lines of expression the same amount.
+ (let ((shift-amt (icon-indent-line))
+ beg end)
+ (save-excursion
+ (if icon-tab-always-indent
+ (beginning-of-line))
+ (setq beg (point))
+ (forward-sexp 1)
+ (setq end (point))
+ (goto-char beg)
+ (forward-line 1)
+ (setq beg (point)))
+ (if (> end beg)
+ (indent-code-rigidly beg end shift-amt "#")))
+ (if (and (not icon-tab-always-indent)
+ (save-excursion
+ (skip-chars-backward " \t")
+ (not (bolp))))
+ (insert-tab)
+ (icon-indent-line))))
+
+(defun icon-indent-line ()
+ "Indent current line as Icon code.
+Return the amount the indentation changed by."
+ (let ((indent (calculate-icon-indent nil))
+ beg shift-amt
+ (case-fold-search nil)
+ (pos (- (point-max) (point))))
+ (beginning-of-line)
+ (setq beg (point))
+ (cond ((eq indent nil)
+ (setq indent (current-indentation)))
+ ((eq indent t)
+ (setq indent (calculate-icon-indent-within-comment)))
+ ((looking-at "[ \t]*#")
+ (setq indent 0))
+ (t
+ (skip-chars-forward " \t")
+ (if (listp indent) (setq indent (car indent)))
+ (cond ((and (looking-at "else\\b")
+ (not (looking-at "else\\s_")))
+ (setq indent (save-excursion
+ (icon-backward-to-start-of-if)
+ (current-indentation))))
+ ((or (= (following-char) ?})
+ (looking-at "end\\b"))
+ (setq indent (- indent icon-indent-level)))
+ ((= (following-char) ?{)
+ (setq indent (+ indent icon-brace-offset))))))
+ (skip-chars-forward " \t")
+ (setq shift-amt (- indent (current-column)))
+ (if (zerop shift-amt)
+ (if (> (- (point-max) pos) (point))
+ (goto-char (- (point-max) pos)))
+ (delete-region beg (point))
+ (indent-to indent)
+ ;; If initial point was within line's indentation,
+ ;; position after the indentation. Else stay at same point in text.
+ (if (> (- (point-max) pos) (point))
+ (goto-char (- (point-max) pos))))
+ shift-amt))
+
+(defun calculate-icon-indent (&optional parse-start)
+ "Return appropriate indentation for current line as Icon code.
+In usual case returns an integer: the column to indent to.
+Returns nil if line starts inside a string, t if in a comment."
+ (save-excursion
+ (beginning-of-line)
+ (let ((indent-point (point))
+ (case-fold-search nil)
+ state
+ containing-sexp
+ toplevel)
+ (if parse-start
+ (goto-char parse-start)
+ (setq toplevel (beginning-of-icon-defun)))
+ (while (< (point) indent-point)
+ (setq parse-start (point))
+ (setq state (parse-partial-sexp (point) indent-point 0))
+ (setq containing-sexp (car (cdr state))))
+ (cond ((or (nth 3 state) (nth 4 state))
+ ;; return nil or t if should not change this line
+ (nth 4 state))
+ ((and containing-sexp
+ (/= (char-after containing-sexp) ?{))
+ ;; line is expression, not statement:
+ ;; indent to just after the surrounding open.
+ (goto-char (1+ containing-sexp))
+ (current-column))
+ (t
+ (if toplevel
+ ;; Outside any procedures.
+ (progn (icon-backward-to-noncomment (point-min))
+ (if (icon-is-continuation-line)
+ icon-continued-statement-offset 0))
+ ;; Statement level.
+ (if (null containing-sexp)
+ (progn (beginning-of-icon-defun)
+ (setq containing-sexp (point))))
+ (goto-char indent-point)
+ ;; Is it a continuation or a new statement?
+ ;; Find previous non-comment character.
+ (icon-backward-to-noncomment containing-sexp)
+ ;; Now we get the answer.
+ (if (icon-is-continuation-line)
+ ;; This line is continuation of preceding line's statement;
+ ;; indent icon-continued-statement-offset more than the
+ ;; first line of the statement.
+ (progn
+ (icon-backward-to-start-of-continued-exp containing-sexp)
+ (+ icon-continued-statement-offset (current-column)
+ (if (save-excursion (goto-char indent-point)
+ (skip-chars-forward " \t")
+ (eq (following-char) ?{))
+ icon-continued-brace-offset 0)))
+ ;; This line starts a new statement.
+ ;; Position following last unclosed open.
+ (goto-char containing-sexp)
+ ;; Is line first statement after an open-brace?
+ (or
+ ;; If no, find that first statement and indent like it.
+ (save-excursion
+ (if (looking-at "procedure\\s ")
+ (forward-sexp 3)
+ (forward-char 1))
+ (while (progn (skip-chars-forward " \t\n")
+ (looking-at "#"))
+ ;; Skip over comments following openbrace.
+ (forward-line 1))
+ ;; The first following code counts
+ ;; if it is before the line we want to indent.
+ (and (< (point) indent-point)
+ (current-column)))
+ ;; If no previous statement,
+ ;; indent it relative to line brace is on.
+ ;; For open brace in column zero, don't let statement
+ ;; start there too. If icon-indent-level is zero,
+ ;; use icon-brace-offset + icon-continued-statement-offset
+ ;; instead.
+ ;; For open-braces not the first thing in a line,
+ ;; add in icon-brace-imaginary-offset.
+ (+ (if (and (bolp) (zerop icon-indent-level))
+ (+ icon-brace-offset
+ icon-continued-statement-offset)
+ icon-indent-level)
+ ;; Move back over whitespace before the openbrace.
+ ;; If openbrace is not first nonwhite thing on the line,
+ ;; add the icon-brace-imaginary-offset.
+ (progn (skip-chars-backward " \t")
+ (if (bolp) 0 icon-brace-imaginary-offset))
+ ;; Get initial indentation of the line we are on.
+ (current-indentation))))))))))
+
+;; List of words to check for as the last thing on a line.
+;; If cdr is t, next line is a continuation of the same statement,
+;; if cdr is nil, next line starts a new (possibly indented) statement.
+
+(defconst icon-resword-alist
+ '(("by" . t) ("case" . t) ("create") ("do") ("dynamic" . t) ("else")
+ ("every" . t) ("if" . t) ("global" . t) ("initial" . t)
+ ("link" . t) ("local" . t) ("of") ("record" . t) ("repeat" . t)
+ ("static" . t) ("then") ("to" . t) ("until" . t) ("while" . t)))
+
+(defun icon-is-continuation-line ()
+ (let* ((ch (preceding-char))
+ (ch-syntax (char-syntax ch)))
+ (if (eq ch-syntax ?w)
+ (assoc (buffer-substring
+ (progn (forward-word -1) (point))
+ (progn (forward-word 1) (point)))
+ icon-resword-alist)
+ (not (memq ch '(0 ?\; ?\} ?\{ ?\) ?\] ?\" ?\' ?\n))))))
+
+(defun icon-backward-to-noncomment (lim)
+ (let (opoint stop)
+ (while (not stop)
+ (skip-chars-backward " \t\n\f" lim)
+ (setq opoint (point))
+ (beginning-of-line)
+ (if (and (nth 5 (parse-partial-sexp (point) opoint))
+ (< lim (point)))
+ (search-backward "#")
+ (setq stop t)))))
+
+(defun icon-backward-to-start-of-continued-exp (lim)
+ (if (memq (preceding-char) '(?\) ?\]))
+ (forward-sexp -1))
+ (beginning-of-line)
+ (skip-chars-forward " \t")
+ (cond
+ ((<= (point) lim) (goto-char (1+ lim)))
+ ((not (icon-is-continued-line)) 0)
+ ((and (eq (char-syntax (following-char)) ?w)
+ (cdr
+ (assoc (buffer-substring (point)
+ (save-excursion (forward-word 1) (point)))
+ icon-resword-alist))) 0)
+ (t (end-of-line 0) (icon-backward-to-start-of-continued-exp lim))))
+
+(defun icon-is-continued-line ()
+ (save-excursion
+ (end-of-line 0)
+ (icon-is-continuation-line)))
+
+(defun icon-backward-to-start-of-if (&optional limit)
+ "Move to the start of the last ``unbalanced'' if."
+ (or limit (setq limit (save-excursion (beginning-of-icon-defun) (point))))
+ (let ((if-level 1)
+ (case-fold-search nil))
+ (while (not (zerop if-level))
+ (backward-sexp 1)
+ (cond ((looking-at "else\\b")
+ (setq if-level (1+ if-level)))
+ ((looking-at "if\\b")
+ (setq if-level (1- if-level)))
+ ((< (point) limit)
+ (setq if-level 0)
+ (goto-char limit))))))
+
+(defun mark-icon-function ()
+ "Put mark at end of Icon function, point at beginning."
+ (interactive)
+ (push-mark (point))
+ (end-of-icon-defun)
+ (push-mark (point))
+ (beginning-of-line 0)
+ (beginning-of-icon-defun))
+
+(defun beginning-of-icon-defun ()
+ "Go to the start of the enclosing procedure; return t if at top level."
+ (interactive)
+ (if (re-search-backward "^procedure\\s \\|^end[ \t\n]" (point-min) 'move)
+ (looking-at "e")
+ t))
+
+(defun end-of-icon-defun ()
+ (interactive)
+ (if (not (bobp)) (forward-char -1))
+ (re-search-forward "\\(\\s \\|^\\)end\\(\\s \\|$\\)" (point-max) 'move)
+ (forward-word -1)
+ (forward-line 1))
+
+(defun indent-icon-exp ()
+ "Indent each line of the Icon grouping following point."
+ (interactive)
+ (let ((indent-stack (list nil))
+ (contain-stack (list (point)))
+ (case-fold-search nil)
+ restart outer-loop-done inner-loop-done state ostate
+ this-indent last-sexp
+ at-else at-brace at-do
+ (opoint (point))
+ (next-depth 0))
+ (save-excursion
+ (forward-sexp 1))
+ (save-excursion
+ (setq outer-loop-done nil)
+ (while (and (not (eobp)) (not outer-loop-done))
+ (setq last-depth next-depth)
+ ;; Compute how depth changes over this line
+ ;; plus enough other lines to get to one that
+ ;; does not end inside a comment or string.
+ ;; Meanwhile, do appropriate indentation on comment lines.
+ (setq innerloop-done nil)
+ (while (and (not innerloop-done)
+ (not (and (eobp) (setq outer-loop-done t))))
+ (setq ostate state)
+ (setq state (parse-partial-sexp (point) (progn (end-of-line) (point))
+ nil nil state))
+ (setq next-depth (car state))
+ (if (and (car (cdr (cdr state)))
+ (>= (car (cdr (cdr state))) 0))
+ (setq last-sexp (car (cdr (cdr state)))))
+ (if (or (nth 4 ostate))
+ (icon-indent-line))
+ (if (or (nth 3 state))
+ (forward-line 1)
+ (setq innerloop-done t)))
+ (if (<= next-depth 0)
+ (setq outer-loop-done t))
+ (if outer-loop-done
+ nil
+ (if (/= last-depth next-depth)
+ (setq last-sexp nil))
+ (while (> last-depth next-depth)
+ (setq indent-stack (cdr indent-stack)
+ contain-stack (cdr contain-stack)
+ last-depth (1- last-depth)))
+ (while (< last-depth next-depth)
+ (setq indent-stack (cons nil indent-stack)
+ contain-stack (cons nil contain-stack)
+ last-depth (1+ last-depth)))
+ (if (null (car contain-stack))
+ (setcar contain-stack (or (car (cdr state))
+ (save-excursion (forward-sexp -1)
+ (point)))))
+ (forward-line 1)
+ (skip-chars-forward " \t")
+ (if (eolp)
+ nil
+ (if (and (car indent-stack)
+ (>= (car indent-stack) 0))
+ ;; Line is on an existing nesting level.
+ ;; Lines inside parens are handled specially.
+ (if (/= (char-after (car contain-stack)) ?{)
+ (setq this-indent (car indent-stack))
+ ;; Line is at statement level.
+ ;; Is it a new statement? Is it an else?
+ ;; Find last non-comment character before this line
+ (save-excursion
+ (setq at-else (looking-at "else\\W"))
+ (setq at-brace (= (following-char) ?{))
+ (icon-backward-to-noncomment opoint)
+ (if (icon-is-continuation-line)
+ ;; Preceding line did not end in comma or semi;
+ ;; indent this line icon-continued-statement-offset
+ ;; more than previous.
+ (progn
+ (icon-backward-to-start-of-continued-exp (car contain-stack))
+ (setq this-indent
+ (+ icon-continued-statement-offset (current-column)
+ (if at-brace icon-continued-brace-offset 0))))
+ ;; Preceding line ended in comma or semi;
+ ;; use the standard indent for this level.
+ (if at-else
+ (progn (icon-backward-to-start-of-if opoint)
+ (setq this-indent (current-indentation)))
+ (setq this-indent (car indent-stack))))))
+ ;; Just started a new nesting level.
+ ;; Compute the standard indent for this level.
+ (let ((val (calculate-icon-indent
+ (if (car indent-stack)
+ (- (car indent-stack))))))
+ (setcar indent-stack
+ (setq this-indent val))))
+ ;; Adjust line indentation according to its contents
+ (if (or (= (following-char) ?})
+ (looking-at "end\\b"))
+ (setq this-indent (- this-indent icon-indent-level)))
+ (if (= (following-char) ?{)
+ (setq this-indent (+ this-indent icon-brace-offset)))
+ ;; Put chosen indentation into effect.
+ (or (= (current-column) this-indent)
+ (progn
+ (delete-region (point) (progn (beginning-of-line) (point)))
+ (indent-to this-indent)))
+ ;; Indent any comment following the text.
+ (or (looking-at comment-start-skip)
+ (if (re-search-forward comment-start-skip (save-excursion (end-of-line) (point)) t)
+ (progn (indent-for-comment) (beginning-of-line))))))))))
+
diff --git a/lisp/rect.el b/lisp/rect.el
new file mode 100644
index 00000000000..3dd06f1be0e
--- /dev/null
+++ b/lisp/rect.el
@@ -0,0 +1,205 @@
+;; Rectangle functions for GNU Emacs.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defun operate-on-rectangle (function start end coerce-tabs)
+ "Call FUNCTION for each line of rectangle with corners at START, END.
+If COERCE-TABS is non-nil, convert multi-column characters
+that span the starting or ending columns on any line
+to multiple spaces before calling FUNCTION.
+FUNCTION is called with three arguments:
+ position of start of segment of this line within the rectangle,
+ number of columns that belong to rectangle but are before that position,
+ number of columns that belong to rectangle but are after point.
+Point is at the end of the segment of this line within the rectangle."
+ (let (startcol startlinepos endcol endlinepos)
+ (save-excursion
+ (goto-char start)
+ (setq startcol (current-column))
+ (beginning-of-line)
+ (setq startlinepos (point)))
+ (save-excursion
+ (goto-char end)
+ (setq endcol (current-column))
+ (forward-line 1)
+ (setq endlinepos (point-marker)))
+ (if (< endcol startcol)
+ (let ((tem startcol))
+ (setq startcol endcol endcol tem)))
+ (if (/= endcol startcol)
+ (save-excursion
+ (goto-char startlinepos)
+ (while (< (point) endlinepos)
+ (let (startpos begextra endextra)
+ (move-to-column startcol)
+ (and coerce-tabs
+ (> (current-column) startcol)
+ (rectangle-coerce-tab startcol))
+ (setq begextra (- (current-column) startcol))
+ (setq startpos (point))
+ (move-to-column endcol)
+ (if (> (current-column) endcol)
+ (if coerce-tabs
+ (rectangle-coerce-tab endcol)
+ (forward-char -1)))
+ (setq endextra (- endcol (current-column)))
+ (if (< begextra 0)
+ (setq endextra (+ endextra begextra)
+ begextra 0))
+ (funcall function startpos begextra endextra))
+ (forward-line 1))))
+ (- endcol startcol)))
+
+(defun delete-rectangle-line (startdelpos ignore ignore)
+ (delete-region startdelpos (point)))
+
+(defun delete-extract-rectangle-line (startdelpos begextra endextra)
+ (save-excursion
+ (extract-rectangle-line startdelpos begextra endextra))
+ (delete-region startdelpos (point)))
+
+(defun extract-rectangle-line (startdelpos begextra endextra)
+ (let ((line (buffer-substring startdelpos (point)))
+ (end (point)))
+ (goto-char startdelpos)
+ (while (search-forward "\t" end t)
+ (let ((width (- (current-column)
+ (save-excursion (forward-char -1)
+ (current-column)))))
+ (setq line (concat (substring line 0 (- (point) end 1))
+ (spaces-string width)
+ (substring line (+ (length line) (- (point) end)))))))
+ (if (or (> begextra 0) (> endextra 0))
+ (setq line (concat (spaces-string begextra)
+ line
+ (spaces-string endextra))))
+ (setq lines (cons line lines))))
+
+(defconst spaces-strings
+ '["" " " " " " " " " " " " " " " " "])
+
+(defun spaces-string (n)
+ (if (<= n 8) (aref spaces-strings n)
+ (let ((val ""))
+ (while (> n 8)
+ (setq val (concat " " val)
+ n (- n 8)))
+ (concat val (aref spaces-strings n)))))
+
+(defun delete-rectangle (start end)
+ "Delete (don't save) text in rectangle with point and mark as corners.
+The same range of columns is deleted in each line
+starting with the line where the region begins
+and ending with the line where the region ends."
+ (interactive "r")
+ (operate-on-rectangle 'delete-rectangle-line start end t))
+
+(defun delete-extract-rectangle (start end)
+ "Delete contents of rectangle and return it as a list of strings.
+Arguments START and END are the corners of the rectangle.
+The value is list of strings, one for each line of the rectangle."
+ (let (lines)
+ (operate-on-rectangle 'delete-extract-rectangle-line
+ start end t)
+ (nreverse lines)))
+
+(defun extract-rectangle (start end)
+ "Return contents of rectangle with corners at START and END.
+Value is list of strings, one for each line of the rectangle."
+ (let (lines)
+ (operate-on-rectangle 'extract-rectangle-line start end nil)
+ (nreverse lines)))
+
+(defvar killed-rectangle nil
+ "Rectangle for yank-rectangle to insert.")
+
+(defun kill-rectangle (start end)
+ "Delete rectangle with corners at point and mark; save as last killed one.
+Calling from program, supply two args START and END, buffer positions.
+But in programs you might prefer to use delete-extract-rectangle."
+ (interactive "r")
+ (setq killed-rectangle (delete-extract-rectangle start end)))
+
+(defun yank-rectangle ()
+ "Yank the last killed rectangle with upper left corner at point."
+ (interactive)
+ (insert-rectangle killed-rectangle))
+
+(defun insert-rectangle (rectangle)
+ "Insert text of RECTANGLE with upper left corner at point.
+RECTANGLE's first line is inserted at point,
+its second line is inserted at a point vertically under point, etc.
+RECTANGLE should be a list of strings."
+ (let ((lines rectangle)
+ (insertcolumn (current-column))
+ (first t))
+ (while lines
+ (or first
+ (progn
+ (forward-line 1)
+ (or (bolp) (insert ?\n))
+ (move-to-column insertcolumn)
+ (if (> (current-column) insertcolumn)
+ (rectangle-coerce-tab insertcolumn))
+ (if (< (current-column) insertcolumn)
+ (indent-to insertcolumn))))
+ (setq first nil)
+ (insert (car lines))
+ (setq lines (cdr lines)))))
+
+(defun open-rectangle (start end)
+ "Blank out rectangle with corners at point and mark, shifting text right.
+The text previously in the region is not overwritten by the blanks,
+but insted winds up to the right of the rectangle."
+ (interactive "r")
+ (operate-on-rectangle 'open-rectangle-line start end nil))
+
+(defun open-rectangle-line (startpos begextra endextra)
+ (let ((column (+ (current-column) begextra endextra)))
+ (goto-char startpos)
+ (let ((ocol (current-column)))
+ (skip-chars-forward " \t")
+ (setq column (+ column (- (current-column) ocol))))
+ (delete-region (point)
+ (progn (skip-chars-backward " \t")
+ (point)))
+ (indent-to column)))
+
+(defun clear-rectangle (start end)
+ "Blank out rectangle with corners at point and mark.
+The text previously in the region is overwritten by the blanks.
+When called from a program, requires two args which specify the corners."
+ (interactive "r")
+ (operate-on-rectangle 'clear-rectangle-line start end t))
+
+(defun clear-rectangle-line (startpos begextra endextra)
+ (skip-chars-forward " \t")
+ (let ((column (+ (current-column) endextra)))
+ (delete-region (point)
+ (progn (goto-char startpos)
+ (skip-chars-backward " \t")
+ (point)))
+ (indent-to column)))
+
+(defun rectangle-coerce-tab (column)
+ (let ((aftercol (current-column))
+ (indent-tabs-mode nil))
+ (delete-char -1)
+ (indent-to aftercol)
+ (backward-char (- aftercol column))))
diff --git a/lisp/tabify.el b/lisp/tabify.el
new file mode 100644
index 00000000000..2d660c82c61
--- /dev/null
+++ b/lisp/tabify.el
@@ -0,0 +1,51 @@
+;; Tab conversion commands for Emacs
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defun untabify (start end)
+ "Convert all tabs in region to multiple spaces, preserving columns.
+The variable tab-width controls the action."
+ (interactive "r")
+ (save-excursion
+ (save-restriction
+ (narrow-to-region start end)
+ (goto-char start)
+ (while (search-forward "\t" nil t) ; faster than re-search
+ (let ((start (point))
+ (column (current-column))
+ (indent-tabs-mode nil))
+ (skip-chars-backward "\t")
+ (delete-region start (point))
+ (indent-to column))))))
+
+(defun tabify (start end)
+ "Convert multiple spaces in region to tabs when possible.
+A group of spaces is partially replaced by tabs
+when this can be done without changing the column they end at.
+The variable tab-width controls the action."
+ (interactive "r")
+ (save-excursion
+ (save-restriction
+ (narrow-to-region start end)
+ (goto-char start)
+ (while (re-search-forward "[ \t][ \t][ \t]*" nil t)
+ (let ((column (current-column))
+ (indent-tabs-mode t))
+ (delete-region (match-beginning 0) (point))
+ (indent-to column))))))
diff --git a/lisp/textmodes/nroff-mode.el b/lisp/textmodes/nroff-mode.el
new file mode 100644
index 00000000000..16e1445080b
--- /dev/null
+++ b/lisp/textmodes/nroff-mode.el
@@ -0,0 +1,203 @@
+;; GNU Emacs major mode for editing nroff source
+;; Copyright (C) 1985, 1986 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+
+(defvar nroff-mode-abbrev-table nil
+ "Abbrev table used while in nroff mode.")
+
+(defvar nroff-mode-map nil
+ "Major mode keymap for nroff-mode buffers")
+(if (not nroff-mode-map)
+ (progn
+ (setq nroff-mode-map (make-sparse-keymap))
+ (define-key nroff-mode-map "\t" 'tab-to-tab-stop)
+ (define-key nroff-mode-map "\es" 'center-line)
+ (define-key nroff-mode-map "\e?" 'count-text-lines)
+ (define-key nroff-mode-map "\n" 'electric-nroff-newline)
+ (define-key nroff-mode-map "\en" 'forward-text-line)
+ (define-key nroff-mode-map "\ep" 'backward-text-line)))
+
+(defun nroff-mode ()
+ "Major mode for editing text intended for nroff to format.
+\\{nroff-mode-map}
+Turning on Nroff mode runs text-mode-hook, then nroff-mode-hook.
+Also, try nroff-electric-mode, for automatically inserting
+closing requests for requests that are used in matched pairs."
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map nroff-mode-map)
+ (setq mode-name "Nroff")
+ (setq major-mode 'nroff-mode)
+ (set-syntax-table text-mode-syntax-table)
+ (setq local-abbrev-table nroff-mode-abbrev-table)
+ (make-local-variable 'nroff-electric-mode)
+ ;; now define a bunch of variables for use by commands in this mode
+ (make-local-variable 'page-delimiter)
+ (setq page-delimiter "^\\.\\(bp\\|SK\\|OP\\)")
+ (make-local-variable 'paragraph-start)
+ (setq paragraph-start (concat "^[.']\\|" paragraph-start))
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-separate (concat "^[.']\\|" paragraph-separate))
+ ;; comment syntax added by mit-erl!gildea 18 Apr 86
+ (make-local-variable 'comment-start)
+ (setq comment-start "\\\" ")
+ (make-local-variable 'comment-start-skip)
+ (setq comment-start-skip "\\\\\"[ \t]*")
+ (make-local-variable 'comment-column)
+ (setq comment-column 24)
+ (make-local-variable 'comment-indent-hook)
+ (setq comment-indent-hook 'nroff-comment-indent)
+ (run-hooks 'text-mode-hook 'nroff-mode-hook))
+
+;;; Compute how much to indent a comment in nroff/troff source.
+;;; By mit-erl!gildea April 86
+(defun nroff-comment-indent ()
+ "Compute indent for an nroff/troff comment.
+Puts a full-stop before comments on a line by themselves."
+ (let ((pt (point)))
+ (unwind-protect
+ (progn
+ (skip-chars-backward " \t")
+ (if (bolp)
+ (progn
+ (setq pt (1+ pt))
+ (insert ?.)
+ 1)
+ (if (save-excursion
+ (backward-char 1)
+ (looking-at "^[.']"))
+ 1
+ (max comment-column
+ (* 8 (/ (+ (current-column)
+ 9) 8)))))) ; add 9 to ensure at least two blanks
+ (goto-char pt))))
+
+(defun count-text-lines (start end &optional print)
+ "Count lines in region, except for nroff request lines.
+All lines not starting with a period are counted up.
+Interactively, print result in echo area.
+Noninteractively, return number of non-request lines from START to END."
+ (interactive "r\np")
+ (if print
+ (message "Region has %d text lines" (count-text-lines start end))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region start end)
+ (goto-char (point-min))
+ (- (buffer-size) (forward-text-line (buffer-size)))))))
+
+(defun forward-text-line (&optional cnt)
+ "Go forward one nroff text line, skipping lines of nroff requests.
+An argument is a repeat count; if negative, move backward."
+ (interactive "p")
+ (if (not cnt) (setq cnt 1))
+ (while (and (> cnt 0) (not (eobp)))
+ (forward-line 1)
+ (while (and (not (eobp)) (looking-at "[.']."))
+ (forward-line 1))
+ (setq cnt (- cnt 1)))
+ (while (and (< cnt 0) (not (bobp)))
+ (forward-line -1)
+ (while (and (not (bobp))
+ (looking-at "[.']."))
+ (forward-line -1))
+ (setq cnt (+ cnt 1)))
+ cnt)
+
+(defun backward-text-line (&optional cnt)
+ "Go backward one nroff text line, skipping lines of nroff requests.
+An argument is a repeat count; negative means move forward."
+ (interactive "p")
+ (forward-text-line (- cnt)))
+
+(defconst nroff-brace-table
+ '((".(b" . ".)b")
+ (".(l" . ".)l")
+ (".(q" . ".)q")
+ (".(c" . ".)c")
+ (".(x" . ".)x")
+ (".(z" . ".)z")
+ (".(d" . ".)d")
+ (".(f" . ".)f")
+ (".LG" . ".NL")
+ (".SM" . ".NL")
+ (".LD" . ".DE")
+ (".CD" . ".DE")
+ (".BD" . ".DE")
+ (".DS" . ".DE")
+ (".DF" . ".DE")
+ (".FS" . ".FE")
+ (".KS" . ".KE")
+ (".KF" . ".KE")
+ (".LB" . ".LE")
+ (".AL" . ".LE")
+ (".BL" . ".LE")
+ (".DL" . ".LE")
+ (".ML" . ".LE")
+ (".RL" . ".LE")
+ (".VL" . ".LE")
+ (".RS" . ".RE")
+ (".TS" . ".TE")
+ (".EQ" . ".EN")
+ (".PS" . ".PE")
+ (".BS" . ".BE")
+ (".G1" . ".G2") ; grap
+ (".na" . ".ad b")
+ (".nf" . ".fi")
+ (".de" . "..")))
+
+(defun electric-nroff-newline (arg)
+ "Insert newline for nroff mode; special if electric-nroff mode.
+In electric-nroff-mode, if ending a line containing an nroff opening request,
+automatically inserts the matching closing request after point."
+ (interactive "P")
+ (let ((completion (save-excursion
+ (beginning-of-line)
+ (and (null arg)
+ nroff-electric-mode
+ (<= (point) (- (point-max) 3))
+ (cdr (assoc (buffer-substring (point)
+ (+ 3 (point)))
+ nroff-brace-table)))))
+ (needs-nl (not (looking-at "[ \t]*$"))))
+ (if (null completion)
+ (newline (prefix-numeric-value arg))
+ (save-excursion
+ (insert "\n\n" completion)
+ (if needs-nl (insert "\n")))
+ (forward-char 1))))
+
+(defun electric-nroff-mode (&optional arg)
+ "Toggle nroff-electric-newline minor mode
+Nroff-electric-newline forces emacs to check for an nroff
+request at the beginning of the line, and insert the
+matching closing request if necessary.
+This command toggles that mode (off->on, on->off),
+with an argument, turns it on iff arg is positive, otherwise off."
+ (interactive "P")
+ (or (eq major-mode 'nroff-mode) (error "Must be in nroff mode"))
+ (or (assq 'nroff-electric-mode minor-mode-alist)
+ (setq minor-mode-alist (append minor-mode-alist
+ (list '(nroff-electric-mode
+ " Electric")))))
+ (setq nroff-electric-mode
+ (cond ((null arg) (null nroff-electric-mode))
+ (t (> (prefix-numeric-value arg) 0)))))
+
diff --git a/lisp/textmodes/page.el b/lisp/textmodes/page.el
new file mode 100644
index 00000000000..19b29d02f08
--- /dev/null
+++ b/lisp/textmodes/page.el
@@ -0,0 +1,123 @@
+;; Page motion commands for emacs.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defun forward-page (&optional count)
+ "Move forward to page boundary. With arg, repeat, or go back if negative.
+A page boundary is any line whose beginning matches the regexp page-delimiter."
+ (interactive "p")
+ (or count (setq count 1))
+ (while (and (> count 0) (not (eobp)))
+ (if (re-search-forward page-delimiter nil t)
+ nil
+ (goto-char (point-max)))
+ (setq count (1- count)))
+ (while (and (< count 0) (not (bobp)))
+ (forward-char -1)
+ (if (re-search-backward page-delimiter nil t)
+ (goto-char (match-end 0))
+ (goto-char (point-min)))
+ (setq count (1+ count))))
+
+(defun backward-page (&optional count)
+ "Move backward to page boundary. With arg, repeat, or go fwd if negative.
+A page boundary is any line whose beginning matches the regexp page-delimiter."
+ (interactive "p")
+ (or count (setq count 1))
+ (forward-page (- count)))
+
+(defun mark-page (&optional arg)
+ "Put mark at end of page, point at beginning.
+A numeric arg specifies to move forward or backward by that many pages,
+thus marking a page other than the one point was originally in."
+ (interactive "P")
+ (setq arg (if arg (prefix-numeric-value arg) 0))
+ (if (> arg 0)
+ (forward-page arg)
+ (if (< arg 0)
+ (forward-page (1- arg))))
+ (forward-page)
+ (push-mark nil t)
+ (forward-page -1))
+
+(defun narrow-to-page (&optional arg)
+ "Make text outside current page invisible.
+A numeric arg specifies to move forward or backward by that many pages,
+thus showing a page other than the one point was originally in."
+ (interactive "P")
+ (setq arg (if arg (prefix-numeric-value arg) 0))
+ (save-excursion
+ (widen)
+ (if (> arg 0)
+ (forward-page arg)
+ (if (< arg 0)
+ (forward-page (1- arg))))
+ ;; Find the end of the page.
+ (forward-page)
+ ;; If we stopped due to end of buffer, stay there.
+ ;; If we stopped after a page delimiter, put end of restriction
+ ;; at the beginning of that line.
+ (if (save-excursion (beginning-of-line)
+ (looking-at page-delimiter))
+ (beginning-of-line))
+ (narrow-to-region (point)
+ (progn
+ ;; Find the top of the page.
+ (forward-page -1)
+ ;; If we found beginning of buffer, stay there.
+ ;; If extra text follows page delimiter on same line,
+ ;; include it.
+ ;; Otherwise, show text starting with following line.
+ (if (and (eolp) (not (bobp)))
+ (forward-line 1))
+ (point)))))
+
+(defun count-lines-page ()
+ "Report number of lines on current page, and how many are before or after point."
+ (interactive)
+ (save-excursion
+ (let ((opoint (point)) beg end
+ total before after)
+ (forward-page)
+ (beginning-of-line)
+ (or (looking-at page-delimiter)
+ (end-of-line))
+ (setq end (point))
+ (backward-page)
+ (setq beg (point))
+ (setq total (count-lines beg end)
+ before (count-lines beg opoint)
+ after (count-lines opoint end))
+ (message "Page has %d lines (%d + %d)" total before after))))
+
+(defun what-page ()
+ "Print page and line number of point."
+ (interactive)
+ (save-restriction
+ (widen)
+ (save-excursion
+ (beginning-of-line)
+ (let ((count 1)
+ (opoint (point)))
+ (goto-char 1)
+ (while (re-search-forward page-delimiter opoint t)
+ (setq count (1+ count)))
+ (message "Page %d, line %d"
+ count
+ (1+ (count-lines (point) opoint)))))))
diff --git a/lisp/textmodes/paragraphs.el b/lisp/textmodes/paragraphs.el
new file mode 100644
index 00000000000..c0bd7793a10
--- /dev/null
+++ b/lisp/textmodes/paragraphs.el
@@ -0,0 +1,205 @@
+;; Paragraph and sentence parsing.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defvar paragraph-ignore-fill-prefix nil
+ "Non-nil means the paragraph commands are not affected by fill-prefix.
+This is desirable in modes where blank lines are the paragraph delimiters.")
+
+(defun forward-paragraph (&optional arg)
+ "Move forward to end of paragraph.
+With arg N, do it N times; negative arg -N means move forward N paragraphs.
+
+A line which `paragraph-start' matches either separates paragraphs
+\(if `paragraph-separate' matches it also) or is the first line of a paragraph.
+A paragraph end is the beginning of a line which is not part of the paragraph
+to which the end of the previous line belongs, or the end of the buffer."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (let* ((fill-prefix-regexp
+ (and fill-prefix (not (equal fill-prefix ""))
+ (not paragraph-ignore-fill-prefix)
+ (regexp-quote fill-prefix)))
+ (paragraph-separate
+ (if fill-prefix-regexp
+ (concat paragraph-separate "\\|^"
+ fill-prefix-regexp "[ \t]*$")
+ paragraph-separate)))
+ (while (< arg 0)
+ (if (and (not (looking-at paragraph-separate))
+ (re-search-backward "^\n" (max (1- (point)) (point-min)) t))
+ nil
+ (forward-char -1) (beginning-of-line)
+ (while (and (not (bobp)) (looking-at paragraph-separate))
+ (forward-line -1))
+ (end-of-line)
+ ;; Search back for line that starts or separates paragraphs.
+ (if (if fill-prefix-regexp
+ ;; There is a fill prefix; it overrides paragraph-start.
+ (progn
+ (while (progn (beginning-of-line)
+ (and (not (bobp))
+ (not (looking-at paragraph-separate))
+ (looking-at fill-prefix-regexp)))
+ (forward-line -1))
+ (not (bobp)))
+ (re-search-backward paragraph-start nil t))
+ ;; Found one.
+ (progn
+ (while (and (not (eobp)) (looking-at paragraph-separate))
+ (forward-line 1))
+ (if (eq (char-after (- (point) 2)) ?\n)
+ (forward-line -1)))
+ ;; No starter or separator line => use buffer beg.
+ (goto-char (point-min))))
+ (setq arg (1+ arg)))
+ (while (> arg 0)
+ (beginning-of-line)
+ (while (prog1 (and (not (eobp))
+ (looking-at paragraph-separate))
+ (forward-line 1)))
+ (if fill-prefix-regexp
+ ;; There is a fill prefix; it overrides paragraph-start.
+ (while (and (not (eobp))
+ (not (looking-at paragraph-separate))
+ (looking-at fill-prefix-regexp))
+ (forward-line 1))
+ (if (re-search-forward paragraph-start nil t)
+ (goto-char (match-beginning 0))
+ (goto-char (point-max))))
+ (setq arg (1- arg)))))
+
+(defun backward-paragraph (&optional arg)
+ "Move backward to start of paragraph.
+With arg N, do it N times; negative arg -N means move forward N paragraphs.
+
+A paragraph start is the beginning of a line which is a first-line-of-paragraph
+or which is ordinary text and follows a paragraph-separating line; except:
+if the first real line of a paragraph is preceded by a blank line,
+the paragraph starts at that blank line.
+See forward-paragraph for more information."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (forward-paragraph (- arg)))
+
+(defun mark-paragraph ()
+ "Put point at beginning of this paragraph, mark at end.
+The paragraph marked is the one that contains point or follows point."
+ (interactive)
+ (forward-paragraph 1)
+ (push-mark nil t)
+ (backward-paragraph 1))
+
+(defun kill-paragraph (arg)
+ "Kill forward to end of paragraph.
+With arg N, kill forward to Nth end of paragraph;
+negative arg -N means kill backward to Nth start of paragraph."
+ (interactive "*p")
+ (kill-region (point) (progn (forward-paragraph arg) (point))))
+
+(defun backward-kill-paragraph (arg)
+ "Kill back to start of paragraph.
+With arg N, kill back to Nth start of paragraph;
+negative arg -N means kill forward to Nth end of paragraph."
+ (interactive "*p")
+ (kill-region (point) (progn (backward-paragraph arg) (point))))
+
+(defun transpose-paragraphs (arg)
+ "Interchange this (or next) paragraph with previous one."
+ (interactive "*p")
+ (transpose-subr 'forward-paragraph arg))
+
+(defun start-of-paragraph-text ()
+ (let ((opoint (point)) npoint)
+ (forward-paragraph -1)
+ (setq npoint (point))
+ (skip-chars-forward " \t\n")
+ (if (>= (point) opoint)
+ (progn
+ (goto-char npoint)
+ (if (> npoint (point-min))
+ (start-of-paragraph-text))))))
+
+(defun end-of-paragraph-text ()
+ (let ((opoint (point)))
+ (forward-paragraph 1)
+ (if (eq (preceding-char) ?\n) (forward-char -1))
+ (if (<= (point) opoint)
+ (progn
+ (forward-char 1)
+ (if (< (point) (point-max))
+ (end-of-paragraph-text))))))
+
+(defun forward-sentence (&optional arg)
+ "Move forward to next sentence-end. With argument, repeat.
+With negative argument, move backward repeatedly to sentence-beginning.
+
+The variable `sentence-end' is a regular expression that matches ends
+of sentences. Also, every paragraph boundary terminates sentences as
+well."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (while (< arg 0)
+ (let ((par-beg (save-excursion (start-of-paragraph-text) (point))))
+ (if (re-search-backward (concat sentence-end "[^ \t\n]") par-beg t)
+ (goto-char (1- (match-end 0)))
+ (goto-char par-beg)))
+ (setq arg (1+ arg)))
+ (while (> arg 0)
+ (let ((par-end (save-excursion (end-of-paragraph-text) (point))))
+ (if (re-search-forward sentence-end par-end t)
+ (skip-chars-backward " \t\n")
+ (goto-char par-end)))
+ (setq arg (1- arg))))
+
+(defun backward-sentence (&optional arg)
+ "Move backward to start of sentence. With arg, do it arg times.
+See forward-sentence for more information."
+ (interactive "p")
+ (or arg (setq arg 1))
+ (forward-sentence (- arg)))
+
+(defun kill-sentence (&optional arg)
+ "Kill from point to end of sentence.
+With arg, repeat; negative arg -N means kill back to Nth start of sentence."
+ (interactive "*p")
+ (let ((beg (point)))
+ (forward-sentence arg)
+ (kill-region beg (point))))
+
+(defun backward-kill-sentence (&optional arg)
+ "Kill back from point to start of sentence.
+With arg, repeat, or kill forward to Nth end of sentence if negative arg -N."
+ (interactive "*p")
+ (let ((beg (point)))
+ (backward-sentence arg)
+ (kill-region beg (point))))
+
+(defun mark-end-of-sentence (arg)
+ "Put mark at end of sentence. Arg works as in forward-sentence."
+ (interactive "p")
+ (push-mark
+ (save-excursion
+ (forward-sentence arg)
+ (point))))
+
+(defun transpose-sentences (arg)
+ "Interchange this (next) and previous sentence."
+ (interactive "*p")
+ (transpose-subr 'forward-sentence arg))
diff --git a/lisp/textmodes/refbib.el b/lisp/textmodes/refbib.el
new file mode 100644
index 00000000000..3b376cdd90b
--- /dev/null
+++ b/lisp/textmodes/refbib.el
@@ -0,0 +1,715 @@
+;; Convert refer-style bibliographic entries to ones usable by latex bib
+;; Copyright (C) 1989 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;; Use: from a buffer containing the refer-style bibliography,
+;; M-x r2b-convert-buffer
+;; Program will prompt for an output buffer name, and will log
+;; warnings during the conversion process in the buffer *Log*.
+
+; HISTORY
+; 9/88, created
+; modified 1/19/89, allow books with editor but no author;
+; added %O ordering field;
+; appended illegal multiple fields, instead of
+; discarding;
+; added rule, a tech report whose %R number
+; contains "ISBN" is really a book
+; added rule, anything with an editor is a book
+; or a proceedings
+; added 'manual type, for items with institution
+; but no author or editor
+; fixed bug so trailing blanks are trimmed
+; added 'proceedings type
+; used "organization" field for proceedings
+; modified 2/16/89, updated help messages
+; modified 2/23/89, include capitalize stop words in r2b stop words,
+; fixed problems with contractions (e.g. it's),
+; caught multiple stop words in a row
+; modified 3/1/89, fixed capitialize-title for first words all caps
+; modified 3/15/89, allow use of " to delimit fields
+; modified 4/18/89, properly "quote" special characters on output
+(provide 'refer-to-bibtex)
+;**********************************************************
+; User Parameters
+
+(defvar r2b-trace-on nil "*trace conversion")
+
+(defvar r2b-journal-abbrevs
+ '(
+ )
+ " Abbreviation list for journal names.
+If the car of an element matches a journal name exactly, it is replaced by
+the cadr when output. Braces must be included if replacement is a
+{string}, but not if replacement is a bibtex abbreviation. The cadr
+may be eliminated if is exactly the same as the car.
+ Because titles are capitalized before matching, the abbreviation
+for the journal name should be listed as beginning with a capital
+letter, even if it really doesn't.
+ For example, a value of '((\"Aij\" \"{Artificial Intelligence}\")
+(\"Ijcai81\" \"ijcai7\")) would expand Aij to the text string
+\"Artificial Intelligence\", but would replace Ijcai81 with the
+BibTeX macro \"ijcai7\".")
+
+(defvar r2b-booktitle-abbrevs
+ '(
+ )
+ " Abbreviation list for book and proceedings names. If the car of
+an element matches a title or booktitle exactly, it is replaced by
+the cadr when output. Braces must be included if replacement is
+a {string}, but not if replacement is a bibtex abbreviation. The cadr
+may be eliminated if is exactly the same as the car.
+ Because titles are capitalized before matching, the abbreviated title
+should be listed as beginning with a capital letter, even if it doesn't.
+ For example, a value of '((\"Aij\" \"{Artificial Intelligence}\")
+(\"Ijcai81\" \"ijcai7\")) would expand Aij to the text string
+\"Artificial Intelligence\", but would replace Ijcai81 with the
+BibTeX macro \"ijcai7\".")
+
+(defvar r2b-proceedings-list
+ '()
+ " Assoc list of books or journals which are really conference proceedings,
+but whose name and whose abbrev expansion (as defined in r2b-journal-abbrevs
+and r2b-booktitle-abbrevs) does not contain the words 'conference' or
+'proceedings'. (Those cases are handled automatically.)
+The entry must match the given data exactly.
+ Because titles are capitalized before matching, the items in this list
+should begin with a capital letter.
+ For example, suppose the title \"Ijcai81\" is used for the proceedings of
+a conference, and it's expansion is the BibTeX macro \"ijcai7\". Then
+r2b-proceedings-list should be '((\"Ijcai81\") ...). If instead its
+expansion were \"Proceedings of the Seventh International Conference
+on Artificial Intelligence\", then you would NOT need to include Ijcai81
+in r2b-proceedings-list (although it wouldn't cause an error).")
+
+(defvar r2b-additional-stop-words
+ "Some\\|What"
+ "Words other than the capitialize-title-stop-words
+which are not to be used to build the citation key")
+
+
+(defvar r2b-delimit-with-quote
+ t
+ "*If true, then use \" to delimit fields, otherwise use braces")
+
+;**********************************************************
+; Utility Functions
+
+(defvar capitalize-title-stop-words
+ (concat
+ "the\\|and\\|of\\|is\\|a\\|an\\|of\\|for\\|in\\|to\\|in\\|on\\|at\\|"
+ "by\\|with\\|that\\|its")
+ "Words not to be capitialized in a title (unless they are the first
+word in the title)")
+
+(defvar capitalize-title-stop-regexp
+ (concat "\\(" capitalize-title-stop-words "\\)\\(\\b\\|'\\)"))
+
+(defun capitalize-title-region (begin end)
+ "Like capitalize-region, but don't capitalize stop words, except the first"
+ (interactive "r")
+ (let ((case-fold-search nil) (orig-syntax-table (syntax-table)))
+ (unwind-protect
+ (save-restriction
+ (set-syntax-table text-mode-syntax-table)
+ (narrow-to-region begin end)
+ (goto-char (point-min))
+ (if (looking-at "[A-Z][a-z]*[A-Z]")
+ (forward-word 1)
+ (capitalize-word 1))
+ (while (re-search-forward "\\<" nil t)
+ (if (looking-at "[A-Z][a-z]*[A-Z]")
+ (forward-word 1)
+ (if (let ((case-fold-search t))
+ (looking-at capitalize-title-stop-regexp))
+ (downcase-word 1)
+ (capitalize-word 1)))
+ ))
+ (set-syntax-table orig-syntax-table))))
+
+
+(defun capitalize-title (s)
+ "Like capitalize, but don't capitalize stop words, except the first"
+ (save-excursion
+ (set-buffer (get-buffer-create "$$$Scratch$$$"))
+ (erase-buffer)
+ (insert s)
+ (capitalize-title-region (point-min) (point-max))
+ (buffer-string)))
+
+;*********************************************************
+(defun r2b-reset ()
+ "unbind defvars, for debugging"
+ (interactive)
+ (makunbound 'r2b-journal-abbrevs)
+ (makunbound 'r2b-booktitle-abbrevs)
+ (makunbound 'r2b-proceedings-list)
+ (makunbound 'capitalize-title-stop-words)
+ (makunbound 'capitalize-title-stop-regexp)
+ (makunbound 'r2b-additional-stop-words)
+ (makunbound 'r2b-stop-regexp)
+ )
+
+(defvar r2b-stop-regexp
+ (concat "\\`\\(\\("
+ r2b-additional-stop-words "\\|" capitalize-title-stop-words
+ "\\)\\('\\w*\\)?\\W+\\)*\\([A-Z0-9]+\\)"))
+
+
+(defun r2b-trace (&rest args)
+ (if r2b-trace-on
+ (progn
+ (apply (function message) args)
+ (sit-for 0)
+ )))
+
+(defun r2b-match (exp)
+ "returns string matched in current buffer"
+ (buffer-substring (match-beginning exp) (match-end exp)))
+
+(defvar r2b-out-buf-name "*Out*" "*output from refer-to-bibtex" )
+(defvar r2b-log-name "*Log*" "*logs errors from refer-to-bibtex" )
+(defvar r2b-in-buf nil)
+(defvar r2b-out-buf nil)
+(defvar r2b-log nil)
+
+(defvar r2b-error-found nil)
+
+(setq r2b-variables '(
+ r2b-error-found
+ r2bv-author
+ r2bv-primary-author
+ r2bv-date
+ r2bv-year
+ r2bv-decade
+ r2bv-month
+ r2bv-title
+ r2bv-title-first-word
+ r2bv-editor
+ r2bv-annote
+ r2bv-tr
+ r2bv-address
+ r2bv-institution
+ r2bv-keywords
+ r2bv-booktitle
+ r2bv-journal
+ r2bv-volume
+ r2bv-number
+ r2bv-pages
+ r2bv-booktitle
+ r2bv-kn
+ r2bv-publisher
+ r2bv-organization
+ r2bv-school
+ r2bv-type
+ r2bv-where
+ r2bv-note
+ r2bv-ordering
+ ))
+
+(defun r2b-clear-variables ()
+ "set all global vars used by r2b to nil"
+ (let ((vars r2b-variables))
+ (while vars
+ (set (car vars) nil)
+ (setq vars (cdr vars)))
+ ))
+
+(defun r2b-warning (&rest args)
+ (setq r2b-error-found t)
+ (princ (apply (function format) args) r2b-log)
+ (princ "\n" r2b-log)
+ (princ "\n" r2b-out-buf)
+ (princ "% " r2b-out-buf)
+ (princ (apply (function format) args) r2b-out-buf)
+ )
+
+(defun r2b-get-field (var field &optional unique required capitalize)
+ "Set VAR to string value of FIELD, if any. If none, VAR is set to
+nil. If multiple fields appear, then separate values with the
+'\\nand\\t\\t', unless UNIQUE is non-nil, in which case log a warning
+and just concatenate the values. Trim off leading blanks and tabs on
+first line, and trailing blanks and tabs of every line. Log a warning
+and set VAR to the empty string if REQUIRED is true. Capitalize as a
+title if CAPITALIZE is true. Returns value of VAR."
+ (let (item val (not-past-end t))
+ (r2b-trace "snarfing %s" field)
+ (goto-char (point-min))
+ (while (and not-past-end
+ (re-search-forward
+ (concat "^" field "\\b[ \t]*\\(.*[^ \t\n]\\)[ \t]*") nil t))
+ (setq item (r2b-match 1))
+ (while (and (setq not-past-end (zerop (forward-line 1)))
+ (not (looking-at "[ \t]*$\\|%")))
+ (looking-at "\\(.*[^ \t\n]\\)[ \t]*$")
+ (setq item (concat item "\n" (r2b-match 1)))
+ )
+ (if (null val)
+ (setq val item)
+ (if unique
+ (progn
+ (r2b-warning "*Illegal multiple field %s %s" field item)
+ (setq val (concat val "\n" item))
+ )
+ (setq val (concat val "\n\t\tand " item))
+ )
+ )
+ )
+ (if (and val capitalize)
+ (setq val (capitalize-title val)))
+ (set var val)
+ (if (and (null val) required)
+ (r2b-require var))
+ ))
+
+(defun r2b-set-match (var n regexp string )
+ "set VAR to the Nth subpattern in REGEXP matched by STRING, or nil if none"
+ (set var
+ (if (and (stringp string) (string-match regexp string))
+ (substring string (match-beginning n) (match-end n))
+ nil)
+ )
+ )
+
+(defvar r2b-month-abbrevs
+ '(("jan") ("feb") ("mar") ("apr") ("may") ("jun") ("jul") ("aug")
+ ("sep") ("oct") ("nov") ("dec")))
+
+(defun r2b-convert-month ()
+ "Try to convert r2bv-month to a standard 3 letter name"
+ (if r2bv-month
+ (let ((months r2b-month-abbrevs))
+ (if (string-match "[^0-9]" r2bv-month)
+ (progn
+ (while (and months (not (string-match (car (car months))
+ r2bv-month)))
+ (setq months (cdr months)))
+ (if months
+ (setq r2bv-month (car (car months)))))
+ (progn
+ (setq months (car (read-from-string r2bv-month)))
+ (if (and (numberp months)
+ (> months 0)
+ (< months 13))
+ (setq r2bv-month (car (nth months r2b-month-abbrevs)))
+ (progn
+ (r2b-warning "* Ridiculous month")
+ (setq r2bv-month nil))
+ ))
+ ))
+ )
+ )
+
+(defun r2b-snarf-input ()
+ "parse buffer into global variables"
+ (let ((case-fold-search t))
+ (r2b-trace "snarfing...")
+ (sit-for 0)
+ (set-buffer r2b-in-buf)
+ (goto-char (point-min))
+ (princ " " r2b-log)
+ (princ (buffer-substring (point) (progn (end-of-line) (point))) r2b-log)
+ (terpri r2b-log)
+
+ (r2b-get-field 'r2bv-author "%A")
+ (r2b-get-field 'r2bv-editor "%E")
+ (cond
+ (r2bv-author
+ (r2b-set-match 'r2bv-primary-author 1
+ "\\b\\(\\w+\\)[ \t]*\\($\\|,\\)" r2bv-author)
+ )
+ (r2bv-editor
+ (r2b-set-match 'r2bv-primary-author 1
+ "\\b\\(\\w+\\)[ \t]*\\($\\|,\\)" r2bv-editor)
+ )
+ (t
+ (setq r2bv-primary-author "")
+ )
+ )
+
+ (r2b-get-field 'r2bv-date "%D" t t)
+ (r2b-set-match 'r2bv-year 0 "[12][0-9][0-9][0-9]" r2bv-date)
+ (and (null r2bv-year)
+ (r2b-set-match 'r2bv-year 1 "[^0-9]\\([0-9][0-9]\\)$" r2bv-date)
+ (setq r2bv-year (concat "19" r2bv-year)))
+ (r2b-set-match 'r2bv-decade 1 "..\\(..\\)" r2bv-year)
+ (r2b-set-match 'r2bv-month 0
+ "[0-9]+/\\|[a-zA-Z]+" r2bv-date)
+ (if (and (stringp r2bv-month) (string-match "\\(.*\\)/$" r2bv-month))
+ (setq r2bv-month (substring r2bv-month 0 (match-end 1))))
+ (r2b-convert-month)
+
+ (r2b-get-field 'r2bv-title "%T" t t t)
+ (r2b-set-match 'r2bv-title-first-word 4
+ r2b-stop-regexp
+ r2bv-title)
+
+ (r2b-get-field 'r2bv-annote "%X" t )
+ (r2b-get-field 'r2bv-tr "%R" t)
+ (r2b-get-field 'r2bv-address "%C" t)
+ (r2b-get-field 'r2bv-institution "%I" t)
+ (r2b-get-field 'r2bv-keywords "%K")
+ (r2b-get-field 'r2bv-booktitle "%B" t nil t)
+ (r2b-get-field 'r2bv-journal "%J" t nil t)
+ (r2b-get-field 'r2bv-volume "%V" t)
+ (r2b-get-field 'r2bv-number "%N" t)
+ (r2b-get-field 'r2bv-pages "%P" t)
+ (r2b-get-field 'r2bv-where "%W" t)
+ (r2b-get-field 'r2bv-ordering "%O" t)
+ )
+ )
+
+
+(defun r2b-put-field (field data &optional abbrevs)
+ "print bibtex FIELD = {DATA} if DATA not null; precede
+with a comma and newline; if ABBREVS list is given, then
+try to replace the {DATA} with an abbreviation"
+ (if data
+ (let (match nodelim multi-line index)
+ (cond
+ ((and abbrevs (setq match (assoc data abbrevs)))
+ (if (null (cdr match))
+ (setq data (car match))
+ (setq data (car (cdr match))))
+ (setq nodelim t))
+ ((and (not (equal data ""))
+ (not (string-match "[^0-9]" data)))
+ (setq nodelim t))
+ (t
+ (setq index 0)
+ (while (string-match "[\\~^]" data index)
+ (setq data (concat (substring data 0 (match-beginning 0))
+ "\\verb+"
+ (substring data (match-beginning 0) (match-end 0))
+ "+"
+ (substring data (match-end 0))))
+ (setq index (+ (match-end 0) 7)))
+ (setq index 0)
+ (while (string-match "[$&%#_{}]" data index)
+ (setq data (concat (substring data 0 (match-beginning 0))
+ "\\"
+ (substring data (match-beginning 0))))
+ (setq index (+ (match-end 0) 1)))
+ (setq index 0)
+ (if r2b-delimit-with-quote
+ (while (string-match "\"" data index)
+ (setq data (concat (substring data 0 (match-beginning 0))
+ "{\"}"
+ (substring data (match-end 0))))
+ (setq index (+ (match-end 0) 2))))
+ ))
+ (princ ", \n ")
+ (princ field)
+ (princ " =\t")
+ (if (not nodelim)
+ (if r2b-delimit-with-quote
+ (princ "\"")
+ (princ "{")))
+ (string-match ".*" data)
+ (if (> (match-end 0) 59)
+ (princ "\n"))
+ (princ data)
+ (if (not nodelim)
+ (if r2b-delimit-with-quote
+ (princ "\"")
+ (princ "}")))
+ )
+ ))
+
+
+(defun r2b-require (vars)
+ "If any of VARS is null, set to empty string and log error"
+ (cond
+ ((null vars))
+ ((listp vars) (r2b-require (car vars)) (r2b-require (cdr vars)))
+ (t
+ (if (null (symbol-value vars))
+ (progn
+ (r2b-warning "*Missing value for field %s" vars)
+ (set vars "")
+ )))
+ )
+ )
+
+
+(defmacro r2b-moveq (new old)
+ "set NEW to OLD and set OLD to nil"
+ (list 'progn (list 'setq new old) (list 'setq old 'nil)))
+
+(defun r2b-isa-proceedings (name)
+ "return t if NAME is the name of proceedings"
+ (and
+ name
+ (or
+ (string-match "proceedings\\|conference" name)
+ (assoc name r2b-proceedings-list)
+ (let ((match (assoc name r2b-booktitle-abbrevs)))
+ (and match
+ (string-match "proceedings\\|conference" (car (cdr match)))))
+ )))
+
+(defun r2b-isa-university (name)
+ "return t if NAME is a university or similar organization,
+but not a publisher"
+ (and
+ name
+ (string-match "university" name)
+ (not (string-match "press" name))
+
+ ))
+
+(defun r2b-barf-output ()
+ "generate bibtex based on global variables"
+ (let ((standard-output r2b-out-buf) (case-fold-search t) match)
+
+ (r2b-trace "...barfing")
+ (sit-for 0)
+ (set-buffer r2b-out-buf)
+
+ (setq r2bv-kn (concat r2bv-primary-author r2bv-decade
+ r2bv-title-first-word))
+
+ (setq r2bv-entry-kind
+ (cond
+ ((r2b-isa-proceedings r2bv-journal)
+ (r2b-moveq r2bv-booktitle r2bv-journal)
+ (if (r2b-isa-university r2bv-institution)
+ (r2b-moveq r2bv-organization r2bv-institution)
+ (r2b-moveq r2bv-publisher r2bv-institution))
+ (r2b-moveq r2bv-note r2bv-tr)
+ (r2b-require 'r2bv-author)
+ 'inproceedings)
+ ((r2b-isa-proceedings r2bv-booktitle)
+ (if (r2b-isa-university r2bv-institution)
+ (r2b-moveq r2bv-organization r2bv-institution)
+ (r2b-moveq r2bv-publisher r2bv-institution))
+ (r2b-moveq r2bv-note r2bv-tr)
+ (r2b-require 'r2bv-author)
+ 'inproceedings)
+ ((and r2bv-tr (string-match "phd" r2bv-tr))
+ (r2b-moveq r2bv-school r2bv-institution)
+ (r2b-require 'r2bv-school )
+ (r2b-require 'r2bv-author)
+ 'phdthesis)
+ ((and r2bv-tr (string-match "master" r2bv-tr))
+ (r2b-moveq r2bv-school r2bv-institution)
+ (r2b-require 'r2bv-school )
+ (r2b-require 'r2bv-author)
+ 'mastersthesis)
+ ((and r2bv-tr (string-match "draft\\|unpublish" r2bv-tr))
+ (r2b-moveq r2bv-note r2bv-institution)
+ (r2b-require 'r2bv-author)
+ 'unpublished)
+ (r2bv-journal
+ (r2b-require 'r2bv-author)
+ 'article)
+ (r2bv-booktitle
+ (r2b-moveq r2bv-publisher r2bv-institution)
+ (r2b-moveq r2bv-note r2bv-tr)
+ (r2b-require 'r2bv-publisher)
+ (r2b-require 'r2bv-author)
+ 'incollection)
+ ((and r2bv-author
+ (null r2bv-editor)
+ (string-match "\\`personal communication\\'" r2bv-title))
+ 'misc)
+ ((r2b-isa-proceedings r2bv-title)
+ (if (r2b-isa-university r2bv-institution)
+ (r2b-moveq r2bv-organization r2bv-institution)
+ (r2b-moveq r2bv-publisher r2bv-institution))
+ (r2b-moveq r2bv-note r2bv-tr)
+ 'proceedings)
+ ((or r2bv-editor
+ (and r2bv-author
+ (or
+ (null r2bv-tr)
+ (string-match "\\bisbn\\b" r2bv-tr))))
+ (r2b-moveq r2bv-publisher r2bv-institution)
+ (r2b-moveq r2bv-note r2bv-tr)
+ (r2b-require 'r2bv-publisher)
+ (if (null r2bv-editor)
+ (r2b-require 'r2bv-author))
+ 'book)
+ (r2bv-tr
+ (r2b-require 'r2bv-institution)
+ (if (string-match
+ "\\`\\(\\(.\\|\n\\)+\\)[ \t\n]+\\([^ \t\n]\\)+\\'"
+ r2bv-tr)
+ (progn
+ (setq r2bv-type (substring r2bv-tr 0 (match-end 1)))
+ (setq r2bv-number (substring r2bv-tr
+ (match-beginning 3)))
+ (setq r2bv-tr nil))
+ (r2b-moveq r2bv-number r2bv-tr))
+ (r2b-require 'r2bv-author)
+ 'techreport)
+ (r2bv-institution
+ (r2b-moveq r2bv-organization r2bv-institution)
+ 'manual)
+ (t
+ 'misc)
+ ))
+
+ (r2b-require '( r2bv-year))
+
+ (if r2b-error-found
+ (princ "\n% Warning -- Errors During Conversion Next Entry\n"))
+
+ (princ "\n@")
+ (princ r2bv-entry-kind)
+ (princ "( ")
+ (princ r2bv-kn)
+
+ (r2b-put-field "author" r2bv-author )
+ (r2b-put-field "title" r2bv-title r2b-booktitle-abbrevs)
+ (r2b-put-field "year" r2bv-year )
+
+ (r2b-put-field "month" r2bv-month r2b-month-abbrevs)
+ (r2b-put-field "journal" r2bv-journal r2b-journal-abbrevs)
+ (r2b-put-field "volume" r2bv-volume)
+ (r2b-put-field "type" r2bv-type)
+ (r2b-put-field "number" r2bv-number)
+ (r2b-put-field "booktitle" r2bv-booktitle r2b-booktitle-abbrevs)
+ (r2b-put-field "editor" r2bv-editor)
+ (r2b-put-field "publisher" r2bv-publisher)
+ (r2b-put-field "institution" r2bv-institution)
+ (r2b-put-field "organization" r2bv-organization)
+ (r2b-put-field "school" r2bv-school)
+ (r2b-put-field "pages" r2bv-pages)
+ (r2b-put-field "address" r2bv-address)
+ (r2b-put-field "note" r2bv-note)
+ (r2b-put-field "keywords" r2bv-keywords)
+ (r2b-put-field "where" r2bv-where)
+ (r2b-put-field "ordering" r2bv-ordering)
+ (r2b-put-field "annote" r2bv-annote)
+
+ (princ " )\n")
+ )
+ )
+
+
+(defun r2b-convert-record (output-name)
+ "transform current bib entry and append to buffer OUTPUT;
+do M-x r2b-help for more info"
+ (interactive
+ (list (read-string "Output to buffer: " r2b-out-buf-name)))
+ (let (rec-end rec-begin not-done)
+ (setq r2b-out-buf-name output-name)
+ (setq r2b-out-buf (get-buffer-create output-name))
+ (setq r2b-in-buf (current-buffer))
+ (set-buffer r2b-out-buf)
+ (goto-char (point-max))
+ (setq r2b-log (get-buffer-create r2b-log-name))
+ (set-buffer r2b-log)
+ (goto-char (point-max))
+ (set-buffer r2b-in-buf)
+ (setq not-done (re-search-forward "[^ \t\n]" nil t))
+ (if not-done
+ (progn
+ (re-search-backward "^[ \t]*$" nil 2)
+ (re-search-forward "^%")
+ (beginning-of-line nil)
+ (setq rec-begin (point))
+ (re-search-forward "^[ \t]*$" nil 2)
+ (setq rec-end (point))
+ (narrow-to-region rec-begin rec-end)
+ (r2b-clear-variables)
+ (r2b-snarf-input)
+ (r2b-barf-output)
+ (set-buffer r2b-in-buf)
+ (widen)
+ (goto-char rec-end)
+ t)
+ nil
+ )
+ ))
+
+
+(defun r2b-convert-buffer (output-name)
+ "transform current buffer and append to buffer OUTPUT;
+do M-x r2b-help for more info"
+ (interactive
+ (list (read-string "Output to buffer: " r2b-out-buf-name)))
+ (save-excursion
+ (setq r2b-log (get-buffer-create r2b-log-name))
+ (set-buffer r2b-log)
+ (erase-buffer))
+ (widen)
+ (goto-char (point-min))
+ (message "Working, please be patient...")
+ (sit-for 0)
+ (while (r2b-convert-record output-name) t)
+ (message "Done, results in %s, errors in %s"
+ r2b-out-buf-name r2b-log-name)
+ )
+
+(defvar r2b-load-quietly nil "*Don't print help message when loaded")
+
+(defvar r2b-help-message
+" Refer to Bibtex Bibliography Conversion
+
+A refer-style database is of the form:
+
+%A Joe Blow
+%T Great Thoughts I've Thought
+%D 1977
+etc.
+
+This utility converts these kind of databases to bibtex form, for
+users of TeX and LaTex. Instructions:
+1. Visit the file containing the refer-style database.
+2. The command
+ M-x r2b-convert-buffer
+ converts the entire buffer, appending it's output by default in a
+ buffer named *Out*, and logging progress and errors in a buffer
+ named *Log*. The original file is never modified.
+ Note that results are appended to *Out*, so if that buffer
+ buffer already exists and contains material you don't want to
+ save, you should kill it first.
+3. Switch to the buffer *Out* and save it as a named file.
+4. To convert a single refer-style entry, simply position the cursor
+ at the entry and enter
+ M-x r2b-convert-record
+ Again output is appended to *Out* and errors are logged in *Log*.
+
+This utility is very robust and pretty smart about determining the
+type of the entry. It includes facilities for expanding refer macros
+to text, or substituting bibtex macros. Do M-x describe-variable on
+ r2b-journal-abbrevs
+ r2b-booktitle-abbrevs
+ r2b-proceedings-list
+for information on these features.
+
+If you don't want to see this help message when you load this utility,
+then include the following line in your .emacs file:
+ (setq r2b-load-quietly t)
+To see this message again, perform
+ M-x r2b-help")
+
+
+(defun r2b-help ()
+ "print help message"
+ (interactive)
+ (with-output-to-temp-buffer "*Help*"
+ (princ r2b-help-message)))
+
+(if (not r2b-load-quietly)
+ (r2b-help))
+
+(message "r2b loaded")
+
diff --git a/lisp/textmodes/spell.el b/lisp/textmodes/spell.el
new file mode 100644
index 00000000000..d7cd286141b
--- /dev/null
+++ b/lisp/textmodes/spell.el
@@ -0,0 +1,132 @@
+;; Spelling correction interface for Emacs.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defvar spell-command "spell"
+ "*Command to run the spell program.")
+
+(defvar spell-filter nil
+ "*Filter function to process text before passing it to spell program.
+This function might remove text-processor commands.
+nil means don't alter the text before checking it.")
+
+(defun spell-buffer ()
+ "Check spelling of every word in the buffer.
+For each incorrect word, you are asked for the correct spelling
+and then put into a query-replace to fix some or all occurrences.
+If you do not want to change a word, just give the same word
+as its \"correct\" spelling; then the query replace is skipped."
+ (interactive)
+ (spell-region (point-min) (point-max) "buffer"))
+
+(defun spell-word ()
+ "Check spelling of word at or before point.
+If it is not correct, ask user for the correct spelling
+and query-replace the entire buffer to substitute it."
+ (interactive)
+ (let (beg end spell-filter)
+ (save-excursion
+ (if (not (looking-at "\\<"))
+ (forward-word -1))
+ (setq beg (point))
+ (forward-word 1)
+ (setq end (point)))
+ (spell-region beg end (buffer-substring beg end))))
+
+(defun spell-region (start end &optional description)
+ "Like spell-buffer but applies only to region.
+Used in a program, applies from START to END.
+DESCRIPTION is an optional string naming the unit being checked:
+for example, \"word\"."
+ (interactive "r")
+ (let ((filter spell-filter)
+ (buf (get-buffer-create " *temp*")))
+ (save-excursion
+ (set-buffer buf)
+ (widen)
+ (erase-buffer))
+ (message "Checking spelling of %s..." (or description "region"))
+ (if (and (null filter) (= ?\n (char-after (1- end))))
+ (if (string= "spell" spell-command)
+ (call-process-region start end "spell" nil buf)
+ (call-process-region start end shell-file-name
+ nil buf nil "-c" spell-command))
+ (let ((oldbuf (current-buffer)))
+ (save-excursion
+ (set-buffer buf)
+ (insert-buffer-substring oldbuf start end)
+ (or (bolp) (insert ?\n))
+ (if filter (funcall filter))
+ (if (string= "spell" spell-command)
+ (call-process-region (point-min) (point-max) "spell" t buf)
+ (call-process-region (point-min) (point-max) shell-file-name
+ t buf nil "-c" spell-command)))))
+ (message "Checking spelling of %s...%s"
+ (or description "region")
+ (if (save-excursion
+ (set-buffer buf)
+ (> (buffer-size) 0))
+ "not correct"
+ "correct"))
+ (let (word newword
+ (case-fold-search t)
+ (case-replace t))
+ (while (save-excursion
+ (set-buffer buf)
+ (> (buffer-size) 0))
+ (save-excursion
+ (set-buffer buf)
+ (goto-char (point-min))
+ (setq word (downcase
+ (buffer-substring (point)
+ (progn (end-of-line) (point)))))
+ (forward-char 1)
+ (delete-region (point-min) (point))
+ (setq newword
+ (read-input (concat "`" word
+ "' not recognized; edit a replacement: ")
+ word))
+ (flush-lines (concat "^" (regexp-quote word) "$")))
+ (if (not (equal word newword))
+ (progn
+ (goto-char (point-min))
+ (query-replace-regexp (concat "\\b" (regexp-quote word) "\\b")
+ newword)))))))
+
+
+(defun spell-string (string)
+ "Check spelling of string supplied as argument."
+ (interactive "sSpell string: ")
+ (let ((buf (get-buffer-create " *temp*")))
+ (save-excursion
+ (set-buffer buf)
+ (widen)
+ (erase-buffer)
+ (insert string "\n")
+ (if (string= "spell" spell-command)
+ (call-process-region (point-min) (point-max) "spell"
+ t t)
+ (call-process-region (point-min) (point-max) shell-file-name
+ t t nil "-c" spell-command))
+ (if (= 0 (buffer-size))
+ (message "%s is correct" string)
+ (goto-char (point-min))
+ (while (search-forward "\n" nil t)
+ (replace-match " "))
+ (message "%sincorrect" (buffer-substring 1 (point-max)))))))
diff --git a/lisp/textmodes/text-mode.el b/lisp/textmodes/text-mode.el
new file mode 100644
index 00000000000..ba54cb845f6
--- /dev/null
+++ b/lisp/textmodes/text-mode.el
@@ -0,0 +1,147 @@
+;; Text mode, and its ideosyncratic commands.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defvar text-mode-syntax-table nil
+ "Syntax table used while in text mode.")
+
+(defvar text-mode-abbrev-table nil
+ "Abbrev table used while in text mode.")
+(define-abbrev-table 'text-mode-abbrev-table ())
+
+(if text-mode-syntax-table
+ ()
+ (setq text-mode-syntax-table (make-syntax-table))
+ (modify-syntax-entry ?\" ". " text-mode-syntax-table)
+ (modify-syntax-entry ?\\ ". " text-mode-syntax-table)
+ (modify-syntax-entry ?' "w " text-mode-syntax-table))
+
+(defvar text-mode-map nil
+ "Keymap for Text mode.
+Many other modes, such as Mail mode, Outline mode and Indented Text mode,
+inherit all the commands defined in this map.")
+
+(if text-mode-map
+ ()
+ (setq text-mode-map (make-sparse-keymap))
+ (define-key text-mode-map "\t" 'tab-to-tab-stop)
+ (define-key text-mode-map "\es" 'center-line)
+ (define-key text-mode-map "\eS" 'center-paragraph))
+
+
+;(defun non-saved-text-mode ()
+; "Like text-mode, but delete auto save file when file is saved for real."
+; (text-mode)
+; (make-local-variable 'delete-auto-save-files)
+; (setq delete-auto-save-files t))
+
+(defun text-mode ()
+ "Major mode for editing text intended for humans to read. Special commands:\\{text-mode-map}
+Turning on text-mode calls the value of the variable `text-mode-hook',
+if that value is non-nil."
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map text-mode-map)
+ (setq mode-name "Text")
+ (setq major-mode 'text-mode)
+ (setq local-abbrev-table text-mode-abbrev-table)
+ (set-syntax-table text-mode-syntax-table)
+ (run-hooks 'text-mode-hook))
+
+(defvar indented-text-mode-map ()
+ "Keymap for Indented Text mode.
+All the commands defined in Text mode are inherited unless overridden.")
+
+(if indented-text-mode-map
+ ()
+ (setq indented-text-mode-map (nconc (make-sparse-keymap) text-mode-map))
+ (define-key indented-text-mode-map "\t" 'indent-relative))
+
+(defun indented-text-mode ()
+ "Major mode for editing indented text intended for humans to read.\\{indented-text-mode-map}
+Turning on indented-text-mode calls the value of the variable `text-mode-hook',
+if that value is non-nil."
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map text-mode-map)
+ (define-abbrev-table 'text-mode-abbrev-table ())
+ (setq local-abbrev-table text-mode-abbrev-table)
+ (set-syntax-table text-mode-syntax-table)
+ (make-local-variable 'indent-line-function)
+ (setq indent-line-function 'indent-relative-maybe)
+ (use-local-map indented-text-mode-map)
+ (setq mode-name "Indented Text")
+ (setq major-mode 'indented-text-mode)
+ (run-hooks 'text-mode-hook))
+
+(defun change-log-mode ()
+ "Major mode for editing ChangeLog files. See M-x add-change-log-entry.
+Almost the same as Indented Text mode, but prevents numeric backups
+and sets `left-margin' to 8 and `fill-column' to 74."
+ (interactive)
+ (indented-text-mode)
+ (setq left-margin 8)
+ (setq fill-column 74)
+ (make-local-variable 'version-control)
+ (setq version-control 'never)
+ (run-hooks 'change-log-mode-hook))
+
+(defun center-paragraph ()
+ "Center each nonblank line in the paragraph at or after point.
+See center-line for more info."
+ (interactive)
+ (save-excursion
+ (forward-paragraph)
+ (or (bolp) (newline 1))
+ (let ((end (point)))
+ (backward-paragraph)
+ (center-region (point) end))))
+
+(defun center-region (from to)
+ "Center each nonblank line starting in the region.
+See center-line for more info."
+ (interactive "r")
+ (if (> from to)
+ (let ((tem to))
+ (setq to from from tem)))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region from to)
+ (goto-char from)
+ (while (not (eobp))
+ (or (save-excursion (skip-chars-forward " \t") (eolp))
+ (center-line))
+ (forward-line 1)))))
+
+(defun center-line ()
+ "Center the line point is on, within the width specified by `fill-column'.
+This means adjusting the indentation so that it equals
+the distance between the end of the text and `fill-column'."
+ (interactive)
+ (save-excursion
+ (let (line-length)
+ (beginning-of-line)
+ (delete-horizontal-space)
+ (end-of-line)
+ (delete-horizontal-space)
+ (setq line-length (current-column))
+ (beginning-of-line)
+ (indent-to
+ (+ left-margin
+ (/ (- fill-column left-margin line-length) 2))))))
diff --git a/lisp/textmodes/underline.el b/lisp/textmodes/underline.el
new file mode 100644
index 00000000000..4a9f3dfa823
--- /dev/null
+++ b/lisp/textmodes/underline.el
@@ -0,0 +1,46 @@
+;; Insert or remove underlining (done by overstriking) in Emacs.
+;; Copyright (C) 1985 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defun underline-region (start end)
+ "Underline all nonblank characters in the region.
+Works by overstriking underscores.
+Called from program, takes two arguments START and END
+which specify the range to operate on."
+ (interactive "r")
+ (save-excursion
+ (let ((end1 (make-marker)))
+ (move-marker end1 (max start end))
+ (goto-char (min start end))
+ (while (< (point) end1)
+ (or (looking-at "[_\^@- ]")
+ (insert "_"))
+ (forward-char 1)))))
+
+(defun ununderline-region (start end)
+ "Remove all underlining (overstruck underscores) in the region.
+Called from program, takes two arguments START and END
+which specify the range to operate on."
+ (interactive "r")
+ (save-excursion
+ (let ((end1 (make-marker)))
+ (move-marker end1 (max start end))
+ (goto-char (min start end))
+ (while (re-search-forward "_\\|_" end1 t)
+ (delete-char -2)))))
diff --git a/lisp/userlock.el b/lisp/userlock.el
new file mode 100644
index 00000000000..e74621675a2
--- /dev/null
+++ b/lisp/userlock.el
@@ -0,0 +1,124 @@
+;; Copyright (C) 1985, 1986 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;; This file is autloaded to handle certain conditions
+;; detected by the file-locking code within Emacs.
+;; The two entry points are `ask-user-about-lock' and
+;; `ask-user-about-supersession-threat'.
+
+
+(put 'file-locked 'error-conditions '(file-locked file-error error))
+
+(defun ask-user-about-lock (fn opponent)
+ "Ask user what to do when he wants to edit FILE but it is locked by USER.
+This function has a choice of three things to do:
+ do (signal 'buffer-file-locked (list FILE USER))
+ to refrain from editing the file
+ return t (grab the lock on the file)
+ return nil (edit the file even though it is locked).
+You can rewrite it to use any criterion you like to choose which one to do."
+ (discard-input)
+ (save-window-excursion
+ (let (answer)
+ (while (null answer)
+ (message "%s is locking %s: action (s, q, p, ?)? " opponent fn)
+ (let ((tem (let ((inhibit-quit t)
+ (cursor-in-echo-area t))
+ (prog1 (downcase (read-char))
+ (setq quit-flag nil)))))
+ (if (= tem help-char)
+ (ask-user-about-lock-help)
+ (setq answer (assoc tem '((?s . t)
+ (?q . yield)
+ (?\C-g . yield)
+ (?p . nil)
+ (?? . help))))
+ (cond ((null answer)
+ (beep)
+ (message "Please type q, s, or p; or ? for help")
+ (sit-for 3))
+ ((eq (cdr answer) 'help)
+ (ask-user-about-lock-help)
+ (setq answer nil))
+ ((eq (cdr answer) 'yield)
+ (signal 'file-locked (list "File is locked" fn opponent)))))))
+ (cdr answer))))
+
+(defun ask-user-about-lock-help ()
+ (with-output-to-temp-buffer "*Help*"
+ (princ "It has been detected that you want to modify a file that someone else has
+already started modifying in EMACS.
+
+You can <s>teal the file; The other user becomes the
+ intruder if (s)he ever unmodifies the file and then changes it again.
+You can <p>roceed; you edit at your own (and the other user's) risk.
+You can <q>uit; don't modify this file.")))
+
+(put
+ 'file-supersession 'error-conditions '(file-supersession file-error error))
+
+(defun ask-user-about-supersession-threat (fn)
+ "Ask a user who is about to modify an obsolete buffer what to do.
+This function has two choices: it can return, in which case the modification
+of the buffer will proceed, or it can (signal 'file-supersession (file)),
+in which case the proposed buffer modification will not be made.
+
+You can rewrite this to use any criterion you like to choose which one to do.
+The buffer in question is current when this function is called."
+ (discard-input)
+ (save-window-excursion
+ (let (answer)
+ (while (null answer)
+ (message "File has changed on disk; really want to edit the buffer? (y, n or C-h) ")
+ (let ((tem (downcase (let ((cursor-in-echo-area t))
+ (read-char)))))
+ (setq answer
+ (if (= tem help-char)
+ 'help
+ (cdr (assoc tem '((?n . yield)
+ (?\C-g . yield)
+ (?y . proceed)
+ (?? . help))))))
+ (cond ((null answer)
+ (beep)
+ (message "Please type y or n; or ? for help")
+ (sit-for 3))
+ ((eq answer 'help)
+ (ask-user-about-supersession-help)
+ (setq answer nil))
+ ((eq answer 'yield)
+ (signal 'file-supersession
+ (list "File changed on disk" fn))))))
+ (message
+ "File on disk now will become a backup file if you save these changes.")
+ (setq buffer-backed-up nil))))
+
+(defun ask-user-about-supersession-help ()
+ (with-output-to-temp-buffer "*Help*"
+ (princ "You want to modify a buffer whose disk file has changed
+since you last read it in or saved it with this buffer.
+
+If you say `y' to go ahead and modify this buffer,
+you risk ruining the work of whoever rewrote the file.
+If you say `n', the change you started to make will be aborted.
+
+Usually, you should type `n' and then `M-x revert-buffer',
+to get the latest version of the file, then make the change again.")))
+
+
diff --git a/lisp/vms-patch.el b/lisp/vms-patch.el
new file mode 100644
index 00000000000..1e173e897e6
--- /dev/null
+++ b/lisp/vms-patch.el
@@ -0,0 +1,99 @@
+;; Override parts of files.el for VMS.
+;; Copyright (C) 1986 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;;; Functions that need redefinition
+
+;;; VMS file names are upper case, but buffer names are more
+;;; convenient in lower case.
+
+(defun create-file-buffer (filename)
+ "Create a suitably named buffer for visiting FILENAME, and return it.
+FILENAME (sans directory) is used unchanged if that name is free;
+otherwise a string <2> or <3> or ... is appended to get an unused name."
+ (generate-new-buffer (downcase (file-name-nondirectory filename))))
+
+;;; Given a string FN, return a similar name which is a legal VMS filename.
+;;; This is used to avoid invalid auto save file names.
+(defun make-legal-file-name (fn)
+ (setq fn (copy-sequence fn))
+ (let ((dot nil) (indx 0) (len (length fn)) chr)
+ (while (< indx len)
+ (setq chr (aref fn indx))
+ (cond
+ ((eq chr ?.) (if dot (aset fn indx ?_) (setq dot t)))
+ ((not (or (and (>= chr ?a) (<= chr ?z)) (and (>= chr ?A) (<= chr ?Z))
+ (and (>= chr ?0) (<= chr ?9))
+ (eq chr ?$) (eq chr ?_) (and (eq chr ?-) (> indx 0))))
+ (aset fn indx ?_)))
+ (setq indx (1+ indx))))
+ fn)
+
+;;; Auto save filesnames start with _$ and end with $.
+
+(defun make-auto-save-file-name ()
+ "Return file name to use for auto-saves of current buffer.
+Does not consider auto-save-visited-file-name; that is checked
+before calling this function.
+This is a separate function so your .emacs file or site-init.el can redefine it.
+See also auto-save-file-name-p."
+ (if buffer-file-name
+ (concat (file-name-directory buffer-file-name)
+ "_$"
+ (file-name-nondirectory buffer-file-name)
+ "$")
+ (expand-file-name (concat "_$_" (make-legal-file-name (buffer-name)) "$"))))
+
+(defun auto-save-file-name-p (filename)
+ "Return t if FILENAME can be yielded by make-auto-save-file-name.
+FILENAME should lack slashes.
+This is a separate function so your .emacs file or site-init.el can redefine it."
+ (string-match "^_\\$.*\\$" filename))
+
+(defun vms-suspend-resume-hook ()
+ "When resuming suspended Emacs, check for file to be found.
+If the logical name `EMACS_FILE_NAME' is defined, `find-file' that file."
+ (let ((file (vms-system-info "LOGICAL" "EMACS_FILE_NAME")))
+ (if file (find-file file))))
+
+(setq suspend-resume-hook 'vms-suspend-resume-hook)
+
+(defun vms-suspend-hook ()
+ "Don't allow suspending if logical name `DONT_SUSPEND_EMACS' is defined."
+ (if (vms-system-info "LOGICAL" "DONT_SUSPEND_EMACS")
+ (error "Can't suspend this emacs"))
+ nil)
+
+(setq suspend-hook 'vms-suspend-hook)
+
+(defun vms-read-directory (dirname switches buffer)
+ (save-excursion
+ (set-buffer buffer)
+ (subprocess-command-to-buffer
+ (concat "DIRECTORY " switches " " dirname)
+ buffer)
+ (goto-char (point-min))
+ ;; Remove all the trailing blanks.
+ (while (search-forward " \n")
+ (forward-char -1)
+ (delete-horizontal-space))
+ (goto-char (point-min))))
+
+(setq dired-listing-switches
+ "/SIZE/DATE/OWNER/WIDTH=(FILENAME=32,SIZE=5)")
diff --git a/lisp/window.el b/lisp/window.el
new file mode 100644
index 00000000000..ce1c0e566c9
--- /dev/null
+++ b/lisp/window.el
@@ -0,0 +1,98 @@
+;; GNU Emacs window commands aside from those written in C.
+;; Copyright (C) 1985, 1989 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+(defun count-windows (&optional minibuf)
+ "Returns the number of visible windows.
+Optional arg NO-MINI non-nil means don't count the minibuffer
+even if it is active."
+ (let ((count 0))
+ (walk-windows (function (lambda ()
+ (setq count (+ count 1))))
+ minibuf)
+ count))
+
+(defun balance-windows ()
+ "Makes all visible windows the same size (approximately)."
+ (interactive)
+ (let ((count 0))
+ (walk-windows (function (lambda (w)
+ (setq count (+ count 1))))
+ 'nomini)
+ (let ((size (/ (screen-height) count)))
+ (walk-windows (function (lambda (w)
+ (select-window w)
+ (enlarge-window (- size (window-height)))))
+ 'nomini))))
+
+(defun split-window-vertically (&optional arg)
+ "Split current window into two windows, one above the other.
+This window becomes the uppermost of the two, and gets
+ARG lines. No arg means split equally."
+ (interactive "P")
+ (let ((old-w (selected-window))
+ new-w bottom)
+ (setq new-w (split-window nil (and arg (prefix-numeric-value arg))))
+ (save-excursion
+ (set-buffer (window-buffer))
+ (goto-char (window-start))
+ (vertical-motion (window-height))
+ (set-window-start new-w (point))
+ (if (> (point) (window-point new-w))
+ (set-window-point new-w (point)))
+ (vertical-motion -1)
+ (setq bottom (point)))
+ (if (<= bottom (point))
+ (set-window-point old-w (1- bottom)))))
+
+(defun split-window-horizontally (&optional arg)
+ "Split current window into two windows side by side.
+This window becomes the leftmost of the two, and gets
+ARG columns. No arg means split equally."
+ (interactive "P")
+ (split-window nil (and arg (prefix-numeric-value arg)) t))
+
+(defun enlarge-window-horizontally (arg)
+ "Make current window ARG columns wider."
+ (interactive "p")
+ (enlarge-window arg t))
+
+(defun shrink-window-horizontally (arg)
+ "Make current window ARG columns narrower."
+ (interactive "p")
+ (shrink-window arg t))
+
+(defun window-config-to-register (name)
+ "Save the current window configuration in register REG (a letter).
+It can be later retrieved using \\[M-x register-to-window-config]."
+ (interactive "cSave window configuration in register: ")
+ (set-register name (current-window-configuration)))
+
+(defun register-to-window-config (name)
+ "Restore (make current) the window configuration in register REG (a letter).
+Use with a register previously set with \\[window-config-to-register]."
+ (interactive "cRestore window configuration from register: ")
+ (set-window-configuration (get-register name)))
+
+(define-key ctl-x-map "2" 'split-window-vertically)
+(define-key ctl-x-map "5" 'split-window-horizontally)
+(define-key ctl-x-map "6" 'window-config-to-register)
+(define-key ctl-x-map "7" 'register-to-window-config)
+(define-key ctl-x-map "}" 'enlarge-window-horizontally)
+(define-key ctl-x-map "{" 'shrink-window-horizontally)