summaryrefslogtreecommitdiff
path: root/lisp/sc.el
diff options
context:
space:
mode:
authorRichard M. Stallman <rms@gnu.org>1993-01-08 15:30:08 +0000
committerRichard M. Stallman <rms@gnu.org>1993-01-08 15:30:08 +0000
commitac96728ddaeb952da40ebda93e7b82b5953601a5 (patch)
tree2b0378530d39d621f0d7bcfd6900c266a3ab0afd /lisp/sc.el
parent2e9c66ab52c654a568bebeeca78e5ee7a6143608 (diff)
downloademacs-ac96728ddaeb952da40ebda93e7b82b5953601a5.tar.gz
Initial revision
Diffstat (limited to 'lisp/sc.el')
-rw-r--r--lisp/sc.el1563
1 files changed, 1563 insertions, 0 deletions
diff --git a/lisp/sc.el b/lisp/sc.el
new file mode 100644
index 00000000000..30b1b5f64e5
--- /dev/null
+++ b/lisp/sc.el
@@ -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)