Mercurial > emacs
changeset 1745:341d551a3411
Initial revision
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Fri, 08 Jan 1993 15:30:08 +0000 |
parents | 5180778aedcb |
children | 7c4fc10fde41 |
files | lisp/=sc.el |
diffstat | 1 files changed, 1563 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/=sc.el Fri Jan 08 15:30:08 1993 +0000 @@ -0,0 +1,1563 @@ +;; -*- Mode: Emacs-Lisp -*- +;; sc.el -- Version 2.3 (used to be supercite.el) + +;; ========== Introduction ========== +;; Citation and attribution package for various GNU emacs news and +;; electronic mail reading subsystems. This version of supercite will +;; interface to VM 4.40+ and MH-E 3.7 (shipped w/ emacs 18.57) as is. +;; It will also interface with GNUS 3.12+, RMAIL 18.55+, GNEWS, and +;; probably most other news/mail subsystems by using the overloading +;; functions provided in sc-oloads.el (see that file or the README for +;; more information on interfacing supercite with your reader subsystem). +;; This version should now be compatible with Lucid Emacs 19.x emacses. + +;; This package does not do any yanking of messages, but instead +;; massages raw reply buffers set up by the reply/forward functions in +;; the news/mail subsystems. Therefore, such useful operations as +;; yanking and citing portions of the original article (instead of the +;; whole article) are not within the ability or responsiblity of +;; supercite. + +;; ========== Disclaimer ========== +;; This software is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY. No author or distributor, nor any +;; author's past, present, or future employers accepts responsibility +;; to anyone for the consequences of using it or for whether it serves +;; any particular purpose or works at all, unless he says so in +;; writing. + +;; Some of this software was written as part of the supercite author's +;; official duty as an employee of the United States Government and is +;; thus not subject to copyright. You are free to use that particular +;; software as you wish, but WITHOUT ANY WARRANTY WHATSOEVER. It +;; would be nice, though if when you use any of this or other freely +;; available code, you give due credit to the author. + +;; Other parts of this code were written by other people. Wherever +;; possible, credit to that author, and the copy* notice supplied by +;; the author are included with that code. The supercite author is no +;; longer an employee of the U.S. Government so the GNU Public Licence +;; should be considered in effect for all enhancements and bug fixes +;; performed by the author. + +;; ========== Author (unless otherwise stated) ======================== +;; NAME: Barry A. Warsaw USMAIL: Century Computing, Inc. +;; TELE: (301) 593-3330 1014 West Street +;; INET: bwarsaw@cen.com Laurel, Md 20707 +;; UUCP: uunet!cen.com!bwarsaw +;; +;; Want to be on the Supercite mailing list? +;; +;; Send articles to: +;; Internet: supercite@anthem.nlm.nih.gov +;; UUCP: uunet!anthem.nlm.nih.gov!supercite +;; +;; Send administrivia (additions/deletions to list, etc) to: +;; Internet: supercite-request@anthem.nlm.nih.gov +;; UUCP: uunet!anthem.nlm.nih.gov!supercite-request + +;; ========== Credits and Thanks ========== +;; This package was derived from the Superyank 1.11 package as posted +;; to the net. Superyank 1.11 was inspired by code and ideas from +;; Martin Neitzel and Ashwin Ram. Supercite version 2.3 has evolved +;; through the comments and suggestions of the supercite mailing list +;; which consists of many authors and users of the various mail and +;; news reading subsystems. + +;; Many folks on the supercite mailing list have contributed their +;; help in debugging, making suggestions and supplying support code or +;; bug fixes for the previous versions of supercite. I want to thank +;; everyone who helped, especially (in no particular order): +;; +;; Mark D. Baushke, Khalid Sattar, David Lawrence, Chris Davis, Kyle +;; Jones, Kayvan Sylvan, Masanobu Umeda, Dan Jacobson, Piet van +;; Oostrum, Hamish (H.I.) Macdonald, and Joe Wells. +;; +;; I don't mean to leave anyone out. All who have helped have been +;; appreciated. + +;; ========== Getting Started ========== +;; Here is a quick guide to getting started with supercite. The +;; information contained here is mostly excerpted from the more +;; detailed explanations given in the accompanying README file. +;; Naturally, there are many customizations you can do to give your +;; replies that personalized flair, but the instructions in this +;; section should be sufficient for getting started. + +;; With this release of supercite overloading is the only supported +;; way to get supercite hooked into your favorite news/mail reading +;; subsystems. Overloading will be necessary for RMAIL, GNUS, GNEWS, +;; RNEWS and PCMAIL. Overloading will not be needed for VM 4.37+ or +;; MH-E 3.7+. MH-E comes with emacs 18.57 but if you have an earlier +;; version of emacs, you should be able to ftp MH-E 3.7 separately. Or +;; you can extract the MH-E overloading stuff from version 2.1's +;; sc-oloads.el. + +;; First, to connect supercite to any mail/news reading subsystem, put +;; this in your .emacs file: +;; +;; (setq mail-yank-hooks 'sc-cite-original) ; for all but MH-E +;; (setq mh-yank-hooks 'sc-cite-original) ; for MH-E only +;; +;; If supercite is not pre-loaded into your emacs session, you should +;; add the following autoload: +;; +;; (autoload 'sc-cite-original "sc" "Supercite 2.3" t) +;; +;; Then, if you need to overload, put the following in your .emacs file: +;; +;; (defun my-sc-overload-hook () +;; (require 'sc-oloads) ; be sure this file is on your load-path +;; (sc-overload-functions)) +;; +;; (setq news-reply-mode-hook 'my-sc-overload-hook) ; for RNEWS,GNUS,GNEWS +;; (setq mail-setup-hook 'my-sc-overload-hook) ; for RMAIL, PCMAIL +;; +;; Finally, if you want to customize supercite, you should do it in a +;; function called my-supercite-hook and: +;; +;; (setq sc-load-hook 'my-supercite-hook) + +(require 'sc-alist) + + +;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +;; start of user defined variables +;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + +(defvar sc-nested-citation-p nil + "*Controls whether to use nested or non-nested citation style. +Non-nil uses nested citations, nil uses non-nested citations. Type +\\[sc-describe] for more information.") + +(defvar sc-citation-leader " " + "*String comprising first part of a citation.") + +(defvar sc-citation-delimiter ">" + "*String comprising third part of a citation. +This string is used in both nested and non-nested citations.") + +(defvar sc-citation-separator " " + "*String comprising fourth and last part of a citation.") + +(defvar sc-default-author-name "Anonymous" + "*String used when author's name cannot be determined.") + +(defvar sc-default-attribution "Anon" + "*String used when author's attribution cannot be determined.") + +;; Noriya KOBAYASHI (nk@ics.osaka-u.ac.jp) writes to the supercite +;; mailing list: +;; I use supercite in Nemacs-3.3.2. In order to handle citation using +;; Kanji, [...set sc-cite-regexp to...] +;; "\\s *\\([a-zA-Z0-9]\\|\\cc\\|\\cC\\|\\ch\\|\\cH\\|\\ck\\|\\cK\\)*\\s *>+" +;; +(defvar sc-cite-regexp "\\s *[-a-zA-Z0-9_.]*>+\\s *" + "*Regular expression describing how a already cited line begins. +The regexp is only used at the beginning of a line, so it doesn't need +to start with a '^'.") + +(defvar sc-titlecue-regexp "\\s +-+\\s +" + "*Regular expression describing the separator between names and titles. +Set to nil to treat entire field as a name.") + +(defvar sc-spacify-name-chars '(?_ ?* ?+ ?=) + "*List of characters to convert to spaces if found in an author's name.") + +(defvar sc-nicknames-alist + '(("Michael" "Mike") + ("Daniel" "Dan") + ("David" "Dave") + ("Jonathan" "John") + ("William" "Bill") + ("Elizabeth" "Beth") + ("Elizabeth" "Betsy") + ("Kathleen" "Kathy") + ("Smith" "Smitty")) + "*Association list of names and their common nicknames. +Entries are of the form (NAME NICKNAME), and NAMEs can have more than +one nickname. Nicknames will not be automatically used as an +attribution string, since I'm not sure this is really polite, but if a +name is glommed from the author name and presented in the attribution +string completion list, the matching nicknames will also be presented. +Set this variable to nil to defeat nickname expansions. Also note that +nicknames are not put in the supercite information alist.") + +(defvar sc-confirm-always-p t + "*If non-nil, always confirm attribution string before citing text body.") + +(defvar sc-preferred-attribution 'firstname + "*Specifies which part of the author's name becomes the attribution. +The value of this variable must be one of the following quoted symbols: + + emailname -- email terminus name + initials -- initials of author + firstname -- first name of author + lastname -- last name of author + middlename1 -- first middle name of author + middlename2 -- second middle name of author + ... + +Middle name indexes can be any positive integer greater than 0, though +it is unlikely that many authors will supply more than one middle +name, if that many.") + +(defvar sc-use-only-preference-p nil + "*Controls what happens when the preferred attribution cannot be found. +If non-nil, then sc-default-attribution will be used. If nil, then +some secondary scheme will be employed to find a suitable attribution +string.") + +(defvar sc-downcase-p nil + "*Non-nil means downcase the attribution and citation strings.") + +(defvar sc-rewrite-header-list + '((sc-no-header) + (sc-header-on-said) + (sc-header-inarticle-writes) + (sc-header-regarding-adds) + (sc-header-attributed-writes) + (sc-header-verbose) + (sc-no-blank-line-or-header) + ) + "*List of reference header rewrite functions. +The variable sc-preferred-header-style controls which function in this +list is chosen for automatic reference header insertions. Electric +reference mode will cycle through this list of functions. For more +information, type \\[sc-describe].") + +(defvar sc-preferred-header-style 1 + "*Index into sc-rewrite-header-list specifying preferred header style. +Index zero accesses the first function in the list.") + +(defvar sc-electric-references-p t + "*Use electric references if non-nil.") + +(defvar sc-electric-circular-p t + "*Treat electric references as circular if non-nil.") + +(defvar sc-mail-fields-list + '("date" "message-id" "subject" "newsgroups" "references" + "from" "return-path" "path" "reply-to" "organization" + "reply" ) + "*List of mail header whose values will be saved by supercite. +These values can be used in header rewrite functions by accessing them +with the sc-field function. Mail headers in this list are case +insensitive and do not require a trailing colon.") + +(defvar sc-mumble-string "" + "*Value returned by sc-field if chosen field cannot be found.") + +(defvar sc-nuke-mail-headers-p t + "*Nuke or don't nuke mail headers. +If non-nil, nuke mail headers after gleaning useful information from +them.") + +(defvar sc-reference-tag-string ">>>>> " + "*String used at the beginning of built-in reference headers.") + +(defvar sc-fill-paragraph-hook 'sc-fill-paragraph + "*Hook for filling a paragraph. +This hook gets executed when you fill a paragraph either manually or +automagically. It expects point to be within the extent of the +paragraph that is going to be filled. This hook allows you to use a +different paragraph filling package than the one supplied with +supercite.") + +(defvar sc-auto-fill-region-p nil + "*If non-nil, automatically fill each paragraph after it has been cited.") + +(defvar sc-auto-fill-query-each-paragraph-p nil + "*If non-nil, query before filling each paragraph. +No querying and no filling will be performed if sc-auto-fill-region-p +is set to nil.") + +(defvar sc-fixup-whitespace-p nil + "*If non-nil, delete all leading white space before citing.") + +(defvar sc-all-but-cite-p nil + "*If non-nil, sc-cite-original does everything but cite the text. +This is useful for manually citing large messages, or portions of +large messages. When non-nil, sc-cite-original will still set up all +necessary variables and databases, but will skip the citing routine +which modify the reply buffer's text.") + +(defvar sc-load-hook nil + "*User definable hook. +Runs after supercite is loaded. Set your customizations here.") + +(defvar sc-pre-hook nil + "*User definable hook. +Runs before sc-cite-original executes.") + +(defvar sc-post-hook nil + "*User definable hook. +Runs after sc-cite-original executes.") + +(defvar sc-header-nuke-list + '("via" "origin" "status" "received" "remailed" "cc" "sender" "replied" + "organization" "keywords" "distribution" "xref" "references" "expires" + "approved" "summary" "precedence" "subject" "newsgroup[s]?" + "\\(followup\\|apparently\\|errors\\|\\(\\(in-\\)?reply\\)?-\\)?to" + "x-[a-z0-9-]+" "[a-z-]*message-id" "\\(summary-\\)?line[s]" + "\\(\\(return\\|reply\\)-\\)?path" "\\(posted-\\)?date" + "\\(mail-\\)?from") + "*List of mail headers to remove from body of reply.") + + + +;; ====================================================================== +;; keymaps + +(defvar sc-default-keymap + '(lambda () + (local-set-key "\C-c\C-r" 'sc-insert-reference) + (local-set-key "\C-c\C-t" 'sc-cite) + (local-set-key "\C-c\C-a" 'sc-recite) + (local-set-key "\C-c\C-u" 'sc-uncite) + (local-set-key "\C-c\C-i" 'sc-insert-citation) + (local-set-key "\C-c\C-o" 'sc-open-line) + (local-set-key "\C-c\C-q" 'sc-fill-paragraph-manually) + (local-set-key "\C-cq" 'sc-fill-paragraph-manually) + (local-set-key "\C-c\C-m" 'sc-modify-information) + (local-set-key "\C-cf" 'sc-view-field) + (local-set-key "\C-cg" 'sc-glom-headers) + (local-set-key "\C-c\C-v" 'sc-version) + (local-set-key "\C-c?" 'sc-describe) + ) + "*Default keymap if major-mode can't be found in `sc-local-keymaps'.") + +(defvar sc-local-keymaps + '((mail-mode + (lambda () + (local-set-key "\C-c\C-r" 'sc-insert-reference) + (local-set-key "\C-c\C-t" 'sc-cite) + (local-set-key "\C-c\C-a" 'sc-recite) + (local-set-key "\C-c\C-u" 'sc-uncite) + (local-set-key "\C-c\C-i" 'sc-insert-citation) + (local-set-key "\C-c\C-o" 'sc-open-line) + (local-set-key "\C-c\C-q" 'sc-fill-paragraph-manually) + (local-set-key "\C-cq" 'sc-fill-paragraph-manually) + (local-set-key "\C-c\C-m" 'sc-modify-information) + (local-set-key "\C-cf" 'sc-view-field) + (local-set-key "\C-cg" 'sc-glom-headers) + (local-set-key "\C-c\C-v" 'sc-version) + (local-set-key "\C-c?" 'sc-describe) + )) + (mh-letter-mode + (lambda () + (local-set-key "\C-c\C-r" 'sc-insert-reference) + (local-set-key "\C-c\C-t" 'sc-cite) + (local-set-key "\C-c\C-a" 'sc-recite) + (local-set-key "\C-c\C-u" 'sc-uncite) + (local-set-key "\C-ci" 'sc-insert-citation) + (local-set-key "\C-c\C-o" 'sc-open-line) + (local-set-key "\C-cq" 'sc-fill-paragraph-manually) + (local-set-key "\C-c\C-m" 'sc-modify-information) + (local-set-key "\C-cf" 'sc-view-field) + (local-set-key "\C-cg" 'sc-glom-headers) + (local-set-key "\C-c\C-v" 'sc-version) + (local-set-key "\C-c?" 'sc-describe) + )) + (news-reply-mode mail-mode) + (vm-mail-mode mail-mode) + (e-reply-mode mail-mode) + (n-reply-mode mail-mode) + ) + "*List of keymaps to use with the associated major-mode.") + +(defvar sc-electric-mode-map nil + "*Keymap for sc-electric-mode.") + +(if sc-electric-mode-map + nil + (setq sc-electric-mode-map (make-sparse-keymap)) + (define-key sc-electric-mode-map "p" 'sc-eref-prev) + (define-key sc-electric-mode-map "n" 'sc-eref-next) + (define-key sc-electric-mode-map "s" 'sc-eref-setn) + (define-key sc-electric-mode-map "j" 'sc-eref-jump) + (define-key sc-electric-mode-map "x" 'sc-eref-abort) + (define-key sc-electric-mode-map "\r" 'sc-eref-exit) + (define-key sc-electric-mode-map "\n" 'sc-eref-exit) + (define-key sc-electric-mode-map "q" 'sc-eref-exit) + (define-key sc-electric-mode-map "g" 'sc-eref-goto) + ) + +;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +;; end of user defined variables +;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +;; ====================================================================== +;; global variables, not user accessable + +(defconst sc-version-number "2.3" + "Supercite's version number.") + +;; when rnewspost.el patch is installed (or function is overloaded) +;; this should be nil since supercite now does this itself. +(setq news-reply-header-hook nil) + +;; autoload for sc-electric-mode +(autoload 'sc-electric-mode "sc-elec" + "Quasi-major mode for viewing supercite reference headers." nil) + +;; global alists (gals), misc variables. make new bytecompiler happy +(defvar sc-gal-information nil + "Internal global alist variable containing information.") +(defvar sc-gal-attributions nil + "Internal global alist variable containing attributions.") +(defvar sc-fill-arg nil + "Internal fill argument holder.") +(defvar sc-cite-context nil + "Internal citation context holder.") +(defvar sc-force-confirmation-p nil + "Internal variable.") + +(make-variable-buffer-local 'sc-gal-attributions) +(make-variable-buffer-local 'sc-gal-information) +(make-variable-buffer-local 'sc-leached-keymap) +(make-variable-buffer-local 'sc-fill-arg) +(make-variable-buffer-local 'sc-cite-context) + +(setq-default sc-gal-attributions nil) +(setq-default sc-gal-information nil) +(setq-default sc-leached-keymap (current-local-map)) +(setq-default sc-fill-arg nil) +(setq-default sc-cite-context nil) + + + +;; ====================================================================== +;; miscellaneous support functions + +(defun sc-mark () + "Mark compatibility between emacs v18 and v19." + (let ((zmacs-regions nil)) + (mark))) + +(defun sc-update-gal (attribution) + "Update the information alist. +Add ATTRIBUTION and compose the nested and non-nested citation +strings." + (let ((attrib (if sc-downcase-p (downcase attribution) attribution))) + (aput 'sc-gal-information "sc-attribution" attrib) + (aput 'sc-gal-information "sc-nested-citation" + (concat attrib sc-citation-delimiter)) + (aput 'sc-gal-information "sc-citation" + (concat sc-citation-leader + attrib + sc-citation-delimiter + sc-citation-separator)))) + +(defun sc-valid-index-p (index) + "Returns t if INDEX is a valid index into sc-rewrite-header-list." + (let ((last (1- (length sc-rewrite-header-list)))) + (and (natnump index) ;; a number, and greater than or equal to zero + (<= index last) ;; less than or equal to the last index + ))) + +(defun sc-string-car (namestring) + "Return the string-equivalent \"car\" of NAMESTRING. + + example: (sc-string-car \"John Xavier Doe\") + => \"John\"" + (substring namestring + (progn (string-match "\\s *" namestring) (match-end 0)) + (progn (string-match "\\s *\\S +" namestring) (match-end 0)))) + +(defun sc-string-cdr (namestring) + "Return the string-equivalent \"cdr\" of NAMESTRING. + + example: (sc-string-cdr \"John Xavier Doe\") + => \"Xavier Doe\"" + (substring namestring + (progn (string-match "\\s *\\S +\\s *" namestring) + (match-end 0)))) + +(defun sc-linepos (&optional position col-p) + "Return the character position at various line positions. +Optional POSITION can be one of the following symbols: + bol == beginning of line + boi == beginning of indentation + eol == end of line [default] + +Optional COL-P non-nil returns current-column instead of character position." + (let ((tpnt (point)) + rval) + (cond + ((eq position 'bol) (beginning-of-line)) + ((eq position 'boi) (back-to-indentation)) + (t (end-of-line))) + (setq rval (if col-p (current-column) (point))) + (goto-char tpnt) + rval)) + + +;; ====================================================================== +;; this section snarfs mail fields and places them in the info alist + +(defun sc-build-header-zap-regexp () + "Return a regexp for sc-mail-yank-clear-headers." + (let ((headers sc-header-nuke-list) + (regexp nil)) + (while headers + (setq regexp (concat regexp + "^" (car headers) ":" + (if (cdr headers) "\\|" nil))) + (setq headers (cdr headers))) + regexp)) + +(defun sc-mail-yank-clear-headers (start end) + "Nuke mail headers between START and END." + (if (and sc-nuke-mail-headers-p sc-header-nuke-list) + (let ((regexp (sc-build-header-zap-regexp))) + (save-excursion + (goto-char start) + (if (search-forward "\n\n" end t) + (save-restriction + (narrow-to-region start (point)) + (goto-char start) + (while (let ((case-fold-search t)) + (re-search-forward regexp nil t)) + (beginning-of-line) + (delete-region (point) + (progn (re-search-forward "\n[^ \t]") + (forward-char -1) + (point))) + ))) + )))) + +(defun sc-mail-fetch-field (field) + "Return the value of the header field FIELD. +The buffer is expected to be narrowed to just the headers of the +message." + (save-excursion + (goto-char (point-min)) + (let ((case-fold-search t) + (name (concat "^" (regexp-quote field) "[ \t]*:[ \t]*"))) + (goto-char (point-min)) + (if (re-search-forward name nil t) + (let ((opoint (point))) + (while (progn (forward-line 1) + (looking-at "[ \t]"))) + (buffer-substring opoint (1- (point)))))))) + +(defun sc-fetch-fields (start end) + "Fetch the mail fields in the region from START to END. +These fields can be accessed in header rewrite functions with sc-field." + (save-excursion + (save-restriction + (narrow-to-region start end) + (goto-char start) + (let ((fields sc-mail-fields-list)) + (while fields + (let ((value (sc-mail-fetch-field (car fields))) + (next (cdr fields))) + (and value + (aput 'sc-gal-information (car fields) value)) + (setq fields next))) + (if (sc-mail-fetch-field "from") + (aput 'sc-gal-information "from" (sc-mail-fetch-field "from"))))))) + +(defun sc-field (field) + "Return the alist information associated with the FIELD. +If FIELD is not a valid key, return sc-mumble-string." + (or (aget sc-gal-information field) sc-mumble-string)) + + +;; ====================================================================== +;; built-in reference header rewrite functions + +(defun sc-no-header () + "Does nothing. Use this instead of nil to get a blank header." + ()) + +(defun sc-no-blank-line-or-header() + "Similar to sc-no-header except it removes the preceeding blank line." + (if (not (bobp)) + (if (and (eolp) + (progn (forward-line -1) + (or (looking-at mail-header-separator) + (and (eq major-mode 'mh-letter-mode) + (mh-in-header-p))))) + (progn (forward-line) + (let ((kill-lines-magic t)) (kill-line)))))) + +(defun sc-header-on-said () + "\"On <date>, <from> said:\", unless 1. the \"from\" field cannot be +found, in which case nothing is inserted; or 2. the \"date\" field is +missing in which case only the from part is printed." + (let* ((sc-mumble-string "") + (whofrom (sc-field "from")) + (when (sc-field "date"))) + (if (not (string= whofrom "")) + (insert sc-reference-tag-string + (if (not (string= when "")) + (concat "On " when ", ") "") + whofrom " said:\n")))) + +(defun sc-header-inarticle-writes () + "\"In article <message-id>, <from> writes:\" +Treats \"message-id\" and \"from\" fields similar to sc-header-on-said." + (let* ((sc-mumble-string "") + (whofrom (sc-field "from")) + (msgid (sc-field "message-id"))) + (if (not (string= whofrom "")) + (insert sc-reference-tag-string + (if (not (string= msgid "")) + (concat "In article " msgid ", ") "") + whofrom " writes:\n")))) + +(defun sc-header-regarding-adds () + "\"Regarding <subject>; <from> adds:\" +Treats \"subject\" and \"from\" fields similar to sc-header-on-said." + (let* ((sc-mumble-string "") + (whofrom (sc-field "from")) + (subj (sc-field "subject"))) + (if (not (string= whofrom "")) + (insert sc-reference-tag-string + (if (not (string= subj "")) + (concat "Regarding " subj "; ") "") + whofrom " adds:\n")))) + +(defun sc-header-attributed-writes () + "\"<sc-attribution>\" == <sc-author> <address> writes: +Treats these fields in a similar manner to sc-header-on-said." + (let* ((sc-mumble-string "") + (whofrom (sc-field "from")) + (reply (sc-field "sc-reply-address")) + (from (sc-field "sc-from-address")) + (attr (sc-field "sc-attribution")) + (auth (sc-field "sc-author"))) + (if (not (string= whofrom "")) + (insert sc-reference-tag-string + (if (not (string= attr "")) + (concat "\"" attr "\" == " ) "") + (if (not (string= auth "")) + (concat auth " ") "") + (if (not (string= reply "")) + (concat "<" reply ">") + (if (not (string= from "")) + (concat "<" from ">") "")) + " writes:\n")))) + +(defun sc-header-verbose () + "Very verbose, some say gross." + (let* ((sc-mumble-string "") + (whofrom (sc-field "from")) + (reply (sc-field "sc-reply-address")) + (from (sc-field "sc-from-address")) + (author (sc-field "sc-author")) + (date (sc-field "date")) + (org (sc-field "organization")) + (msgid (sc-field "message-id")) + (ngrps (sc-field "newsgroups")) + (subj (sc-field "subject")) + (refs (sc-field "references")) + (cite (sc-field "sc-citation")) + (nl sc-reference-tag-string)) + (if (not (string= whofrom "")) + (insert (if (not (string= date "")) + (concat nl "On " date ",\n") "") + (concat nl (if (not (string= author "")) + author + whofrom) "\n") + (if (not (string= org "")) + (concat nl "from the organization of " org "\n") "") + (if (not (string= reply "")) + (concat nl "who can be reached at: " reply "\n") + (if (not (string= from "")) + (concat nl "who can be reached at: " from "\n") "")) + (if (not (string= cite "")) + (concat nl "(whose comments are cited below with \"" + cite "\"),\n") "") + (if (not (string= msgid "")) + (concat nl "had this to say in article " msgid "\n") "") + (if (not (string= ngrps "")) + (concat nl "in newsgroups " ngrps "\n") "") + (if (not (string= subj "")) + (concat nl "concerning the subject of " subj "\n") "") + (if (not (string= refs "")) + (concat nl "(see " refs " for more details)\n") "") + )))) + + +;; ====================================================================== +;; this section queries the user for necessary information + +(defun sc-query (&optional default) + "Query for an attribution string with the optional DEFAULT choice. +Returns the string entered by the user, if non-empty and non-nil, or +DEFAULT otherwise. If DEFAULT is not supplied, sc-default-attribution +is used." + (if (not default) (setq default sc-default-attribution)) + (let* ((prompt (concat "Enter attribution string: (default " default ") ")) + (query (read-string prompt))) + (if (or (null query) + (string= query "")) + default + query))) + +(defun sc-confirm () + "Confirm the preferred attribution with the user." + (if (or sc-confirm-always-p + sc-force-confirmation-p) + (aput 'sc-gal-attributions + (let* ((default (aheadsym sc-gal-attributions)) + chosen + (prompt (concat "Complete " + (cond + ((eq sc-cite-context 'citing) "cite") + ((eq sc-cite-context 'reciting) "recite") + (t "")) + " attribution string: (default " + default ") ")) + (minibuffer-local-completion-map + (copy-keymap minibuffer-local-completion-map))) + (define-key minibuffer-local-completion-map "\C-g" + '(lambda () (interactive) (beep) (throw 'select-abort nil))) + (setq chosen (completing-read prompt sc-gal-attributions)) + (if (or (not chosen) + (string= chosen "")) + default + chosen))))) + + +;; ====================================================================== +;; this section contains primitive functions used in the email address +;; parsing schemes. they extract name fields from various parts of +;; the "from:" field. + +(defun sc-style1-addresses (from-string &optional delim) + "Extract the author's email terminus from email address FROM-STRING. +Match addresses of the style \"name%[stuff].\" when called with DELIM +of \"%\" and addresses of the style \"[stuff]name@[stuff]\" when +called with DELIM \"@\". If DELIM is nil or not provided, matches +addresses of the style \"name\"." + (and (string-match (concat "[a-zA-Z0-9_-]+" delim) from-string 0) + (substring from-string + (match-beginning 0) + (- (match-end 0) (if (null delim) 0 1))))) + +(defun sc-style2-addresses (from-string) + "Extract the author's email terminus from email address FROM-STRING. +Match addresses of the style \"[stuff]![stuff]...!name[stuff].\"" + (let ((eos (length from-string)) + (mstart (string-match "![a-zA-Z0-9_-]+\\([^!a-zA-Z0-9_-]\\|$\\)" + from-string 0)) + (mend (match-end 0))) + (and mstart + (substring from-string (1+ mstart) (- mend (if (= mend eos) 0 1))) + ))) + +(defun sc-get-address (from-string author) + "Get the full email address path from FROM-STRING. +AUTHOR is the author's name (which is removed from the address)." + (let ((eos (length from-string))) + (if (string-match (concat "\\(^\\|^\"\\)" author + "\\(\\s +\\|\"\\s +\\)") from-string 0) + (let ((addr (substring from-string (match-end 0) eos))) + (if (and (= (aref addr 0) ?<) + (= (aref addr (1- (length addr))) ?>)) + (substring addr 1 (1- (length addr))) + addr)) + (if (string-match "[a-zA-Z0-9!@%._-]+" from-string 0) + (substring from-string (match-beginning 0) (match-end 0)) + "") + ))) + +(defun sc-get-emailname (from-string) + "Get the email terminus name from FROM-STRING." + (cond + ((sc-style1-addresses from-string "%")) + ((sc-style1-addresses from-string "@")) + ((sc-style2-addresses from-string)) + ((sc-style1-addresses from-string nil)) + (t (substring from-string 0 10)))) + + +;; ====================================================================== +;; this section contains functions that will extract a list of names +;; from the name field string. + +(defun sc-spacify-name-chars (name) + (let ((len (length name)) + (s 0)) + (while (< s len) + (if (memq (aref name s) sc-spacify-name-chars) + (aset name s 32)) + (setq s (1+ s))) + name)) + +(defun sc-name-substring (string start end extend) + "Extract the specified substring of STRING from START to END. +EXTEND is the number of characters on each side to extend the +substring." + (and start + (let ((sos (+ start extend)) + (eos (- end extend))) + (substring string sos + (or (string-match sc-titlecue-regexp string sos) eos) + )))) + +(defun sc-extract-namestring (from-string) + "Extract the name string from FROM-STRING. +This should be the author's full name minus an optional title." + (let ((pstart (string-match "(.*)" from-string 0)) + (pend (match-end 0)) + (qstart (string-match "\".*\"" from-string 0)) + (qend (match-end 0)) + (bstart (string-match "\\([.a-zA-Z0-9_-]+\\s *\\)+" from-string 0)) + (bend (match-end 0))) + (sc-spacify-name-chars + (cond + ((sc-name-substring from-string pstart pend 1)) + ((sc-name-substring from-string qstart qend 1)) + ((sc-name-substring from-string bstart bend 0)) + )))) + +(defun sc-chop-namestring (namestring) + "Convert NAMESTRING to a list of names. + + example: (sc-namestring-to-list \"John Xavier Doe\") + => (\"John\" \"Xavier\" \"Doe\")" + (if (not (string= namestring "")) + (append (list (sc-string-car namestring)) + (sc-chop-namestring (sc-string-cdr namestring))))) + +(defun sc-strip-initials (namelist) + "Extract the author's initials from the NAMELIST." + (if (not namelist) + nil + (concat (if (string= (car namelist) "") + "" + (substring (car namelist) 0 1)) + (sc-strip-initials (cdr namelist))))) + + +;; ====================================================================== +;; this section handles selection of the attribution and citation strings + +(defun sc-populate-alists (from-string) + "Put important and useful information in the alists using FROM-STRING. +Return the list of name symbols." + (let* ((namelist (sc-chop-namestring (sc-extract-namestring from-string))) + (revnames (reverse (cdr namelist))) + (midnames (reverse (cdr revnames))) + (firstname (car namelist)) + (midnames (reverse (cdr revnames))) + (lastname (car revnames)) + (initials (sc-strip-initials namelist)) + (emailname (sc-get-emailname from-string)) + (n 1) + (symlist (list 'emailname 'initials 'firstname 'lastname))) + + ;; put basic information + (aput 'sc-gal-attributions 'firstname firstname) + (aput 'sc-gal-attributions 'lastname lastname) + (aput 'sc-gal-attributions 'emailname emailname) + (aput 'sc-gal-attributions 'initials initials) + + (aput 'sc-gal-information "sc-firstname" firstname) + (aput 'sc-gal-information "sc-lastname" lastname) + (aput 'sc-gal-information "sc-emailname" emailname) + (aput 'sc-gal-information "sc-initials" initials) + + ;; put middle names and build sc-author entry + (let ((author (concat firstname " "))) + (while midnames + (let ((name (car midnames)) + (next (cdr midnames)) + (symbol (intern (format "middlename%d" n))) + (string (format "sc-middlename-%d" n))) + ;; first put new middlename + (aput 'sc-gal-attributions symbol name) + (aput 'sc-gal-information string name) + (setq n (1+ n)) + (nconc symlist (list symbol)) + + ;; now build author name + (setq author (concat author name " ")) + + ;; incr loop + (setq midnames next) + )) + (setq author (concat author lastname)) + + ;; put author name and email address + (aput 'sc-gal-information "sc-author" author) + (aput 'sc-gal-information "sc-from-address" + (sc-get-address from-string author)) + (aput 'sc-gal-information "sc-reply-address" + (sc-get-address (sc-field "reply-to") author)) + ) + ;; return value + symlist)) + +(defun sc-sort-attribution-alist () + "Put preferred attribution at head of attributions alist." + (asort 'sc-gal-attributions sc-preferred-attribution) + + ;; use backup scheme if preference is not legal + (if (or (null sc-preferred-attribution) + (anot-head-p sc-gal-attributions sc-preferred-attribution) + (let ((prefval (aget sc-gal-attributions + sc-preferred-attribution))) + (or (null prefval) + (string= prefval "")))) + ;; no legal attribution + (if sc-use-only-preference-p + (aput 'sc-gal-attributions 'sc-user-query + (sc-query sc-default-attribution)) + ;; else use secondary scheme + (asort 'sc-gal-attributions 'firstname)))) + +(defun sc-build-attribution-alist (from-string) + "Extract attributions from FROM-STRING, applying preferences." + (let ((symlist (sc-populate-alists from-string)) + (headval (progn (sc-sort-attribution-alist) + (aget sc-gal-attributions + (aheadsym sc-gal-attributions) t)))) + + ;; for each element in the symlist, remove the corresponding + ;; key-value pair in the alist, then insert just the value. + (while symlist + (let ((value (aget sc-gal-attributions (car symlist) t)) + (next (cdr symlist))) + (if (not (or (null value) + (string= value ""))) + (aput 'sc-gal-attributions value)) + (adelete 'sc-gal-attributions (car symlist)) + (setq symlist next))) + + ;; add nicknames to the completion list + (let ((gal sc-gal-attributions)) + (while gal + (let ((nns sc-nicknames-alist) + (galname (car (car gal)))) + (while nns + (if (string= galname (car (car nns))) + (aput 'sc-gal-attributions (car (cdr (car nns))))) + (setq nns (cdr nns))) + (setq gal (cdr gal))))) + + ;; now reinsert the head (preferred) attribution unless it is nil, + ;; this effectively just moves the head value to the front of the + ;; list. + (if headval + (aput 'sc-gal-attributions headval)) + + ;; check to be sure alist is not nil + (if (null sc-gal-attributions) + (aput 'sc-gal-attributions sc-default-attribution)))) + +(defun sc-select () + "Select an attribution and create a citation string." + (cond + (sc-nested-citation-p + (sc-update-gal "")) + ((null (aget sc-gal-information "from" t)) + (aput 'sc-gal-information "sc-author" sc-default-author-name) + (sc-update-gal (sc-query sc-default-attribution))) + ((null sc-gal-attributions) + (sc-build-attribution-alist (aget sc-gal-information "from" t)) + (sc-confirm) + (sc-update-gal (aheadsym sc-gal-attributions))) + (t + (sc-confirm) + (sc-update-gal (aheadsym sc-gal-attributions)))) + t) + + +;; ====================================================================== +;; region citing and unciting + +(defun sc-cite-region (start end) + "Cite a region delineated by START and END." + (save-excursion + ;; set real end-of-region + (goto-char end) + (forward-line 1) + (set-mark (point)) + ;; goto real beginning-of-region + (goto-char start) + (beginning-of-line) + (let ((fstart (point)) + (fend (point))) + (while (< (point) (sc-mark)) + ;; remove leading whitespace if desired + (and sc-fixup-whitespace-p + (fixup-whitespace)) + ;; if end of line then perhaps autofill + (cond ((eolp) + (or (= fstart fend) + (not sc-auto-fill-region-p) + (and sc-auto-fill-query-each-paragraph-p + (not (y-or-n-p "Fill this paragraph? "))) + (save-excursion (set-mark fend) + (goto-char (/ (+ fstart fend 1) 2)) + (run-hooks 'sc-fill-paragraph-hook))) + (setq fstart (point) + fend (point))) + ;; not end of line so perhap cite it + ((not (looking-at sc-cite-regexp)) + (insert (aget sc-gal-information "sc-citation"))) + (sc-nested-citation-p + (insert (aget sc-gal-information "sc-nested-citation")))) + (setq fend (point)) + (forward-line 1)) + (and sc-auto-fill-query-each-paragraph-p + (message " ")) + ))) + +(defun sc-uncite-region (start end cite-regexp) + "Uncite a previously cited region delineated by START and END. +CITE-REGEXP describes how a cited line of texts starts. Unciting also +auto-fills paragraph if sc-auto-fill-region-p is non-nil." + (save-excursion + (set-mark end) + (goto-char start) + (beginning-of-line) + (let ((fstart (point)) + (fend (point))) + (while (< (point) (sc-mark)) + ;; if end of line, then perhaps autofill + (cond ((eolp) + (or (= fstart fend) + (not sc-auto-fill-region-p) + (and sc-auto-fill-query-each-paragraph-p + (not (y-or-n-p "Fill this paragraph? "))) + (save-excursion (set-mark fend) + (goto-char (/ (+ fstart fend 1) 2)) + (run-hooks 'sc-fill-paragraph-hook))) + (setq fstart (point) + fend (point))) + ;; not end of line so perhaps uncite it + ((looking-at cite-regexp) + (save-excursion + (save-restriction + (narrow-to-region (sc-linepos 'bol) (sc-linepos)) + (beginning-of-line) + (delete-region (point-min) + (progn (re-search-forward cite-regexp + (point-max) + t) + (match-end 0))))))) + (setq fend (point)) + (forward-line 1))))) + + +;; ====================================================================== +;; this section contains paragraph filling support + +(defun sc-guess-fill-prefix (&optional literalp) + "Guess the fill prefix used on the current line. +Use various heuristics to find the fill prefix. Search begins on first +non-blank line after point. + + 1) If fill-prefix is already bound to the empty string, return + nil. + + 2) If fill-prefix is already bound, but not to the empty + string, return the value of fill-prefix. + + 3) If the current line starts with the last chosen citation + string, then that string is returned. + + 4) If the current line starts with a string matching the regular + expression sc-cite-regexp, return the match. Note that if + optional LITERALP is provided and non-nil, then the *string* + that matches the regexp is return. Otherwise, if LITERALP is + not provided or is nil, the *regexp* sc-cite-regexp is + returned. + + 5) If the current line starts with any number of characters, + followed by the sc-citation-delimiter and then white space, + that match is returned. See comment #4 above for handling of + LITERALP. + + 6) Nil is returned." + (save-excursion + ;; scan for first non-blank line in the region + (beginning-of-line) + (skip-chars-forward "\n\t ") + (beginning-of-line) + (let ((citation (aget sc-gal-information "sc-citation")) + (generic-citation + (concat "\\s *[^ \t\n" sc-citation-delimiter "]+>\\s +"))) + (cond + ((string= fill-prefix "") nil) ;; heuristic #1 + (fill-prefix) ;; heuristic #2 + ((looking-at (regexp-quote citation)) citation) ;; heuristic #3 + ((looking-at sc-cite-regexp) ;; heuristic #4 + (if literalp + (buffer-substring + (point) + (progn (re-search-forward (concat sc-cite-regexp "\\s *") + (point-max) nil) + (point))) + sc-cite-regexp)) + ((looking-at generic-citation) ;; heuristic #5 + (if literalp + (buffer-substring + (point) + (progn (re-search-forward generic-citation) (point))) + generic-citation)) + (t nil))))) ;; heuristic #6 + +(defun sc-consistant-cite-p (prefix) + "Check current paragraph for consistant citation. +Scans to paragraph delineated by (forward|backward)-paragraph to see +if all lines start with PREFIX. Returns t if entire paragraph is +consistantly cited, nil otherwise." + (save-excursion + (let ((end (progn (forward-paragraph) + (beginning-of-line) + (or (not (eolp)) + (forward-char -1)) + (point))) + (start (progn (backward-paragraph) + (beginning-of-line) + (or (not (eolp)) + (forward-char 1)) + (point))) + (badline t)) + (goto-char start) + (beginning-of-line) + (while (and (< (point) end) + badline) + (setq badline (looking-at prefix)) + (forward-line 1)) + badline))) + +(defun sc-fill-start (fill-prefix) + "Find buffer position of start of region which begins with FILL-PREFIX. +Restrict scan to current paragraph." + (save-excursion + (let ((badline nil) + (top (save-excursion + (backward-paragraph) + (beginning-of-line) + (or (not (eolp)) + (forward-char 1)) + (point)))) + (while (and (not badline) + (> (point) top)) + (forward-line -1) + (setq badline (not (looking-at fill-prefix))))) + (forward-line 1) + (point))) + +(defun sc-fill-end (fill-prefix) + "Find the buffer position of end of region which begins with FILL-PREFIX. +Restrict scan to current paragraph." + (save-excursion + (let ((badline nil) + (bot (save-excursion + (forward-paragraph) + (beginning-of-line) + (or (not (eolp)) + (forward-char -1)) + (point)))) + (while (and (not badline) + (< (point) bot)) + (beginning-of-line) + (setq badline (not (looking-at fill-prefix))) + (forward-line 1))) + (forward-line -1) + (point))) + +(defun sc-fill-paragraph () + "Supercite's paragraph fill function. +Fill the paragraph containing or following point. Use +sc-guess-fill-prefix to find the fill-prefix for the paragraph. + +If the paragraph is inconsistantly cited (mixed fill-prefix), then the +user is queried to restrict the the fill to only those lines around +point which begin with the fill prefix. + +The variable sc-fill-arg is passed to fill-paragraph and +fill-region-as-paragraph which controls justification of the +paragraph. sc-fill-arg is set by sc-fill-paragraph-manually." + (save-excursion + (let ((pnt (point)) + (fill-prefix (sc-guess-fill-prefix t))) + (cond + ((not fill-prefix) + (fill-paragraph sc-fill-arg)) + ((sc-consistant-cite-p fill-prefix) + (fill-paragraph sc-fill-arg)) + ((y-or-n-p "Inconsistent citation found. Restrict? ") + (message "") + (fill-region-as-paragraph (progn (goto-char pnt) + (sc-fill-start fill-prefix)) + (progn (goto-char pnt) + (sc-fill-end fill-prefix)) + sc-fill-arg)) + (t + (message "") + (progn + (setq fill-prefix (aget sc-gal-information "sc-citation")) + (fill-paragraph sc-fill-arg))))))) + + +;; ====================================================================== +;; the following functions are the top level, interactive commands that +;; can be bound to key strokes + +(defun sc-insert-reference (arg) + "Insert, at point, a reference header in the body of the reply. +Numeric ARG indicates which header style from sc-rewrite-header-list +to use when rewriting the header. No supplied ARG indicates use of +sc-preferred-header-style. + +With just \\[universal-argument], electric reference insert mode is +entered, regardless of the value of sc-electric-references-p. See +sc-electric-mode for more information." + (interactive "P") + (if (consp arg) + (sc-electric-mode) + (let ((pref (cond ((sc-valid-index-p arg) arg) + ((sc-valid-index-p sc-preferred-header-style) + sc-preferred-header-style) + (t 0)))) + (if sc-electric-references-p (sc-electric-mode pref) + (condition-case err + (eval (nth pref sc-rewrite-header-list)) + (void-function + (progn (message + "Symbol's function definition is void: %s. (Header %d)." + (symbol-name (car (cdr err))) + pref) + (beep))) + (error + (progn (message "Error evaluating rewrite header function %d." + pref) + (beep))) + ))))) + +(defun sc-cite (arg) + "Cite the region of text between point and mark. +Numeric ARG, if supplied, is passed unaltered to sc-insert-reference." + (interactive "P") + (if (not (sc-mark)) + (error "Please designate a region to cite (i.e. set the mark).")) + (catch 'select-abort + (let ((sc-cite-context 'citing) + (sc-force-confirmation-p (interactive-p))) + (sc-select) + (undo-boundary) + (let ((xchange (if (> (sc-mark) (point)) nil + (exchange-point-and-mark) + t))) + (sc-insert-reference arg) + (sc-cite-region (point) (sc-mark)) + ;; leave point on first cited line + (while (and (< (point) (sc-mark)) + (not (looking-at (aget sc-gal-information + (if sc-nested-citation-p + "sc-nested-citation" + "sc-citation"))))) + (forward-line 1)) + (and xchange + (exchange-point-and-mark)) + )))) + +(defun sc-uncite () + "Uncite the region between point and mark." + (interactive) + (if (not (sc-mark)) + (error "Please designate a region to uncite (i.e. set the mark).")) + (undo-boundary) + (let ((xchange (if (> (sc-mark) (point)) nil + (exchange-point-and-mark) + t)) + (fp (or (sc-guess-fill-prefix) + ""))) + (sc-uncite-region (point) (sc-mark) fp) + (and xchange + (exchange-point-and-mark)))) + +(defun sc-recite () + "Recite the region by first unciting then citing the text." + (interactive) + (if (not (sc-mark)) + (error "Please designate a region to recite (i.e. set the mark).")) + (catch 'select-abort + (let ((sc-cite-context 'reciting) + (sc-force-confirmation-p t)) + (sc-select) + (undo-boundary) + (let ((xchange (if (> (sc-mark) (point)) nil + (exchange-point-and-mark) + t)) + (fp (or (sc-guess-fill-prefix) + ""))) + (sc-uncite-region (point) (sc-mark) fp) + (sc-cite-region (point) (sc-mark)) + (and xchange + (exchange-point-and-mark)) + )))) + +(defun sc-insert-citation () + "Insert citation string at beginning of current line." + (interactive) + (save-excursion + (beginning-of-line) + (insert (aget sc-gal-information "sc-citation")))) + +(defun sc-open-line (arg) + "Insert a newline and leave point before it. +Also inserts the guessed prefix at the beginning of the new line. With +numeric ARG, inserts that many new lines." + (interactive "p") + (save-excursion + (let ((start (point)) + (string (or (sc-guess-fill-prefix t) + ""))) + (open-line arg) + (goto-char start) + (forward-line 1) + (while (< 0 arg) + (insert string) + (forward-line 1) + (setq arg (- arg 1)))))) + +(defun sc-fill-paragraph-manually (arg) + "Fill current cited paragraph. +Really just runs the hook sc-fill-paragraph-hook, however it does set +the global variable sc-fill-arg to the value of ARG. This is +currently the only way to pass an argument to a hookified function." + (interactive "P") + (setq sc-fill-arg arg) + (run-hooks 'sc-fill-paragraph-hook)) + +(defun sc-modify-information (arg) + "Interactively modify information in the information alist. +\\[universal-argument] if supplied, deletes the entry from the alist. +You can add an entry by supplying a key instead of completing." + (interactive "P") + (let* ((delete-p (consp arg)) + (action (if delete-p "delete" "modify")) + (defaultkey (aheadsym sc-gal-information)) + (prompt (concat "Select information key to " + action ": (default " + defaultkey ") ")) + (key (completing-read prompt sc-gal-information)) + ) + (if (or (string= key "") + (null key)) + (setq key defaultkey)) + (if delete-p (adelete 'sc-gal-information key) + (let* ((oldval (aget sc-gal-information key t)) + (prompt (concat "Enter new value for key \"" + key "\" (default \"" oldval "\") ")) + (newval (read-input prompt))) + (if (or (string= newval "") + (null newval)) + nil + (aput 'sc-gal-information key newval) + ))))) + +(defun sc-view-field (arg) + "View field values in the information alist. +This is essentially an interactive version of sc-field, and is similar +to sc-modify-information, except that the field values can't be +modified. With \\[universal-argument], if supplied, inserts the value +into the current buffer as well." + (interactive "P") + (let* ((defaultkey (aheadsym sc-gal-information)) + (prompt (concat "View information key: (default " + defaultkey ") ")) + (key (completing-read prompt sc-gal-information))) + (if (or (string= key "") + (null key)) + (setq key defaultkey)) + (let* ((val (aget sc-gal-information key t)) + (pval (if val (concat "\"" val "\"") "nil"))) + (message "value of key %s: %s" key pval) + (if (and key (consp arg)) (insert val))))) + +(defun sc-glom-headers () + "Glom information from mail headers in region between point and mark. +Any old information is lost, unless an error occurs." + (interactive) + (let ((attr (copy-sequence sc-gal-attributions)) + (info (copy-sequence sc-gal-information))) + (setq sc-gal-attributions nil + sc-gal-information nil) + (let ((start (region-beginning)) + (end (region-end)) + (sc-force-confirmation-p t) + (sc-cite-context nil)) + (sc-fetch-fields start end) + (if (null sc-gal-information) + (progn + (message "No mail headers found! Restoring old information.") + (setq sc-gal-attributions attr + sc-gal-information info)) + (sc-mail-yank-clear-headers start end) + (if (not (catch 'select-abort + (condition-case foo + (sc-select) + (quit (beep) (throw 'select-abort nil))) + )) + (setq sc-gal-attributions attr + sc-gal-information info)) + )))) + +(defun sc-version (arg) + "Show supercite version. +Universal argument (\\[universal-argument]) ARG inserts version +information in the current buffer instead of printing the message in +the echo area." + (interactive "P") + (if (consp arg) + (insert "Using Supercite version " sc-version-number) + (message "Using Supercite version %s" sc-version-number))) + + +;; ====================================================================== +;; leach onto current mode + +(defun sc-append-current-keymap () + "Append some useful key bindings to the current local key map. +This searches sc-local-keymap for the keymap to install based on the +major-mode of the current buffer." + (let ((hook (car (cdr (assq major-mode sc-local-keymaps))))) + (cond + ((not hook) + (run-hooks 'sc-default-keymap)) + ((not (listp hook)) + (setq hook (car (cdr (assq hook sc-local-keymaps)))) + (run-hooks 'hook)) + (t + (run-hooks 'hook)))) + (setq sc-leached-keymap (current-local-map))) + +(defun sc-snag-all-keybindings () + "Snag all keybindings in major-mode's current keymap." + (let* ((curkeymap (current-local-map)) + (symregexp ".*sc-.*\n") + (docstring (substitute-command-keys "\\{curkeymap}")) + (start 0) + (maxend (length docstring)) + (spooge "")) + (while (and (< start maxend) + (string-match symregexp docstring start)) + (setq spooge (concat spooge (substring docstring + (match-beginning 0) + (match-end 0)))) + (setq start (match-end 0))) + spooge)) + +(defun sc-spoogify-docstring () + "Modifies (makes into spooge) the docstring for the current major mode. +This will leach the keybinding descriptions for supercite onto the end +of the current major mode's docstring. If major mode is preloaded, +this function will first make a copy of the list associated with the +mode, then modify this copy." + (let* ((symfunc (symbol-function major-mode)) + (doc-cdr (and (listp symfunc) (nthcdr 2 symfunc))) + (doc-str (documentation major-mode))) + (cond + ;; is a docstring even provided? + ((not (stringp doc-str))) + ;; have we already leached on? + ((string-match "Supercite" doc-str)) + ;; lets build the new doc string + (t + (let* ((described (sc-snag-all-keybindings)) + (commonstr " + +The major mode for this buffer has been modified to include the +Supercite 2.3 package for handling attributions and citations of +original messages in email replies. For more information on this +package, type \"\\[sc-describe]\".") + (newdoc-str + (concat doc-str commonstr + (if (not (string= described "")) + (concat "\n\nThe following keys are bound " + "to Supercite commands:\n\n" + described))) + )) + (cond + (doc-cdr + (condition-case nil + (setcar doc-cdr newdoc-str) + (error + ;; the major mode must be preloaded, make a copy first + (setq symfunc (copy-sequence (symbol-function major-mode)) + doc-cdr (nthcdr 2 symfunc)) + (setcar doc-cdr newdoc-str) + (fset major-mode symfunc)))) + ;; lemacs 19 byte-code. + ;; Set function to a new byte-code vector with the + ;; new documentation in the documentation slot (element 4). + ;; We can't use aset because aset won't allow you to modify + ;; a byte-code vector. + ;; Include element 5 if the vector has one. + (t + (fset major-mode + (apply 'make-byte-code + (aref symfunc 0) (aref symfunc 1) + (aref symfunc 2) (aref symfunc 3) + newdoc-str + (if (> (length symfunc) 5) + (list (aref symfunc 5))))) + ))))))) + + +;; ====================================================================== +;; this section contains default hooks and hook support for execution + +(defun sc-cite-original () + "Hook version of sc-cite. +This is callable from the various mail and news readers' reply +function according to the agreed upon standard. See \\[sc-describe] +for more details. Sc-cite-original does not do any yanking of the +original message but it does require a few things: + + 1) The reply buffer is the current buffer. + + 2) The original message has been yanked and inserted into the + reply buffer. + + 3) Verbose mail headers from the original message have been + inserted into the reply buffer directly before the text of the + original message. + + 4) Point is at the beginning of the verbose headers. + + 5) Mark is at the end of the body of text to be cited." + (run-hooks 'sc-pre-hook) + (setq sc-gal-attributions nil) + (setq sc-gal-information nil) + (let ((start (region-beginning)) + (end (region-end))) + (sc-fetch-fields start end) + (sc-mail-yank-clear-headers start end) + (if (not sc-all-but-cite-p) + (sc-cite sc-preferred-header-style)) + (sc-append-current-keymap) + (sc-spoogify-docstring) + (run-hooks 'sc-post-hook))) + + +;; ====================================================================== +;; describe this package +;; +(defun sc-describe () + "Supercite version 2.3 is now described in a texinfo manual which +makes the documenation available both for online perusal via emacs' +info system, or for hard-copy printing using the TeX facility. + +To view the online document hit \\[info], then \"mSupercite <RET>\"." + (interactive) + (describe-function 'sc-describe)) + +;; ====================================================================== +;; load hook +(run-hooks 'sc-load-hook) +(provide 'sc)