Initial Commit
[packages] / xemacs-packages / supercite / supercite.el
1 ;;; supercite.el --- minor mode for citing mail and news replies
2
3 ;; Author: 1993 Barry A. Warsaw, Century Computing, Inc. <bwarsaw@python.org>
4 ;; Maintainer:    The XEmacs Development Team <xemacs-beta@xemacs.org>
5 ;; Created:       February 1993
6 ;; Version:       3.1
7 ;; Last Modified: Fri Aug  4 15:01:46 CEST 2000
8 ;; Keywords: citation attribution mail news article reply followup
9
10 ;; supercite.el revision: 3.55-x
11
12 ;; Copyright (C) 1993 Barry A. Warsaw
13
14 ;; This file is part of XEmacs.
15
16 ;; XEmacs is free software; you can redistribute it and/or modify it
17 ;; under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; any later version.
20
21 ;; XEmacs is distributed in the hope that it will be useful, but
22 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24 ;; General Public License for more details.
25
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with XEmacs; see the file COPYING.  If not, write to the
28 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
29 ;; Boston, MA 02111-1307, USA.
30
31 ;;; Synched up with: Not synched.
32
33 ;; LCD Archive Entry
34 ;; supercite|Barry A. Warsaw|supercite-help@python.org
35 ;; |Mail and news reply citation package
36 ;; |1993/09/22 18:58:46|3.1|
37
38 ;; njsf 2005/10/28: Added customize for extract address components
39 ;; sb 1997/May/07:  Add autoloads
40 ;; sb 1997/Apr/02:  Added attribution function sc-header-author-email-writes
41 ;; which gives attribution in the form -
42 ;; Steve Baur <steve@xemacs.org> writes:
43
44 ;; Modified by Bob Weiner <weiner@infodock.com>, 8/5/95, for use in InfoDock.
45 ;;   Added sc-rewrite-address-function and sc-rewrite-region-function
46 ;;   variables.  See their doc strings.
47 ;;   Modified these functions to handle ugly Motorola X.400 addresses.
48 ;;     sc-attribs-%@-addresses, sc-attribs-chop-namestring, sc-get-address,
49 ;;     sc-mail-process-headers.
50 ;;
51 ;; Modified by Bob Weiner <weiner@infodock.com>, 3/1/97, for use in InfoDock.
52 ;;   Added removal of [bracketed] terms from attributed names via
53 ;;   `sc-name-filter-alist'.
54
55 ;; Code:
56
57 \f
58 (require 'regi)
59
60 ;; start user configuration variables
61 ;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
62
63 (defgroup supercite nil
64   "Supercite package"
65   :prefix "sc-"
66   :group 'mail
67   :group 'news)
68
69 (defgroup supercite-frames nil
70   "Supercite (regi) frames"
71   :prefix "sc-"
72   :group 'supercite)
73
74 (defgroup supercite-attr nil
75   "Supercite attributions"
76   :prefix "sc-"
77   :group 'supercite)
78
79 (defgroup supercite-cite nil
80   "Supercite citings"
81   :prefix "sc-"
82   :group 'supercite)
83
84 (defgroup supercite-hooks nil
85   "Hooking into supercite"
86   :prefix "sc-"
87   :group 'supercite)
88
89 (defcustom sc-rewrite-address-function nil
90   "*Function to rewrite addresses prior to being parsed by Supercite.
91 It should take as its only parameter an email address."
92   :type 'function
93   :group 'supercite)
94
95 (defcustom sc-rewrite-region-function nil
96   "*Function to rewrite addresses prior to being parsed by Supercite."
97   :type 'function
98   :group 'supercite)
99
100 (defcustom sc-extract-address-components 'mail-extract-address-components
101   "*Function for extracting address components from a From header.
102 One pre-defined function exist: `mail-extract-address-components'."
103 :group 'supercite
104 :type '(radio (function-item mail-extract-address-components)
105                 (function :tag "Other")))
106
107 (defcustom sc-auto-fill-region-p t
108   "*If non-nil, automatically fill each paragraph after it has been cited."
109   :type 'boolean
110   :group 'supercite)
111
112 (defcustom sc-blank-lines-after-headers 1
113   "*Number of blank lines to leave after mail headers have been nuked.
114 Set to nil, to use whatever blank lines happen to occur naturally."
115   :type '(choice (const :tag "leave" nil)
116                  integer)
117   :group 'supercite)
118
119 (defcustom sc-citation-leader "    "
120   "*String comprising first part of a citation."
121   :type 'string
122   :group 'supercite-cite)
123
124 (defcustom sc-citation-delimiter ">"
125   "*String comprising third part of a citation.
126 This string is used in both nested and non-nested citations."
127   :type 'string
128   :group 'supercite-cite)
129
130 (defcustom sc-citation-separator " "
131   "*String comprising fourth and last part of a citation."
132   :type 'string
133   :group 'supercite-cite)
134
135 (defcustom sc-citation-leader-regexp "[ \t]*"
136   "*Regexp describing citation leader for a cited line.
137 This should NOT have a leading `^' character."
138   :type 'regexp
139   :group 'supercite-cite)
140
141 ;; Nemacs and Mule users note: please see the texinfo manual for
142 ;; suggestions on setting these variables.
143 (defcustom sc-citation-root-regexp "[-._a-zA-Z0-9]*"
144   "*Regexp describing variable root part of a citation for a cited line.
145 This should NOT have a leading `^' character.  See also
146 `sc-citation-nonnested-root-regexp'."
147   :type 'regexp
148   :group 'supercite-cite)
149
150 (defcustom sc-citation-nonnested-root-regexp "[-._a-zA-Z0-9]+"
151   "*Regexp describing the variable root part of a nested citation.
152 This should NOT have a leading `^' character.  This variable is
153 related to `sc-citation-root-regexp' but where as that varariable
154 describes both nested and non-nested citation roots, this variable
155 describes only nested citation roots."
156   :type 'regexp
157   :group 'supercite-cite)
158
159 (defcustom sc-citation-delimiter-regexp "[>]+"
160   "*Regexp describing citation delimiter for a cited line.
161 This should NOT have a leading `^' character."
162   :type 'regexp
163   :group 'supercite-cite)
164
165 (defcustom sc-citation-separator-regexp "[ \t]*"
166   "*Regexp describing citation separator for a cited line.
167 This should NOT have a leading `^' character."
168   :type 'regexp
169   :group 'supercite-cite)
170
171 (defcustom sc-cite-blank-lines-p nil
172   "*If non-nil, put a citation on blank lines."
173   :type 'boolean
174   :group 'supercite-cite)
175
176 (defcustom sc-cite-frame-alist '()
177   "*Alist for frame selection during citing.
178 Each element of this list has the following form:
179
180    (INFOKEY ((REGEXP . FRAME)
181              (REGEXP . FRAME)
182              (...)))
183
184 Where INFOKEY is a key for `sc-mail-field', REGEXP is a regular
185 expression to match against the INFOKEY's value.  FRAME is a citation
186 frame, or a variable containing a citation frame."
187   :type '(repeat (list symbol (repeat (cons regexp
188                                             (choice (repeat (repeat sexp))
189                                                     symbol)))))
190   :group 'supercite-frames)
191
192 (defcustom sc-uncite-frame-alist '()
193   "*Alist for frame selection during unciting.
194 See the variable `sc-cite-frame-alist' for details."
195   :type '(repeat (list symbol (repeat (cons regexp
196                                             (choice (repeat (repeat sexp))
197                                                     symbol)))))
198   :group 'supercite-frames)
199
200 (defcustom sc-recite-frame-alist '()
201   "*Alist for frame selection during reciting.
202 See the variable `sc-cite-frame-alist' for details."
203   :type '(repeat (list symbol (repeat (cons regexp
204                                             (choice (repeat (repeat sexp))
205                                                     symbol)))))
206   :group 'supercite-frames)
207
208 (defcustom sc-default-cite-frame
209   '(;; initialize fill state and temporary variables when entering
210     ;; frame. this makes things run much faster
211     (begin (progn
212              (sc-fill-if-different)
213              (setq sc-tmp-nested-regexp (sc-cite-regexp "")
214                    sc-tmp-nonnested-regexp (sc-cite-regexp)
215                    sc-tmp-dumb-regexp
216                    (concat "\\("
217                            (sc-cite-regexp "")
218                            "\\)"
219                            (sc-cite-regexp sc-citation-nonnested-root-regexp))
220                    )))
221     ;; blank lines mean paragraph separators, so fill the last cited
222     ;; paragraph, unless sc-cite-blank-lines-p is non-nil, in which
223     ;; case we treat blank lines just like any other line.
224     ("^[ \t]*$"                 (if sc-cite-blank-lines-p
225                                     (sc-cite-line)
226                                   (sc-fill-if-different "")))
227     ;; do nothing if looking at a reference tag. make sure that the
228     ;; tag string isn't the empty string since this will match every
229     ;; line.  it cannot be nil.
230     (sc-reference-tag-string    (if (string= sc-reference-tag-string "")
231                                     (list 'continue)
232                                   nil))
233     ;; this regexp catches nested citations in which the author cited
234     ;; a non-nested citation with a dumb citer.
235     (sc-tmp-dumb-regexp         (sc-cite-coerce-dumb-citer))
236     ;; if we are looking at a nested citation then add a citation level
237     (sc-tmp-nested-regexp       (sc-add-citation-level))
238     ;; if we're looking at a non-nested citation, coerce it to our style
239     (sc-tmp-nonnested-regexp    (sc-cite-coerce-cited-line))
240     ;; we must be looking at an uncited line. if we are in nested
241     ;; citations, just add a citation level
242     (sc-nested-citation-p       (sc-add-citation-level))
243     ;; we're looking at an uncited line and we are in non-nested
244     ;; citations, so cite it with a non-nested citation
245     (t                          (sc-cite-line))
246     ;; be sure when we're done that we fill the last cited paragraph.
247     (end                        (sc-fill-if-different ""))
248     )
249   "*Default REGI frame for citing a region."
250   :type '(repeat (repeat sexp))
251   :group 'supercite-frames)
252
253 (defcustom sc-default-uncite-frame
254   '(;; do nothing on a blank line
255     ("^[ \t]*$"       nil)
256     ;; if the line is cited, uncite it
257     ((sc-cite-regexp) (sc-uncite-line))
258     )
259   "*Default REGI frame for unciting a region."
260   :type '(repeat (repeat sexp))
261   :group 'supercite-frames)
262
263 (defcustom sc-default-recite-frame
264   '(;; initialize fill state when entering frame
265     (begin            (sc-fill-if-different))
266     ;; do nothing on a blank line
267     ("^[ \t]*$"       nil)
268     ;; if we're looking at a cited line, recite it
269     ((sc-cite-regexp) (sc-recite-line (sc-cite-regexp)))
270     ;; otherwise, the line is uncited, so just cite it
271     (t                (sc-cite-line))
272     ;; be sure when we're done that we fill the last cited paragraph.
273     (end              (sc-fill-if-different ""))
274     )
275   "*Default REGI frame for reciting a region."
276   :type '(repeat (repeat sexp))
277   :group 'supercite-frames)
278
279 (defcustom sc-cite-region-limit t
280   "*This variable controls automatic citation of yanked text.
281 Legal values are:
282
283 non-nil   -- cite the entire region, regardless of its size
284 nil       -- do not cite the region at all
285 <integer> -- a number indicating the threshold for citation.  When
286              the number of lines in the region is greater than this
287              value, a warning message will be printed and the region
288              will not be cited.  Lines in region are counted with
289              `count-lines'.
290
291 The gathering of attribution information is not affected by the value
292 of this variable.  The number of lines in the region is calculated
293 *after* all mail headers are removed.  This variable is only consulted
294 during the initial citing via `sc-cite-original'."
295   :type '(choice (const :tag "always cite" t)
296                  (const :tag "do not cite" nil)
297                  (integer :tag "citation threshold"))
298   :group 'supercite-cite)
299
300 (defcustom sc-confirm-always-p t
301   "*If non-nil, always confirm attribution string before citing text body."
302   :type 'boolean
303   :group 'supercite-attr)
304
305 (defcustom sc-default-attribution "Anon"
306   "*String used when author's attribution cannot be determined."
307   :type 'string
308   :group 'supercite-attr)
309 (defcustom sc-default-author-name "Anonymous"
310   "*String used when author's name cannot be determined."
311   :type 'string
312   :group 'supercite-attr)
313 (defcustom sc-downcase-p nil
314   "*Non-nil means downcase the attribution and citation strings."
315   :type 'boolean
316   :group 'supercite-attr
317   :group 'supercite-cite)
318 (defcustom sc-electric-circular-p t
319   "*If non-nil, treat electric references as circular."
320   :type 'boolean
321   :group 'supercite-attr)
322
323 (defcustom sc-electric-mode-hook nil
324   "*Hook for `sc-electric-mode' electric references mode."
325   :type 'hook
326   :group 'supercite-hooks)
327 (defcustom sc-electric-references-p nil
328   "*Use electric references if non-nil."
329   :type 'boolean
330   :group 'supercite)
331
332 (defcustom sc-fixup-whitespace-p nil
333   "*If non-nil, delete all leading white space before citing."
334   :type 'boolean
335   :group 'supercite)
336
337 (defcustom sc-load-hook nil
338   "*Hook which gets run once after Supercite loads."
339   :type 'hook
340   :group 'supercite-hooks)
341 (defcustom sc-pre-hook nil
342   "*Hook which gets run before each invocation of `sc-cite-original'."
343   :type 'hook
344   :group 'supercite-hooks)
345 (defcustom sc-post-hook nil
346   "*Hook which gets run after each invocation of `sc-cite-original'."
347   :type 'hook
348   :group 'supercite-hooks)
349
350 (defcustom sc-mail-warn-if-non-rfc822-p t
351   "*Warn if mail headers don't conform to RFC822."
352   :type 'boolean
353   :group 'supercite-attr)
354 (defcustom sc-mumble ""
355   "*Value returned by `sc-mail-field' if field isn't in mail headers."
356   :type 'string
357   :group 'supercite-attr)
358
359 (defcustom sc-name-filter-alist
360   '(
361     ;; This first item removes any [bracketed] multi-word items in names.
362     ;; Each word is already split into a separate string when this filter is
363     ;; applied, hence the complexity of the expression.
364     ("\\[.*\\]\\|\\[[^\]]*\\|[^\[]*\\]" . any)
365     ("^\\(Mr\\|Mrs\\|Ms\\|Dr\\)[.]?$" . 0)
366     ("^\\(Jr\\|Sr\\)[.]?$" . last)
367     ("^ASTS$" . 0)
368     ("^[I]+$" . last))
369   "*Name list components which are filtered out as noise.
370 This variable contains an association list where each element is of
371 the form:  (REGEXP . POSITION).
372
373 REGEXP is a regular expression which matches the name list component.
374 Match is performed using `string-match'.  POSITION is the position in
375 the name list which can match the regular expression, starting at zero
376 for the first element.  Use `last' to match the last element in the
377 list and `any' to match all elements."
378   :type '(repeat (cons regexp (choice (const last) (const any)
379                                       (integer :tag "position"))))
380   :group 'supercite-attr)
381
382 (defcustom sc-nested-citation-p nil
383   "*Controls whether to use nested or non-nested citation style.
384 Non-nil uses nested citations, nil uses non-nested citations."
385   :type 'boolean
386   :group 'supercite)
387
388 (defcustom sc-nuke-mail-headers 'all
389   "*Controls mail header nuking.
390 Used in conjunction with `sc-nuke-mail-header-list'.  Legal values are:
391
392 `all'       -- nuke all mail headers
393 `none'      -- don't nuke any mail headers
394 `specified' -- nuke headers specified in `sc-nuke-mail-header-list'
395 `keep'      -- keep headers specified in `sc-nuke-mail-header-list'"
396   :type '(choice (const all) (const none)
397                  (const specified) (const keep))
398   :group 'supercite)
399
400 (defcustom sc-nuke-mail-header-list nil
401   "*List of mail header regexps to remove or keep in body of reply.
402 This list contains regular expressions describing the mail headers to
403 keep or nuke, depending on the value of `sc-nuke-mail-headers'."
404   :type '(repeat regexp)
405   :group 'supercite)
406
407 (defcustom sc-preferred-attribution-list
408   '("sc-lastchoice" "x-attribution" "firstname" "initials" "lastname")
409   "*Specifies what to use as the attribution string.
410 Supercite creates a list of possible attributions when it scans the
411 mail headers from the original message.  Each attribution choice is
412 associated with a key in an attribution alist.  Supercite tries to
413 pick a \"preferred\" attribution by matching the attribution alist
414 keys against the elements in `sc-preferred-attribution-list' in order.
415 The first non-empty string value found is used as the preferred
416 attribution.
417
418 Note that Supercite now honors the X-Attribution: mail field.  If
419 present in the original message, the value of this field should always
420 be used to select the most preferred attribution since it reflects how
421 the original author would like to be distinguished.  It should be
422 considered bad taste to put any attribution preference key before
423 \"x-attribution\" in this list, except perhaps for \"sc-lastchoice\"
424 \(see below).
425
426 Supercite remembers the last attribution used when reciting an already
427 cited paragraph.  This attribution will always be saved with the
428 \"sc-lastchoice\" key, which can be used in this list.  Note that the
429 last choice is always reset after every call of `sc-cite-original'.
430
431 Barring error conditions, the following preferences are always present
432 in the attribution alist:
433
434 \"emailname\"    -- email terminus name
435 \"initials\"     -- initials of author
436 \"firstname\"    -- first name of author
437 \"lastname\"     -- last name of author
438 \"middlename-1\" -- first middle name of author
439 \"middlename-2\" -- second middle name of author
440 ...
441
442 Middle name indexes can be any positive integer greater than 0,
443 although it is unlikely that many authors will supply more than one
444 middle name, if that many.  The string of all middle names is
445 associated with the key \"middlenames\"."
446   :type '(repeat string)
447   :group 'supercite-attr)
448
449 (defcustom sc-attrib-selection-list nil
450   "*An alist for selecting preferred attribution based on mail headers.
451 Each element of this list has the following form:
452
453    (INFOKEY ((REGEXP . ATTRIBUTION)
454              (REGEXP . ATTRIBUTION)
455              (...)))
456
457 Where INFOKEY is a key for `sc-mail-field', REGEXP is a regular
458 expression to match against the INFOKEY's value. ATTRIBUTION can be a
459 string or a list.  If its a string, then it is the attribution that is
460 selected by `sc-select-attribution'.  If it is a list, it is `eval'd
461 and the return value must be a string, which is used as the selected
462 attribution.  Note that the variable `sc-preferred-attribution-list'
463 must contain an element of the string \"sc-consult\" for this variable
464 to be consulted during attribution selection."
465   :type '(repeat (group (string :tag "Infokey")
466                         (repeat :format "\n%v%i\n"
467                                 (cons :format "\n%v"
468                                       (regexp :tag "     Regexp")
469                                       (choice :tag "Attribution"
470                                               :format "%{%t%}: %[choice%] %v"
471                                               sexp string)))))
472   :group 'supercite-attr)
473
474 (defcustom sc-attribs-preselect-hook nil
475   "*Hook to run before selecting an attribution."
476   :type 'hook
477   :group 'supercite-attr
478   :group 'supercite-hooks)
479 (defcustom sc-attribs-postselect-hook nil
480   "*Hook to run after selecting an attribution, but before confirmation."
481   :type 'hook
482   :group 'supercite-attr
483   :group 'supercite-hooks)
484
485 (defcustom sc-pre-cite-hook nil
486   "*Hook to run before citing a region of text."
487   :type 'hook
488   :group 'supercite-cite
489   :group 'supercite-hooks)
490 (defcustom sc-pre-uncite-hook nil
491   "*Hook to run before unciting a region of text."
492   :type 'hook
493   :group 'supercite-cite
494   :group 'supercite-hooks)
495 (defcustom sc-pre-recite-hook nil
496   "*Hook to run before reciting a region of text."
497   :type 'hook
498   :group 'supercite-cite
499   :group 'supercite-hooks)
500
501 (defcustom sc-preferred-header-style 4
502   "*Index into `sc-rewrite-header-list' specifying preferred header style.
503 Index zero accesses the first function in the list."
504   :type 'integer
505   :group 'supercite)
506
507 (defcustom sc-reference-tag-string ">>>>> "
508   "*String used at the beginning of built-in reference headers."
509   :type 'string
510   :group 'supercite)
511
512 (defcustom sc-rewrite-header-list
513   '((sc-no-header)
514     (sc-header-on-said)
515     (sc-header-inarticle-writes)
516     (sc-header-regarding-adds)
517     (sc-header-attributed-writes)
518     (sc-header-author-writes)
519     (sc-header-verbose)
520     (sc-no-blank-line-or-header)
521     (sc-header-author-email-writes)
522     )
523   "*List of reference header rewrite functions.
524 The variable `sc-preferred-header-style' controls which function in
525 this list is chosen for automatic reference header insertions.
526 Electric reference mode will cycle through this list of functions."
527   :type '(repeat sexp)
528   :group 'supercite)
529
530 (defcustom sc-titlecue-regexp "\\s +-+\\s +"
531   "*Regular expression describing the separator between names and titles.
532 Set to nil to treat entire field as a name."
533   :type '(choice (const :tag "entire field as name" nil)
534                  regexp)
535   :group 'supercite-attr)
536
537 (defcustom sc-use-only-preference-p nil
538   "*Controls what happens when the preferred attribution cannot be found.
539 If non-nil, then `sc-default-attribution' will be used.  If nil, then
540 some secondary scheme will be employed to find a suitable attribution
541 string."
542   :type 'boolean
543   :group 'supercite-attr)
544
545 ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
546 ;; end user configuration variables
547 \f
548 (defconst sc-version "3.1"
549   "Supercite version number.")
550 (defconst sc-help-address "xemacs-beta@xemacs.org"
551   "Address accepting submissions of bug reports.")
552
553 (defvar sc-mail-info nil
554   "Alist of mail header information gleaned from reply buffer.")
555 (defvar sc-attributions nil
556   "Alist of attributions for use when citing.")
557
558 (defconst sc-emacs-features
559   (let ((version 'v18)
560         (flavor  'GNU))
561     (if (or
562          (string= (substring emacs-version 0 2) "19")
563          (string= (substring emacs-version 0 2) "20"))
564         (setq version 'v19))
565     (if (string-match "XEmacs" emacs-version)
566         (setq flavor 'Lucid))
567     ;; cobble up list
568     (list version flavor))
569   "A list describing what version of Emacs we're running on.
570 Known flavors are:
571
572 All GNU18's: (v18 GNU)
573 FSF19.x    : (v19 GNU)
574 Lucid19.x  : (v19 Lucid)")
575
576
577 (defvar sc-tmp-nested-regexp nil
578   "Temporary regepx describing nested citations.")
579 (defvar sc-tmp-nonnested-regexp nil
580   "Temporary regexp describing non-nested citations.")
581 (defvar sc-tmp-dumb-regexp nil
582   "Temp regexp describing non-nested citation cited with a nesting citer.")
583
584 (defvar sc-minor-mode nil
585   "Supercite minor mode on flag.")
586 (defvar sc-mode-string " SC"
587   "Supercite minor mode string.")
588
589 (make-variable-buffer-local 'sc-mail-info)
590 (make-variable-buffer-local 'sc-attributions)
591 (make-variable-buffer-local 'sc-minor-mode)
592
593 \f
594 ;; ======================================================================
595 ;; supercite keymaps
596
597 (defvar sc-mode-map-prefix "\C-c\C-p"
598   "*Key binding to install Supercite keymap.
599 If this is nil, Supercite keymap is not installed.")
600
601 (defvar sc-T-keymap ()
602   "Keymap for sub-keymap of setting and toggling functions.")
603 (if sc-T-keymap
604     ()
605   (setq sc-T-keymap (make-sparse-keymap))
606   (define-key sc-T-keymap "a" 'sc-S-preferred-attribution-list)
607   (define-key sc-T-keymap "b" 'sc-T-mail-nuke-blank-lines)
608   (define-key sc-T-keymap "c" 'sc-T-confirm-always)
609   (define-key sc-T-keymap "d" 'sc-T-downcase)
610   (define-key sc-T-keymap "e" 'sc-T-electric-references)
611   (define-key sc-T-keymap "f" 'sc-T-auto-fill-region)
612   (define-key sc-T-keymap "h" 'sc-T-describe)
613   (define-key sc-T-keymap "l" 'sc-S-cite-region-limit)
614   (define-key sc-T-keymap "n" 'sc-S-mail-nuke-mail-headers)
615   (define-key sc-T-keymap "N" 'sc-S-mail-header-nuke-list)
616   (define-key sc-T-keymap "o" 'sc-T-electric-circular)
617   (define-key sc-T-keymap "p" 'sc-S-preferred-header-style)
618   (define-key sc-T-keymap "s" 'sc-T-nested-citation)
619   (define-key sc-T-keymap "u" 'sc-T-use-only-preferences)
620   (define-key sc-T-keymap "w" 'sc-T-fixup-whitespace)
621   (define-key sc-T-keymap "?" 'sc-T-describe)
622   )
623
624 (defvar sc-mode-map ()
625   "Keymap for Supercite quasi-mode.")
626 (if sc-mode-map
627     ()
628   (setq sc-mode-map (make-sparse-keymap))
629   (define-key sc-mode-map "c"    'sc-cite-region)
630   (define-key sc-mode-map "f"    'sc-mail-field-query)
631   (define-key sc-mode-map "g"    'sc-mail-process-headers)
632   (define-key sc-mode-map "h"    'sc-describe)
633   (define-key sc-mode-map "i"    'sc-insert-citation)
634   (define-key sc-mode-map "o"    'sc-open-line)
635   (define-key sc-mode-map "r"    'sc-recite-region)
636   (define-key sc-mode-map "\C-p" 'sc-raw-mode-toggle)
637   (define-key sc-mode-map "u"    'sc-uncite-region)
638   (define-key sc-mode-map "v"    'sc-version)
639   (define-key sc-mode-map "w"    'sc-insert-reference)
640   (define-key sc-mode-map "\C-t"  sc-T-keymap)
641   (define-key sc-mode-map "\C-b" 'sc-submit-bug-report)
642   (define-key sc-mode-map "?"    'sc-describe)
643   )
644
645 (defvar sc-electric-mode-map ()
646   "Keymap for `sc-electric-mode' electric references mode.")
647 (if sc-electric-mode-map
648     nil
649   (setq sc-electric-mode-map (make-sparse-keymap))
650   (define-key sc-electric-mode-map "p"    'sc-eref-prev)
651   (define-key sc-electric-mode-map "n"    'sc-eref-next)
652   (define-key sc-electric-mode-map "s"    'sc-eref-setn)
653   (define-key sc-electric-mode-map "j"    'sc-eref-jump)
654   (define-key sc-electric-mode-map "x"    'sc-eref-abort)
655   (define-key sc-electric-mode-map "q"    'sc-eref-abort)
656   (define-key sc-electric-mode-map "\r"   'sc-eref-exit)
657   (define-key sc-electric-mode-map "\n"   'sc-eref-exit)
658   (define-key sc-electric-mode-map "g"    'sc-eref-goto)
659   (define-key sc-electric-mode-map "?"    'describe-mode)
660   (define-key sc-electric-mode-map "\C-h" 'describe-mode)
661   )
662
663 (defvar sc-minibuffer-local-completion-map nil
664   "Keymap for minibuffer confirmation of attribution strings.")
665 (if sc-minibuffer-local-completion-map
666     ()
667   (setq sc-minibuffer-local-completion-map
668         (copy-keymap minibuffer-local-completion-map))
669   (define-key sc-minibuffer-local-completion-map "\C-t" 'sc-toggle-fn)
670   (define-key sc-minibuffer-local-completion-map " "    'self-insert-command))
671
672 (defvar sc-minibuffer-local-map nil
673   "Keymap for minibuffer confirmation of attribution strings.")
674 (if sc-minibuffer-local-map
675     ()
676   (setq sc-minibuffer-local-map (copy-keymap minibuffer-local-map))
677   (define-key sc-minibuffer-local-map "\C-t" 'sc-toggle-fn))
678
679 \f
680 ;; ======================================================================
681 ;; utility functions
682
683 (defun sc-completing-read (prompt table &optional predicate require-match
684                                   initial-contents history)
685   "Compatibility between Emacs 18 and 19 `completing-read'.
686 In version 18, the HISTORY argument is ignored."
687   (if (memq 'v19 sc-emacs-features)
688       (funcall 'completing-read prompt table predicate require-match
689                initial-contents history)
690     (funcall 'completing-read prompt table predicate require-match
691              (or (car-safe initial-contents)
692                  initial-contents))))
693
694 (defun sc-read-string (prompt &optional initial-contents history)
695   "Compatibility between Emacs 18 and 19 `read-string'.
696 In version 18, the HISTORY argument is ignored."
697   (if (memq 'v19 sc-emacs-features)
698       (funcall 'read-string prompt initial-contents history)
699     (funcall 'read-string prompt initial-contents)))
700
701 (defun sc-submatch (matchnum &optional string)
702   "Returns `match-beginning' and `match-end' sub-expression for MATCHNUM.
703 If optional STRING is provided, take sub-expression using `substring'
704 of argument, otherwise use `buffer-substring' on current buffer.  Note
705 that `match-data' must have already been generated and no error
706 checking is performed by this function."
707   (if string
708       (substring string (match-beginning matchnum) (match-end matchnum))
709     (buffer-substring (match-beginning matchnum) (match-end matchnum))))
710
711 (defun sc-member (elt list)
712   "Like `memq', but uses `equal' instead of `eq'.
713 Emacs19 has a builtin function `member' which does exactly this."
714   (catch 'elt-is-member
715     (while list
716       (if (equal elt (car list))
717           (throw 'elt-is-member list))
718       (setq list (cdr list)))))
719 (and (memq 'v19 sc-emacs-features)
720      (fset 'sc-member 'member))
721
722 (defun sc-ask (alist)
723   "Ask a question in the minibuffer requiring a single character answer.
724 This function is kind of an extension of `y-or-n-p' where a single
725 letter is used to answer a question.  Question is formed from ALIST
726 which has members of the form:  (WORD . LETTER).  WORD is the long
727 word form, while LETTER is the letter for selecting that answer.  The
728 selected letter is returned, or nil if the question was not answered.
729 Note that WORD is a string and LETTER is a character.  All LETTERs in
730 the list should be unique."
731   (let* ((prompt (concat
732                   (mapconcat (function (lambda (elt) (car elt))) alist ", ")
733                   "? ("
734                   (mapconcat
735                    (function
736                     (lambda (elt) (char-to-string (cdr elt)))) alist "/")
737                   ") "))
738          (p prompt)
739          (event
740           (if (memq 'Lucid sc-emacs-features)
741               (allocate-event)
742             nil)))
743     (while (stringp p)
744       (if (let ((cursor-in-echo-area t)
745                 (inhibit-quit t))
746             (message "%s" p)
747             ;; lets be good neighbors and be compatible with all emacsen
748             (cond
749              ((memq 'v18 sc-emacs-features)
750               (setq event (read-char)))
751              ((memq 'Lucid sc-emacs-features)
752               (next-command-event event))
753              (t                         ; must be FSF19
754               (setq event (read-event))))
755             (prog1 quit-flag (setq quit-flag nil)))
756           (progn
757             (message "%s%s" p (single-key-description event))
758             (and (memq 'Lucid sc-emacs-features)
759                  (deallocate-event event))
760             (setq quit-flag nil)
761             (signal 'quit '())))
762       (let ((char
763              (if (memq 'Lucid sc-emacs-features)
764                  (let* ((key (and (key-press-event-p event) (event-key event)))
765                         (char (and key (event-to-character event))))
766                    char)
767                event))
768             elt)
769         (if char (setq char (downcase char)))
770         (cond
771          ((setq elt (rassq char alist))
772           (message "%s%s" p (car elt))
773           (setq p (cdr elt)))
774          ((and (memq 'Lucid sc-emacs-features)
775                (button-release-event-p event)) ; ignore them
776           nil)
777          (t
778           (message "%s%s" p (single-key-description event))
779           (if (memq 'Lucid sc-emacs-features)
780               (ding nil 'y-or-n-p)
781             (ding))
782           (discard-input)
783           (if (eq p prompt)
784               (setq p (concat "Try again.  " prompt)))))))
785     (and (memq 'Lucid sc-emacs-features)
786          (deallocate-event event))
787     p))
788
789 (defun sc-scan-info-alist (alist)
790   "Find a match in the info alist that matches a regexp in ALIST."
791   (let ((sc-mumble "")
792         rtnvalue)
793     (while alist
794       (let* ((elem    (car alist))
795              (infokey (car elem))
796              (infoval (sc-mail-field infokey))
797              (mlist   (car (cdr elem))))
798         (while mlist
799           (let* ((ml-elem (car mlist))
800                  (regexp  (car ml-elem))
801                  (thing   (cdr ml-elem)))
802             (if (string-match regexp infoval)
803                 ;; we found a match, time to return
804                 (setq rtnvalue thing
805                       mlist nil
806                       alist nil)
807               ;; else we didn't find a match
808               (setq mlist (cdr mlist))
809               )))                       ;end of mlist loop
810         (setq alist (cdr alist))
811         ))                              ;end of alist loop
812     rtnvalue))
813
814 \f
815 ;; ======================================================================
816 ;; extract mail field information from headers in reply buffer
817
818 ;; holder variables for bc happiness
819 (defvar sc-mail-headers-start nil
820   "Start of header fields.")
821 (defvar sc-mail-headers-end nil
822   "End of header fields.")
823 (defvar sc-mail-field-history nil
824   "For minibuffer completion on mail field queries.")
825 (defvar sc-mail-field-modification-history nil
826   "For minibuffer completion on mail field modifications.")
827 (defvar sc-mail-glom-frame
828   '((begin                        (setq sc-mail-headers-start (point)))
829     ("^x-attribution:[ \t]+.*$"   (sc-mail-fetch-field t) nil t)
830     ("^\\S +:.*$"                 (sc-mail-fetch-field) nil t)
831     ("^$"                         (list 'abort '(step . 0)))
832     ("^[ \t]+"                    (sc-mail-append-field))
833     (sc-mail-warn-if-non-rfc822-p (sc-mail-error-in-mail-field))
834     (end                          (setq sc-mail-headers-end (point))))
835   "Regi frame for glomming mail header information.")
836
837 ;; regi functions
838 (defun sc-mail-fetch-field (&optional attribs-p)
839   "Insert a key and value into `sc-mail-info' alist.
840 If optional ATTRIBS-P is non-nil, the key/value pair is placed in
841 `sc-attributions' too."
842   (if (string-match "^\\(\\S *\\)\\s *:\\s +\\(.*\\)$" curline)
843       (let* ((key (downcase (sc-submatch 1 curline)))
844              (val (sc-submatch 2 curline))
845              (keyval (cons key val)))
846         (setq sc-mail-info (cons keyval sc-mail-info))
847         (if attribs-p
848             (setq sc-attributions (cons keyval sc-attributions)))
849         ))
850   nil)
851
852 (defun sc-mail-append-field ()
853   "Append a continuation line onto the last fetched mail field's info."
854   (let ((keyval (car sc-mail-info)))
855     (if (and keyval (string-match "^\\s *\\(.*\\)$" curline))
856         (setcdr keyval (concat (cdr keyval) " " (sc-submatch 1 curline)))))
857   nil)
858
859 (defun sc-mail-error-in-mail-field ()
860   "Issue warning that mail headers don't conform to RFC 822."
861   (let* ((len (min (length curline) 10))
862          (ellipsis (if (< len (length curline)) "..." ""))
863          (msg "Mail header \"%s%s\" doesn't conform to RFC 822. skipping..."))
864     (message msg (substring curline 0 len) ellipsis))
865   (beep)
866   (sit-for 2)
867   nil)
868
869 ;; mail header nuking
870 (defvar sc-mail-last-header-nuked-p nil
871   "True if the last header was nuked.")
872
873 (defun sc-mail-nuke-line ()
874   "Nuke the current mail header line."
875   (delete-region (regi-pos 'bol) (regi-pos 'bonl))
876   '((step . -1)))
877
878 (defun sc-mail-nuke-header-line ()
879   "Delete current-line and set up for possible continuation."
880   (setq sc-mail-last-header-nuked-p t)
881   (sc-mail-nuke-line))
882
883 (defun sc-mail-nuke-continuation-line ()
884   "Delete a continuation line if the last header line was deleted."
885   (if sc-mail-last-header-nuked-p
886       (sc-mail-nuke-line)))
887
888 (defun sc-mail-cleanup-blank-lines ()
889   "Leave some blank lines after original mail headers are nuked.
890 The number of lines left is specified by `sc-blank-lines-after-headers'."
891   (if sc-blank-lines-after-headers
892       (save-restriction
893         (widen)
894         (skip-chars-backward " \t\n")
895         (forward-line 1)
896         (delete-blank-lines)
897         (beginning-of-line)
898         (if (looking-at "[ \t]*$")
899             (delete-region (regi-pos 'bol) (regi-pos 'bonl)))
900         (insert-char ?\n sc-blank-lines-after-headers)))
901   nil)
902
903 (defun sc-mail-build-nuke-frame ()
904   "Build the regiframe for nuking mail headers."
905   (let (every-func entry-func nonentry-func)
906     (cond
907      ((eq sc-nuke-mail-headers 'all)
908       (setq every-func '(progn (forward-line -1) (sc-mail-nuke-line))))
909      ((eq sc-nuke-mail-headers 'specified)
910       (setq entry-func    '(sc-mail-nuke-header-line)
911             nonentry-func '(setq sc-mail-last-header-nuked-p nil)))
912      ((eq sc-nuke-mail-headers 'keep)
913       (setq entry-func    '(setq sc-mail-last-header-nuked-p nil)
914             nonentry-func '(sc-mail-nuke-header-line)))
915      ;; we never get far enough to interpret a frame if s-n-m-h == 'none
916      ((eq sc-nuke-mail-headers 'none))
917      (t (error "Illegal value for sc-nuke-mail-headers: %s"
918                sc-nuke-mail-headers))
919      )                                  ; end-cond
920     (append
921      (and entry-func
922           (regi-mapcar sc-nuke-mail-header-list entry-func nil t))
923      (and nonentry-func (list (list "^\\S +:.*$" nonentry-func)))
924      (and (not every-func)
925           '(("^[ \t]+" (sc-mail-nuke-continuation-line))))
926      '((begin     (setq sc-mail-last-header-zapped-p nil)))
927      '((end       (sc-mail-cleanup-blank-lines)))
928      (and every-func (list (list 'every every-func)))
929      )))
930
931 ;; mail processing and zapping. this is the top level entry defun to
932 ;; all header processing.
933 (defun sc-mail-process-headers (start end)
934   "Process original mail message's mail headers.
935 After processing, mail headers may be nuked.  Header information is
936 stored in `sc-mail-info', and any old information is lost unless an
937 error occurs."
938   (interactive "r")
939   ;; Region may contain a whole message, so we must limit the rewrite-region
940   ;; function to just the headers, delimited by a blank line.
941   (if (fboundp sc-rewrite-region-function)
942       (funcall sc-rewrite-region-function start
943                (save-excursion
944                  (if (search-forward "\n\n" nil t)
945                      (point)
946                    end))))
947   (let ((info (copy-alist sc-mail-info))
948         (attribs (copy-alist sc-attributions)))
949     (setq sc-mail-info nil
950           sc-attributions nil)
951     (regi-interpret sc-mail-glom-frame start end)
952     (if (null sc-mail-info)
953         (progn
954           (message "No mail headers found! Restoring old information.")
955           (setq sc-mail-info info
956                 sc-attributions attribs))
957       (regi-interpret (sc-mail-build-nuke-frame)
958                       sc-mail-headers-start sc-mail-headers-end)
959       )))
960
961 \f
962 ;; let the user change mail field information
963 (defun sc-mail-field (field)
964   "Return the mail header field value associated with FIELD.
965 If there was no mail header with FIELD as its key, return the value of
966 `sc-mumble'.  FIELD is case insensitive."
967   (or (cdr (assoc (downcase field) sc-mail-info)) sc-mumble))
968
969 (defun sc-mail-field-query (arg)
970   "View the value of a mail field.
971 With `\\[universal-argument]', prompts for action on mail field.
972 Action can be one of: View, Modify, Add, or Delete."
973   (interactive "P")
974   (let* ((alist '(("view" . ?v) ("modify" . ?m) ("add" . ?a) ("delete" . ?d)))
975          (action (if (not arg) ?v (sc-ask alist)))
976          key)
977     (if (not action)
978         ()
979       (setq key (sc-completing-read
980                  (concat (car (rassq action alist))
981                               " information key: ")
982                  sc-mail-info nil
983                  (if (eq action ?a) nil 'noexit)
984                  nil 'sc-mail-field-history))
985       (cond
986        ((eq action ?v)
987         (message "%s: %s" key (cdr (assoc key sc-mail-info))))
988        ((eq action ?d)
989         (setq sc-mail-info (delq (assoc key sc-mail-info) sc-mail-info)))
990        ((eq action ?m)
991         (let ((keyval (assoc key sc-mail-info)))
992           ;; first put initial value onto list if not already there
993           (if (not (sc-member (cdr keyval)
994                               sc-mail-field-modification-history))
995               (setq sc-mail-field-modification-history
996                     (cons (cdr keyval) sc-mail-field-modification-history)))
997           (setcdr keyval (sc-read-string
998                           (concat key ": ") (cdr keyval)
999                           'sc-mail-field-modification-history))))
1000        ((eq action ?a)
1001         (setq sc-mail-info
1002               (cons (cons key
1003                           (sc-read-string (concat key ": "))) sc-mail-info)))
1004        ))))
1005
1006 \f
1007 ;; ======================================================================
1008 ;; attributions
1009
1010 (defvar sc-attribution-confirmation-history nil
1011   "History for confirmation of attribution strings.")
1012 (defvar sc-citation-confirmation-history nil
1013   "History for confirmation of attribution prefixes.")
1014
1015 (defun sc-attribs-%@-addresses (from &optional delim)
1016   "Extract the author's email terminus from email address FROM.
1017 Match addresses of the style ``name%[stuff].'' when called with DELIM
1018 of \"%\" and addresses of the style ``[stuff]name@[stuff]'' when
1019 called with DELIM \"@\".  If DELIM is nil or not provided, matches
1020 addresses of the style ``name''."
1021   ;; Handle X.400 addresses where G & S fields contain the sender name.
1022   (if (string-match (concat "/[GS]=\\([-a-zA-Z0-9_.]+\\)\\|\\([-+a-zA-Z0-9_.]+\\)" delim) from)
1023       (if (match-beginning 2)
1024           (substring from (match-beginning 2) (match-end 2))
1025         (substring from (match-beginning 1) (match-end 1)))))
1026
1027 (defun sc-attribs-!-addresses (from)
1028   "Extract the author's email terminus from email address FROM.
1029 Match addresses of the style ``[stuff]![stuff]...!name[stuff].''"
1030   (let ((eos (length from))
1031         (mstart (string-match "![-a-zA-Z0-9_.]+\\([^-!a-zA-Z0-9_.]\\|$\\)"
1032                               from 0))
1033         (mend (match-end 0)))
1034     (and mstart
1035          (substring from (1+ mstart) (- mend (if (= mend eos) 0 1)))
1036          )))
1037
1038 (defun sc-attribs-<>-addresses (from)
1039   "Extract the author's email terminus from email address FROM.
1040 Match addresses of the style ``<name[stuff]>.''"
1041   (and (string-match "<\\(.*\\)>" from)
1042        (sc-submatch 1 from)))
1043
1044 (defun sc-get-address (from author)
1045   "Get the full email address path from FROM.
1046 AUTHOR is the author's name (which is removed from the address)."
1047   (when (fboundp sc-rewrite-address-function)
1048     (setq from (funcall sc-rewrite-address-function from)))
1049   (let ((eos (length from)))
1050     (if (string-match (concat "\\(^\\|^\"\\)" author
1051                               "\\(\\s +\\|\"\\s +\\)") from 0)
1052         (let ((address (substring from (match-end 0) eos)))
1053           (if (and (= (aref address 0) ?<)
1054                    (= (aref address (1- (length address))) ?>))
1055               (substring address 1 (1- (length address)))
1056             address))
1057       (cond ((string-match "/[GS]=\\([-a-zA-Z0-9._]+\\)[^!@%]+\\([-a-zA-Z0-9!@%._]+\\)" from)
1058              ;; Motorola X.400 non-prettified address
1059              (concat (sc-submatch 1 from) (sc-submatch 2 from)))
1060             ((string-match "[-+a-zA-Z0-9!@%._]+" from)
1061              (sc-submatch 0 from))
1062             (t "")))))
1063
1064 (defun sc-attribs-emailname (from)
1065   "Get the email terminus name from FROM."
1066   (or
1067    (sc-attribs-%@-addresses from "%")
1068    (sc-attribs-%@-addresses from "@")
1069    (sc-attribs-!-addresses  from)
1070    (sc-attribs-<>-addresses from)
1071    (sc-attribs-%@-addresses from)
1072    (substring from 0 10)))
1073
1074 (defun sc-name-substring (string start end extend)
1075   "Extract the specified substring of STRING from START to END.
1076 EXTEND is the number of characters on each side to extend the
1077 substring."
1078   (and start
1079        (let ((sos (+ start extend))
1080              (eos (- end extend)))
1081          (substring string sos
1082                     (or (string-match sc-titlecue-regexp string sos) eos)
1083                     ))))
1084
1085 (require 'mail-extr)
1086
1087 (defun sc-attribs-extract-namestring (from)
1088   "Extract the name string from FROM.
1089 This should be the author's full name minus an optional title."
1090   (let ((from-parts (funcall sc-extract-address-components from)))
1091     (or (car from-parts)
1092         (and (cadr from-parts)
1093              (sc-attribs-emailname (cadr from-parts)))
1094         (error "cannot extract address components from %s" from))))
1095
1096 (defun sc-attribs-chop-namestring (namestring)
1097   "Convert NAMESTRING to a list of names.
1098 example: (sc-attribs-chop-namestring \"John Xavier Doe\")
1099          => (\"John\" \"Xavier\" \"Doe\")"
1100   ;; Handle non-prettified Motorola X.400 addresses.
1101   (if (or (string-match "^\\([ \t]*\\)\\([^0-9 \t._-]+\\)\\(-[A-Za-z]+[0-9]+[A-Za-z0-9_]*[ \t]*\\)" namestring)
1102           (string-match
1103            "\\([ \t]*\\)\\([^ \t._]+\\)\\([ \t]*\\)"
1104            namestring))
1105       (cons (sc-submatch 2 namestring)
1106             (sc-attribs-chop-namestring (substring namestring (match-end 3)))
1107             )))
1108
1109 (defun sc-attribs-strip-initials (namelist)
1110   "Extract the author's initials from the NAMELIST."
1111   (mapconcat
1112    (function
1113     (lambda (name)
1114       (if (< 0 (length name))
1115           (substring name 0 1))))
1116    namelist ""))
1117
1118 (defun sc-guess-attribution (&optional string)
1119   "Guess attribution string on current line.
1120 If attribution cannot be guessed, nil is returned.  Optional STRING if
1121 supplied, is used instead of the line point is on in the current buffer."
1122   (let ((start 0)
1123         (string (or string (buffer-substring (regi-pos 'bol) (regi-pos 'eol))))
1124         attribution)
1125     (and
1126      (= start (or (string-match sc-citation-leader-regexp string start) -1))
1127      (setq start (match-end 0))
1128      (= start (or (string-match sc-citation-root-regexp string start) 1))
1129      (setq attribution (sc-submatch 0 string)
1130            start (match-end 0))
1131      (= start (or (string-match sc-citation-delimiter-regexp string start) -1))
1132      (setq start (match-end 0))
1133      (= start (or (string-match sc-citation-separator-regexp string start) -1))
1134      attribution)))
1135
1136 (defun sc-attribs-filter-namelist (namelist)
1137   "Filter out noise in NAMELIST according to `sc-name-filter-alist'."
1138   (let ((elements (length namelist))
1139         (position -1)
1140         keepers filtered-list)
1141     (mapcar
1142      (function
1143       (lambda (name)
1144         (setq position (1+ position))
1145         (let ((keep-p t))
1146           (mapcar
1147            (function
1148             (lambda (filter)
1149               (let ((regexp (car filter))
1150                     (pos (cdr filter)))
1151                 (if (and (string-match regexp name)
1152                          (or (and (numberp pos)
1153                                   (= pos position))
1154                              (and (eq pos 'last)
1155                                   (= position (1- elements)))
1156                              (eq pos 'any)))
1157                     (setq keep-p nil))
1158                 )))
1159            sc-name-filter-alist)
1160           (if keep-p
1161               (setq keepers (cons position keepers)))
1162           )))
1163      namelist)
1164     (mapcar
1165      (function
1166       (lambda (position)
1167         (setq filtered-list (cons (nth position namelist) filtered-list))
1168         ))
1169      keepers)
1170     filtered-list))
1171
1172 (defun sc-attribs-chop-address (from)
1173   "Extract attribution information from FROM.
1174 This populates the `sc-attributions' with the list of possible attributions."
1175   (if (and (stringp from)
1176            (< 0 (length from)))
1177       (let* ((sc-mumble "")
1178              (namestring (sc-attribs-extract-namestring from))
1179              (namelist   (sc-attribs-filter-namelist
1180                           (sc-attribs-chop-namestring namestring)))
1181              (revnames   (reverse (cdr namelist)))
1182              (firstname  (car namelist))
1183              (midnames   (reverse (cdr revnames)))
1184              (lastname   (car revnames))
1185              (initials   (sc-attribs-strip-initials namelist))
1186              (emailname  (sc-attribs-emailname from))
1187              (n 1)
1188              author middlenames)
1189
1190         ;; put basic information
1191         (setq
1192          ;; put middle names and build sc-author entry
1193          middlenames (mapconcat
1194                       (function
1195                        (lambda (midname)
1196                          (let ((key-attribs (format "middlename-%d" n))
1197                                (key-mail    (format "sc-middlename-%d" n)))
1198                            (setq
1199                             sc-attributions (cons (cons key-attribs midname)
1200                                                   sc-attributions)
1201                             sc-mail-info (cons (cons key-mail midname)
1202                                                sc-mail-info)
1203                             n (1+ n))
1204                            midname)))
1205                       midnames " ")
1206
1207          author (concat firstname " " middlenames (and midnames " ") lastname)
1208
1209          sc-attributions (append
1210                           (list
1211                            (cons "firstname"   firstname)
1212                            (cons "lastname"    lastname)
1213                            (cons "emailname"   emailname)
1214                            (cons "initials"    initials))
1215                           sc-attributions)
1216          sc-mail-info (append
1217                        (list
1218                         (cons "sc-firstname"   firstname)
1219                         (cons "sc-middlenames" middlenames)
1220                         (cons "sc-lastname"    lastname)
1221                         (cons "sc-emailname"   emailname)
1222                         (cons "sc-initials"    initials)
1223                         (cons "sc-author"      author)
1224                         (cons "sc-from-address" (sc-get-address
1225                                                  (sc-mail-field "from")
1226                                                  namestring))
1227                         (cons "sc-reply-address" (sc-get-address
1228                                                   (sc-mail-field "reply-to")
1229                                                   namestring))
1230                         (cons "sc-sender-address" (sc-get-address
1231                                                    (sc-mail-field "sender")
1232                                                    namestring))
1233                         )
1234                        sc-mail-info)
1235          ))
1236     ;; from string is empty
1237     (setq sc-mail-info (cons (cons "sc-author" sc-default-author-name)
1238                              sc-mail-info))))
1239
1240 (defvar sc-attrib-or-cite nil
1241   "Used to toggle between attribution input or citation input.")
1242
1243 (defun sc-toggle-fn ()
1244   "Toggle between attribution selection and citation selection.
1245 Only used during confirmation."
1246   (interactive)
1247   (setq sc-attrib-or-cite (not sc-attrib-or-cite))
1248   (throw 'sc-reconfirm t))
1249
1250 (defun sc-select-attribution ()
1251   "Select an attribution from `sc-attributions'.
1252
1253 Variables involved in selection process include:
1254      `sc-preferred-attribution-list'
1255      `sc-use-only-preference-p'
1256      `sc-confirm-always-p'
1257      `sc-default-attribution'
1258      `sc-attrib-selection-list'.
1259
1260 Runs the hook `sc-attribs-preselect-hook' before selecting an
1261 attribution and the hook `sc-attribs-postselect-hook' after making the
1262 selection but before querying is performed.  During
1263 `sc-attribs-postselect-hook' the variable `citation' is bound to the
1264 auto-selected citation string and the variable `attribution' is bound
1265 to the auto-selected attribution string."
1266   (run-hooks 'sc-attribs-preselect-hook)
1267   (let ((query-p sc-confirm-always-p)
1268         attribution citation
1269         (attriblist sc-preferred-attribution-list))
1270
1271     ;; first cruise through sc-preferred-attribution-list looking for
1272     ;; a match in either sc-attributions or sc-mail-info.  if the
1273     ;; element is "sc-consult", then we have to do the alist
1274     ;; consultation phase
1275     (while attriblist
1276       (let* ((preferred (car attriblist)))
1277         (cond
1278          ((string= preferred "sc-consult")
1279           ;; we've been told to consult the attribution vs. mail
1280           ;; header key alist.  we do this until we find a match in
1281           ;; the sc-attrib-selection-list.  if we do not find a match,
1282           ;; we continue scanning attriblist
1283           (let ((attrib (sc-scan-info-alist sc-attrib-selection-list)))
1284             (cond
1285              ((not attrib)
1286               (setq attriblist (cdr attriblist)))
1287              ((stringp attrib)
1288               (setq attribution attrib
1289                     attriblist nil))
1290              ((listp attrib)
1291               (setq attribution (eval attrib)
1292                     attriblist nil))
1293              (t (error "%s did not evaluate to a string or list!"
1294                        "sc-attrib-selection-list"))
1295              )))
1296          ((setq attribution (cdr (assoc preferred sc-attributions)))
1297           (setq attriblist nil))
1298          (t
1299           (setq attriblist (cdr attriblist)))
1300          )))
1301
1302     ;; if preference was not found, we may use a secondary method to
1303     ;; find a valid attribution
1304     (if (and (not attribution)
1305              (not sc-use-only-preference-p))
1306         ;; secondary method tries to find a preference in this order
1307         ;; 1. sc-lastchoice
1308         ;; 2. x-attribution
1309         ;; 3. firstname
1310         ;; 4. lastname
1311         ;; 5. initials
1312         ;; 6. first non-empty attribution in alist
1313         (setq attribution
1314               (or (cdr (assoc "sc-lastchoice" sc-attributions))
1315                   (cdr (assoc "x-attribution" sc-attributions))
1316                   (cdr (assoc "firstname" sc-attributions))
1317                   (cdr (assoc "lastname" sc-attributions))
1318                   (cdr (assoc "initials" sc-attributions))
1319                   (cdr (car sc-attributions)))))
1320
1321     ;; still couldn't find an attribution. we're now limited to using
1322     ;; the default attribution, but we'll force a query when this happens
1323     (if (not attribution)
1324         (setq attribution sc-default-attribution
1325               query-p t))
1326
1327     ;; create the attribution prefix
1328     (setq citation (sc-make-citation attribution))
1329
1330     ;; run the post selection hook before querying the user
1331     (run-hooks 'sc-attribs-postselect-hook)
1332
1333     ;; query for confirmation
1334     (if query-p
1335         (let* ((query-alist (mapcar (function (lambda (entry)
1336                                                 (list (cdr entry))))
1337                                     sc-attributions))
1338                (minibuffer-local-completion-map
1339                 sc-minibuffer-local-completion-map)
1340                (minibuffer-local-map sc-minibuffer-local-map)
1341                (initial attribution)
1342                (completer-disable t)    ; in case completer.el is used
1343                choice)
1344           (setq sc-attrib-or-cite nil)  ; nil==attribution, t==citation
1345           (while
1346               (catch 'sc-reconfirm
1347                 (string= "" (setq choice
1348                                   (if sc-attrib-or-cite
1349                                       (sc-read-string
1350                                        "Enter citation prefix: "
1351                                        citation
1352                                        'sc-citation-confirmation-history)
1353                                     (sc-completing-read
1354                                      "Complete attribution name: "
1355                                      query-alist nil nil
1356                                      (cons initial 0)
1357                                      'sc-attribution-confirmation-history)
1358                                     )))))
1359           (if sc-attrib-or-cite
1360               ;; since the citation was chosen, we have to guess at
1361               ;; the attribution
1362               (setq citation choice
1363                     attribution (or (sc-guess-attribution citation)
1364                                     citation))
1365
1366             (setq citation (sc-make-citation choice)
1367                   attribution choice))
1368           ))
1369
1370     ;; its possible that the user wants to downcase the citation and
1371     ;; attribution
1372     (if sc-downcase-p
1373         (setq citation (downcase citation)
1374               attribution (downcase attribution)))
1375
1376     ;; set up mail info alist
1377     (let* ((ckey "sc-citation")
1378            (akey "sc-attribution")
1379            (ckeyval (assoc ckey sc-mail-info))
1380            (akeyval (assoc akey sc-mail-info)))
1381       (if ckeyval
1382           (setcdr ckeyval citation)
1383         (setq sc-mail-info
1384               (append (list (cons ckey citation)) sc-mail-info)))
1385       (if akeyval
1386           (setcdr akeyval attribution)
1387         (setq sc-mail-info
1388               (append (list (cons akey attribution)) sc-mail-info))))
1389
1390     ;; set the sc-lastchoice attribution
1391     (let* ((lkey "sc-lastchoice")
1392            (lastchoice (assoc lkey sc-attributions)))
1393       (if lastchoice
1394           (setcdr lastchoice attribution)
1395         (setq sc-attributions
1396               (cons (cons lkey attribution) sc-attributions))))
1397     ))
1398
1399 \f
1400 ;; ======================================================================
1401 ;; filladapt hooks for supercite 3.1. you shouldn't need anything
1402 ;; extra to make gin-mode understand supercited lines.  Even this
1403 ;; stuff might not be entirely necessary...
1404
1405 (defun sc-cite-regexp (&optional root-regexp)
1406   "Return a regexp describing a Supercited line.
1407 The regexp is the concatenation of `sc-citation-leader-regexp',
1408 `sc-citation-root-regexp', `sc-citation-delimiter-regexp', and
1409 `sc-citation-separator-regexp'.  If optional ROOT-REGEXP is supplied,
1410 use it instead of `sc-citation-root-regexp'."
1411   (concat sc-citation-leader-regexp
1412           (or root-regexp sc-citation-root-regexp)
1413           sc-citation-delimiter-regexp
1414           sc-citation-separator-regexp))
1415
1416 (defun sc-make-citation (attribution)
1417   "Make a non-nested citation from ATTRIBUTION."
1418   (concat sc-citation-leader
1419           attribution
1420           sc-citation-delimiter
1421           sc-citation-separator))
1422
1423 (defun sc-setup-filladapt ()
1424   "Setup `filladapt-prefix-table' to handle Supercited paragraphs."
1425   (if (boundp 'filladapt-prefix-table)
1426       (let* ((fa-sc-elt 'filladapt-supercite-included-text)
1427              (elt (rassq fa-sc-elt filladapt-prefix-table)))
1428         (if elt (setcar elt (sc-cite-regexp))
1429           (message "Filladapt doesn't seem to know about Supercite.")
1430           (beep)))))
1431
1432 \f
1433 ;; ======================================================================
1434 ;; citing and unciting regions of text
1435
1436 (defvar sc-fill-begin 1
1437   "Buffer position to begin filling.")
1438 (defvar sc-fill-line-prefix ""
1439   "Fill prefix of previous line")
1440
1441 ;; filling
1442 (defun sc-fill-if-different (&optional prefix)
1443   "Fill the region bounded by `sc-fill-begin' and point.
1444 Only fill if optional PREFIX is different than `sc-fill-line-prefix'.
1445 If `sc-auto-fill-region-p' is nil, do not fill region.  If PREFIX is
1446 not supplied, initialize fill variables.  This is useful for a regi
1447 `begin' frame-entry."
1448   (if (not prefix)
1449       (setq sc-fill-line-prefix ""
1450             sc-fill-begin (regi-pos 'bol))
1451     (if (and sc-auto-fill-region-p
1452              (not (string= prefix sc-fill-line-prefix)))
1453         (let ((fill-prefix sc-fill-line-prefix))
1454           (if (not (string= fill-prefix ""))
1455               (fill-region sc-fill-begin (regi-pos 'bol)))
1456           (setq sc-fill-line-prefix prefix
1457                 sc-fill-begin (regi-pos 'bol))))
1458     )
1459   nil)
1460
1461 (defun sc-cite-coerce-cited-line ()
1462   "Coerce a Supercited line to look like our style."
1463   (let* ((attribution (sc-guess-attribution))
1464          (regexp (sc-cite-regexp attribution))
1465          (prefix (sc-make-citation attribution)))
1466     (if (and attribution
1467              (looking-at regexp))
1468         (progn
1469           (delete-region
1470            (match-beginning 0)
1471            (save-excursion
1472              (goto-char (match-end 0))
1473              (if (bolp) (forward-char -1))
1474              (point)))
1475           (insert prefix)
1476           (sc-fill-if-different prefix)))
1477     nil))
1478
1479 (defun sc-cite-coerce-dumb-citer ()
1480   "Coerce a non-nested citation that's been cited with a dumb nesting citer."
1481   (delete-region (match-beginning 1) (match-end 1))
1482   (beginning-of-line)
1483   (sc-cite-coerce-cited-line))
1484
1485 (defun sc-guess-nesting (&optional string)
1486   "Guess the citation nesting on the current line.
1487 If nesting cannot be guessed, nil is returned.  Optional STRING if
1488 supplied, is used instead of the line point is on in the current
1489 buffer."
1490   (let ((start 0)
1491         (string (or string (buffer-substring (regi-pos 'bol) (regi-pos 'eol))))
1492         nesting)
1493     (and
1494      (= start (or (string-match sc-citation-leader-regexp string start) -1))
1495      (setq start (match-end 0))
1496      (= start (or (string-match sc-citation-delimiter-regexp string start) -1))
1497      (setq nesting (sc-submatch 0 string)
1498            start (match-end 0))
1499      (= start (or (string-match sc-citation-separator-regexp string start) -1))
1500      nesting)))
1501
1502 (defun sc-add-citation-level ()
1503   "Add a citation level for nested citation style w/ coersion."
1504   (let* ((nesting (sc-guess-nesting))
1505          (citation (make-string (1+ (length nesting))
1506                                 (string-to-char sc-citation-delimiter)))
1507          (prefix (concat sc-citation-leader citation sc-citation-separator)))
1508     (if (looking-at (sc-cite-regexp ""))
1509         (delete-region (match-beginning 0) (match-end 0)))
1510     (insert prefix)
1511     (sc-fill-if-different prefix)))
1512
1513 (defun sc-cite-line (&optional citation)
1514   "Cite a single line of uncited text.
1515 Optional CITATION overrides any citation automatically selected."
1516   (if sc-fixup-whitespace-p
1517       (fixup-whitespace))
1518   (let ((prefix (or citation
1519                     (cdr (assoc "sc-citation" sc-mail-info))
1520                     sc-default-attribution)))
1521     (insert prefix)
1522     (sc-fill-if-different prefix))
1523   nil)
1524
1525 (defun sc-uncite-line ()
1526   "Remove citation from current line."
1527   (let ((cited (looking-at (sc-cite-regexp))))
1528     (if cited
1529         (delete-region (match-beginning 0) (match-end 0))))
1530   nil)
1531
1532 (defun sc-recite-line (regexp)
1533   "Remove citation matching REGEXP from current line and recite line."
1534   (let ((cited (looking-at (concat "^" regexp)))
1535         (prefix (cdr (assoc "sc-citation" sc-mail-info))))
1536     (if cited
1537         (delete-region (match-beginning 0) (match-end 0)))
1538     (insert (or prefix sc-default-attribution))
1539     (sc-fill-if-different prefix))
1540   nil)
1541
1542 ;; interactive functions
1543 (defun sc-cite-region (start end &optional confirm-p)
1544   "Cite a region delineated by START and END.
1545 If optional CONFIRM-P is non-nil, the attribution is confirmed before
1546 its use in the citation string.  This function first runs
1547 `sc-pre-cite-hook'."
1548   (interactive "r\nP")
1549   (undo-boundary)
1550   (let ((frame (or (sc-scan-info-alist sc-cite-frame-alist)
1551                    sc-default-cite-frame))
1552         (sc-confirm-always-p (if confirm-p t sc-confirm-always-p)))
1553     (run-hooks 'sc-pre-cite-hook)
1554     (if (interactive-p)
1555         (sc-select-attribution))
1556     (regi-interpret frame start end)))
1557
1558 (defun sc-uncite-region (start end)
1559   "Uncite a region delineated by START and END.
1560 First runs `sc-pre-uncite-hook'."
1561   (interactive "r")
1562   (undo-boundary)
1563   (let ((frame (or (sc-scan-info-alist sc-uncite-frame-alist)
1564                    sc-default-uncite-frame)))
1565     (run-hooks 'sc-pre-uncite-hook)
1566     (regi-interpret frame start end)))
1567
1568 (defun sc-recite-region (start end)
1569   "Recite a region delineated by START and END.
1570 First runs `sc-pre-recite-hook'."
1571   (interactive "r")
1572   (let ((sc-confirm-always-p t))
1573     (sc-select-attribution))
1574   (undo-boundary)
1575   (let ((frame (or (sc-scan-info-alist sc-recite-frame-alist)
1576                    sc-default-recite-frame)))
1577     (run-hooks 'sc-pre-recite-hook)
1578     (regi-interpret frame start end)))
1579
1580 \f
1581 ;; ======================================================================
1582 ;; building headers
1583
1584 (defun sc-hdr (prefix field &optional sep return-nil-p)
1585   "Returns a concatenation of PREFIX and FIELD.
1586 If FIELD is not a string or is the empty string, the empty string will
1587 be returned.  Optional third argument SEP is concatenated on the end if
1588 it is a string.  Returns empty string, unless optional RETURN-NIL-P is
1589 non-nil."
1590   (if (and (stringp field)
1591            (not (string= field "")))
1592       (concat prefix field (or sep ""))
1593     (and (not return-nil-p) "")))
1594
1595 (defun sc-whofrom ()
1596   "Return the value of (sc-mail-field \"from\") or nil."
1597   (let ((sc-mumble nil))
1598     (sc-mail-field "from")))
1599
1600 (defun sc-no-header ()
1601   "Does nothing.  Use this instead of nil to get a blank header."
1602   ())
1603
1604 (defun sc-no-blank-line-or-header()
1605   "Similar to `sc-no-header' except it removes the preceding blank line."
1606   (if (not (bobp))
1607       (if (and (eolp)
1608                (progn (forward-line -1)
1609                       (or (looking-at mail-header-separator)
1610                           (and (eq major-mode 'mh-letter-mode)
1611                                (mh-in-header-p)))))
1612           (progn (forward-line)
1613                  (let ((kill-lines-magic t))
1614                    (kill-line))))))
1615
1616 (defun sc-header-on-said ()
1617   "\"On <date>, <from> said:\" unless:
1618 1. the \"from\" field cannot be found, in which case nothing is inserted;
1619 2. the \"date\" field is missing in which case only the from part is printed."
1620   (let ((sc-mumble "")
1621         (whofrom (sc-whofrom)))
1622     (if whofrom
1623         (insert sc-reference-tag-string
1624                 (sc-hdr "On " (sc-mail-field "date") ", ")
1625                 whofrom " said:\n"))))
1626
1627 (defun sc-header-inarticle-writes ()
1628   "\"In article <message-id>, <from> writes:\"
1629 Treats \"message-id\" and \"from\" fields similar to `sc-header-on-said'."
1630   (let ((sc-mumble "")
1631         (whofrom (sc-mail-field "from")))
1632     (if whofrom
1633         (insert sc-reference-tag-string
1634                 (sc-hdr "In article " (sc-mail-field "message-id") ", ")
1635                 whofrom " writes:\n"))))
1636
1637 (defun sc-header-regarding-adds ()
1638   "\"Regarding <subject>; <from> adds:\"
1639 Treats \"subject\" and \"from\" fields similar to `sc-header-on-said'."
1640   (let ((sc-mumble "")
1641         (whofrom (sc-whofrom)))
1642     (if whofrom
1643         (insert sc-reference-tag-string
1644                 (sc-hdr "Regarding " (sc-mail-field "subject") "; ")
1645                 whofrom " adds:\n"))))
1646
1647 (defun sc-header-attributed-writes ()
1648   "\"<sc-attribution>\" == <sc-author> <address> writes:
1649 Treats these fields in a similar manner to `sc-header-on-said'."
1650   (let ((sc-mumble "")
1651         (whofrom (sc-whofrom)))
1652     (if whofrom
1653         (insert sc-reference-tag-string
1654                 (sc-hdr "\"" (sc-mail-field "sc-attribution") "\" == ")
1655                 (sc-hdr ""   (sc-mail-field "sc-author") " ")
1656                 (or (sc-hdr "<" (sc-mail-field "sc-from-address") ">" t)
1657                     (sc-hdr "<" (sc-mail-field "sc-reply-address") ">"  t)
1658                     "")
1659                 " writes:\n"))))
1660
1661 (defun sc-header-author-writes ()
1662   "<sc-author> writes:"
1663   (let ((sc-mumble "")
1664         (whofrom (sc-whofrom)))
1665     (if whofrom
1666         (insert sc-reference-tag-string
1667                 (sc-hdr "" (sc-mail-field "sc-author"))
1668                 " writes:\n"))))
1669
1670 (defun sc-header-verbose ()
1671   "Very verbose, some say gross."
1672   (let ((sc-mumble "")
1673         (whofrom (sc-whofrom))
1674         (tag sc-reference-tag-string))
1675     (if whofrom
1676         (insert (sc-hdr (concat tag "On ") (sc-mail-field "date") ",\n")
1677                 (or (sc-hdr tag (sc-mail-field "sc-author") "\n" t)
1678                     (concat tag whofrom "\n"))
1679                 (sc-hdr (concat tag "from the organization of ")
1680                         (sc-mail-field "organization") "\n")
1681                 (let ((rtag (concat tag "who can be reached at: ")))
1682                   (or (sc-hdr rtag (sc-mail-field "sc-from-address") "\n" t)
1683                       (sc-hdr rtag (sc-mail-field "sc-reply-address")  "\n" t)
1684                       ""))
1685                 (sc-hdr
1686                  (concat tag "(whose comments are cited below with \"")
1687                  (sc-mail-field "sc-citation") "\"),\n")
1688                 (sc-hdr (concat tag "had this to say in article ")
1689                         (sc-mail-field "message-id") "\n")
1690                 (sc-hdr (concat tag "in newsgroups ")
1691                         (sc-mail-field "newsgroups") "\n")
1692                 (sc-hdr (concat tag "concerning the subject of ")
1693                         (sc-mail-field "subject") "\n")
1694                 (sc-hdr (concat tag "(see ")
1695                         (sc-mail-field "references")
1696                         " for more details)\n")
1697                 ))))
1698
1699 ;; Added by Steve Baur <steve@xemacs.org> Apr-02-1997.
1700 (defun sc-header-author-email-writes ()
1701   "sc-author <email-addr> writes:"
1702   (let ((sc-mumble "")
1703         (whofrom (sc-whofrom)))
1704     (if whofrom
1705         (insert sc-reference-tag-string
1706                 (sc-hdr "" (sc-mail-field "sc-author") " ")
1707                 (or (sc-hdr "<" (sc-mail-field "sc-from-address") ">" t)
1708                     (sc-hdr "<" (sc-mail-field "sc-reply-address") ">"  t)
1709                     "")
1710                 " writes:\n"))))
1711
1712 \f
1713 ;; ======================================================================
1714 ;; header rewrites
1715
1716 (defconst sc-electric-bufname " *sc-erefs* "
1717   "Supercite electric reference mode's buffer name.")
1718 (defvar sc-eref-style 0
1719   "Current electric reference style.")
1720
1721 (defun sc-valid-index-p (index)
1722   "Returns INDEX if it is a valid index into `sc-rewrite-header-list'.
1723 Otherwise returns nil."
1724     ;; a number, and greater than or equal to zero
1725     ;; less than or equal to the last index
1726     (and (natnump index)
1727          (< index (length sc-rewrite-header-list))
1728          index))
1729
1730 (defun sc-eref-insert-selected (&optional nomsg)
1731   "Insert the selected reference header in the current buffer.
1732 Optional NOMSG, if non-nil, inhibits printing messages, unless an
1733 error occurs."
1734   (let ((ref (nth sc-eref-style sc-rewrite-header-list)))
1735     (condition-case err
1736         (progn
1737           (eval ref)
1738           (let ((lines (count-lines (point-min) (point-max))))
1739             (or nomsg (message "Ref header %d [%d line%s]: %s"
1740                                sc-eref-style lines
1741                                (if (= lines 1) "" "s")
1742                                ref))))
1743       (void-function
1744        (progn (message
1745                "Symbol's function definition is void: %s (Header %d)"
1746                (car (cdr err)) sc-eref-style)
1747               (beep)
1748               ))
1749       )))
1750
1751 (defun sc-electric-mode (&optional arg)
1752   "
1753 Mode for viewing Supercite reference headers.  Commands are:
1754 \n\\{sc-electric-mode-map}
1755
1756 `sc-electric-mode' is not intended to be run interactively, but rather
1757 accessed through Supercite's electric reference feature.  See
1758 `sc-insert-reference' for more details.  Optional ARG is the initial
1759 header style to use, unless not supplied or invalid, in which case
1760 `sc-preferred-header-style' is used."
1761
1762   (let ((info sc-mail-info))
1763
1764     (setq sc-eref-style
1765           (or (sc-valid-index-p arg)
1766               (sc-valid-index-p sc-preferred-header-style)
1767               0))
1768
1769     (get-buffer-create sc-electric-bufname)
1770     ;; set up buffer and enter command loop
1771     (save-excursion
1772       (save-window-excursion
1773         (pop-to-buffer sc-electric-bufname)
1774         (kill-all-local-variables)
1775         (let ((sc-mail-info info)
1776               (buffer-read-only t)
1777               (mode-name "SC Electric Refs")
1778               (major-mode 'sc-electric-mode))
1779           (use-local-map sc-electric-mode-map)
1780           (sc-eref-show sc-eref-style)
1781           (run-hooks 'sc-electric-mode-hook)
1782           (recursive-edit)
1783           )))
1784
1785     (and sc-eref-style
1786          (sc-eref-insert-selected))
1787     (kill-buffer sc-electric-bufname)
1788     ))
1789
1790 ;; functions for electric reference mode
1791 (defun sc-eref-show (index)
1792   "Show reference INDEX in `sc-rewrite-header-list'."
1793   (let ((msg "No %ing reference headers in list.")
1794         (last (length sc-rewrite-header-list)))
1795     (setq sc-eref-style
1796           (cond
1797            ((sc-valid-index-p index) index)
1798            ((< index 0)
1799             (if sc-electric-circular-p
1800                 (1- last)
1801               (progn (error msg "preced") 0)))
1802            ((>= index last)
1803             (if sc-electric-circular-p
1804                 0
1805               (progn (error msg "follow") (1- last))))
1806            ))
1807     (save-excursion
1808      (set-buffer sc-electric-bufname)
1809      (let ((buffer-read-only nil))
1810        (erase-buffer)
1811        (goto-char (point-min))
1812        (sc-eref-insert-selected)
1813        ;; now shrink the window to just contain the electric reference
1814        ;; header.
1815        (let ((hdrlines (count-lines (point-min) (point-max)))
1816              (winlines (1- (window-height))))
1817          (if (/= hdrlines winlines)
1818              (if (> hdrlines winlines)
1819                  ;; we have to enlarge the window
1820                  (enlarge-window (- hdrlines winlines))
1821                ;; we have to shrink the window
1822                (shrink-window (- winlines (max hdrlines window-min-height)))
1823                )))
1824        ))))
1825
1826 (defun sc-eref-next ()
1827   "Display next reference in other buffer."
1828   (interactive)
1829   (sc-eref-show (1+ sc-eref-style)))
1830
1831 (defun sc-eref-prev ()
1832   "Display previous reference in other buffer."
1833   (interactive)
1834   (sc-eref-show (1- sc-eref-style)))
1835
1836 (defun sc-eref-setn ()
1837   "Set reference header selected as preferred."
1838   (interactive)
1839   (setq sc-preferred-header-style sc-eref-style)
1840   (message "Preferred reference style set to header %d." sc-eref-style))
1841
1842 (defun sc-eref-goto (refnum)
1843   "Show reference style indexed by REFNUM.
1844 If REFNUM is an invalid index, don't go to that reference and return
1845 nil."
1846   (interactive "NGoto Reference: ")
1847   (if (sc-valid-index-p refnum)
1848       (sc-eref-show refnum)
1849     (error "Invalid reference: %d. (Range: [%d .. %d])"
1850            refnum 0 (1- (length sc-rewrite-header-list)))
1851     ))
1852
1853 (defun sc-eref-jump ()
1854   "Set reference header to preferred header."
1855   (interactive)
1856   (sc-eref-show sc-preferred-header-style))
1857
1858 (defun sc-eref-abort ()
1859   "Exit from electric reference mode without inserting reference."
1860   (interactive)
1861   (setq sc-eref-style nil)
1862   (exit-recursive-edit))
1863
1864 (defun sc-eref-exit ()
1865   "Exit from electric reference mode and insert selected reference."
1866   (interactive)
1867   (exit-recursive-edit))
1868
1869 (defun sc-insert-reference (arg)
1870   "Insert, at point, a reference header in the body of the reply.
1871 Numeric ARG indicates which header style from `sc-rewrite-header-list'
1872 to use when rewriting the header.  No supplied ARG indicates use of
1873 `sc-preferred-header-style'.
1874
1875 With just `\\[universal-argument]', electric reference insert mode is
1876 entered, regardless of the value of `sc-electric-references-p'.  See
1877 `sc-electric-mode' for more information."
1878   (interactive "P")
1879   (if (consp arg)
1880       (sc-electric-mode)
1881     (let ((preference (or (sc-valid-index-p arg)
1882                           (sc-valid-index-p sc-preferred-header-style)
1883                           sc-preferred-header-style
1884                           0)))
1885       (if sc-electric-references-p
1886           (sc-electric-mode preference)
1887         (sc-eref-insert-selected t)
1888         ))))
1889
1890 \f
1891 ;; ======================================================================
1892 ;; variable toggling
1893
1894 (defun sc-raw-mode-toggle ()
1895   "Toggle, in one fell swoop, two important SC variables:
1896 `sc-fixup-whitespace-p' and `sc-auto-fill-region-p'"
1897   (interactive)
1898   (setq sc-fixup-whitespace-p (not sc-fixup-whitespace-p)
1899         sc-auto-fill-region-p (not sc-auto-fill-region-p))
1900   (sc-set-mode-string)
1901   (set-buffer-modified-p (buffer-modified-p)))
1902
1903 (defun sc-toggle-var (variable)
1904   "Boolean toggle VARIABLE's value.
1905 VARIABLE must be a bound symbol.  Nil values change to t, non-nil
1906 values are changed to nil."
1907   (message "%s changed from %s to %s"
1908            variable (symbol-value variable)
1909            (set-variable variable (not (eval-expression variable))))
1910   (sc-set-mode-string))
1911
1912 (defun sc-set-variable (var)
1913   "Set the Supercite VARIABLE.
1914 This function mimics `set-variable', except that the variable to set
1915 is determined non-interactively.  The value is queried for in the
1916 minibuffer exactly the same way that `set-variable' does it.
1917
1918 You can see the current value of the variable when the minibuffer is
1919 querying you by typing `C-h'.  Note that the format is changed
1920 slightly from that used by `set-variable' -- the current value is
1921 printed just after the variable's name instead of at the bottom of the
1922 help window."
1923   (let* ((minibuffer-help-form
1924           '(funcall myhelp))
1925          (myhelp
1926           (function
1927            (lambda ()
1928              (with-output-to-temp-buffer "*Help*"
1929                (prin1 var)
1930                (if (boundp var)
1931                    (let ((print-length 20))
1932                      (princ "\t(Current value: ")
1933                      (prin1 (symbol-value var))
1934                      (princ ")")))
1935                (princ "\n\nDocumentation:\n")
1936                (princ (substring (documentation-property
1937                                   var
1938                                   'variable-documentation)
1939                                   1))
1940                nil)))))
1941     (set var (eval-minibuffer (format "Set %s to value: " var))))
1942   (sc-set-mode-string))
1943
1944 (defmacro sc-toggle-symbol (rootname)
1945   (list 'defun (intern (concat "sc-T-" rootname)) '()
1946         (list 'interactive)
1947         (list 'sc-toggle-var
1948               (list 'quote (intern (concat "sc-" rootname "-p"))))))
1949
1950 (defmacro sc-setvar-symbol (rootname)
1951   (list 'defun (intern (concat "sc-S-" rootname)) '()
1952         (list 'interactive)
1953         (list 'sc-set-variable
1954               (list 'quote (intern (concat "sc-" rootname))))))
1955
1956 (sc-toggle-symbol "confirm-always")
1957 (sc-toggle-symbol "downcase")
1958 (sc-toggle-symbol "electric-references")
1959 (sc-toggle-symbol "auto-fill-region")
1960 (sc-toggle-symbol "mail-nuke-blank-lines")
1961 (sc-toggle-symbol "nested-citation")
1962 (sc-toggle-symbol "electric-circular")
1963 (sc-toggle-symbol "use-only-preferences")
1964 (sc-toggle-symbol "fixup-whitespace")
1965
1966 (sc-setvar-symbol "preferred-attribution-list")
1967 (sc-setvar-symbol "preferred-header-style")
1968 (sc-setvar-symbol "mail-nuke-mail-headers")
1969 (sc-setvar-symbol "mail-header-nuke-list")
1970 (sc-setvar-symbol "cite-region-limit")
1971
1972 (defun sc-T-describe ()
1973   "
1974
1975 Supercite provides a number of key bindings which simplify the process
1976 of setting or toggling certain variables controlling its operation.
1977
1978 Note on function names in this list: all functions of the form
1979 `sc-S-<name>' actually call `sc-set-variable' on the corresponding
1980 `sc-<name>' variable.  All functions of the form `sc-T-<name>' call
1981 `sc-toggle-var' on the corresponding `sc-<name>-p' variable.
1982
1983 \\{sc-T-keymap}"
1984   (interactive)
1985   (describe-function 'sc-T-describe))
1986
1987 (defun sc-set-mode-string ()
1988   "Update the minor mode string to show state of Supercite."
1989   (setq sc-mode-string
1990         (concat " SC"
1991                 (if (or sc-auto-fill-region-p
1992                         sc-fixup-whitespace-p)
1993                     ":" "")
1994                 (if sc-auto-fill-region-p "f" "")
1995                 (if sc-fixup-whitespace-p "w" "")
1996                 )))
1997
1998 \f
1999 ;; ======================================================================
2000 ;; published interface to mail and news readers
2001
2002 ;;;###autoload
2003 (defun sc-cite-original ()
2004   "Workhorse citing function which performs the initial citation.
2005 This is callable from the various mail and news readers' reply
2006 function according to the agreed upon standard.  See `sc-describe'
2007 for more details.  `sc-cite-original' does not do any yanking of the
2008 original message but it does require a few things:
2009
2010      1) The reply buffer is the current buffer.
2011
2012      2) The original message has been yanked and inserted into the
2013         reply buffer.
2014
2015      3) Verbose mail headers from the original message have been
2016         inserted into the reply buffer directly before the text of the
2017         original message.
2018
2019      4) Point is at the beginning of the verbose headers.
2020
2021      5) Mark is at the end of the body of text to be cited.
2022
2023 For Emacs 19's, the region need not be active (and typically isn't
2024 when this function is called.  Also, the hook `sc-pre-hook' is run
2025 before, and `sc-post-hook' is run after the guts of this function."
2026   (run-hooks 'sc-pre-hook)
2027
2028   ;; before we do anything, we want to insert the supercite keymap so
2029   ;; we can proceed from here
2030   (and sc-mode-map-prefix
2031        (local-set-key sc-mode-map-prefix sc-mode-map))
2032
2033   ;; hack onto the minor mode alist, if it hasn't been done before,
2034   ;; then turn on the minor mode. also, set the minor mode string with
2035   ;; the values of fill and fixup whitespace variables
2036   (if (not (get 'minor-mode-alist 'sc-minor-mode))
2037       (progn
2038         (put 'minor-mode-alist 'sc-minor-mode 'sc-minor-mode)
2039         (setq minor-mode-alist
2040               (cons '(sc-minor-mode sc-mode-string) minor-mode-alist))
2041         ))
2042   (setq sc-minor-mode t)
2043   (sc-set-mode-string)
2044
2045   (undo-boundary)
2046
2047   ;; grab point and mark since the region is probably not active when
2048   ;; this function gets automatically called. we want point to be a
2049   ;; mark so any deleting before point works properly
2050   (let* ((zmacs-regions nil)            ; for XEmacs
2051          (mark-active t)                ; for FSFmacs
2052          (point (point-marker))
2053          (mark  (copy-marker (mark-marker))))
2054
2055     ;; make sure point comes before mark, not all functions are
2056     ;; interactive "r"
2057     (if (< mark point)
2058         (let ((tmp point))
2059           (setq point mark
2060                 mark tmp)))
2061
2062     ;; first process mail headers, and populate sc-mail-info
2063     (sc-mail-process-headers point mark)
2064
2065     ;; now get possible attributions
2066     (sc-attribs-chop-address (or (sc-mail-field "from")
2067                                  (sc-mail-field "reply")
2068                                  (sc-mail-field "reply-to")
2069                                  (sc-mail-field "sender")))
2070     ;; select the attribution
2071     (sc-select-attribution)
2072
2073     ;; cite the region, but first check the value of sc-cite-region-limit
2074     (let ((linecnt (count-lines point mark)))
2075       (and sc-cite-region-limit
2076            (if (or (not (numberp sc-cite-region-limit))
2077                    (<= linecnt sc-cite-region-limit))
2078                (progn
2079                  ;; cite the region and insert the header rewrite
2080                  (sc-cite-region point mark)
2081                  (goto-char point)
2082                  (let ((sc-eref-style (or sc-preferred-header-style 0)))
2083                    (if sc-electric-references-p
2084                        (sc-electric-mode sc-eref-style)
2085                      (sc-eref-insert-selected t))))
2086              (beep)
2087              (message
2088               "Region not cited. %d lines exceeds sc-cite-region-limit: %d"
2089               linecnt sc-cite-region-limit))))
2090
2091     ;; finally, free the point-marker
2092     (set-marker point nil)
2093     (set-marker mark nil)
2094     )
2095   (run-hooks 'sc-post-hook)
2096   ;; post hook could have changed the variables
2097   (sc-set-mode-string))
2098
2099 \f
2100 ;; ======================================================================
2101 ;; bug reporting and miscellaneous commands
2102
2103 (defun sc-open-line (arg)
2104   "Like `open-line', but insert the citation prefix at the front of the line.
2105 With numeric ARG, inserts that many new lines."
2106   (interactive "p")
2107   (save-excursion
2108     (let ((start (point))
2109           (prefix (or (progn (beginning-of-line)
2110                              (if (looking-at (sc-cite-regexp))
2111                                  (sc-submatch 0)))
2112                       "")))
2113       (goto-char start)
2114       (open-line arg)
2115       (forward-line 1)
2116       (while (< 0 arg)
2117         (insert prefix)
2118         (forward-line 1)
2119         (setq arg (1- arg))
2120         ))))
2121
2122 (defun sc-insert-citation (arg)
2123   "Insert citation string at beginning of current line if not already cited.
2124 With `\\[universal-argument]' insert citation even if line is already
2125 cited."
2126   (interactive "P")
2127   (save-excursion
2128     (beginning-of-line)
2129     (if (or (not (looking-at (sc-cite-regexp)))
2130             (looking-at "^[ \t]*$")
2131             (consp arg))
2132         (insert (sc-mail-field "sc-citation"))
2133       (error "Line is already cited."))))
2134
2135 (defun sc-version (arg)
2136   "Echo the current version of Supercite in the minibuffer.
2137 With \\[universal-argument] (universal-argument), or if run non-interactively,
2138 inserts the version string in the current buffer instead."
2139   (interactive "P")
2140   (let ((verstr (format "Using Supercite.el %s" sc-version)))
2141     (if (or (consp arg)
2142             (not (interactive-p)))
2143         (insert "`sc-version' says: " verstr)
2144       (message verstr))))
2145
2146 (defun sc-describe ()
2147   "
2148 Supercite is a package which provides a flexible mechanism for citing
2149 email and news replies.  Please see the associated texinfo file for
2150 more information."
2151   (interactive)
2152   (describe-function 'sc-describe))
2153
2154 (defun sc-submit-bug-report ()
2155   "Submit a bug report on Supercite via mail."
2156   (interactive)
2157   (require 'reporter)
2158   (and
2159    (y-or-n-p "Do you want to submit a report on Supercite? ")
2160    (reporter-submit-bug-report
2161     sc-help-address
2162     (concat "Supercite version " sc-version)
2163     (list
2164      'sc-attrib-selection-list
2165      'sc-auto-fill-region-p
2166      'sc-blank-lines-after-headers
2167      'sc-citation-leader
2168      'sc-citation-delimiter
2169      'sc-citation-separator
2170      'sc-citation-leader-regexp
2171      'sc-citation-root-regexp
2172      'sc-citation-nonnested-root-regexp
2173      'sc-citation-delimiter-regexp
2174      'sc-citation-separator-regexp
2175      'sc-cite-region-limit
2176      'sc-confirm-always-p
2177      'sc-default-attribution
2178      'sc-default-author-name
2179      'sc-downcase-p
2180      'sc-electric-circular-p
2181      'sc-electric-references-p
2182      'sc-fixup-whitespace-p
2183      'sc-mail-warn-if-non-rfc822-p
2184      'sc-mumble
2185      'sc-name-filter-alist
2186      'sc-nested-citation-p
2187      'sc-nuke-mail-headers
2188      'sc-nuke-mail-header-list
2189      'sc-preferred-attribution-list
2190      'sc-preferred-header-style
2191      'sc-reference-tag-string
2192      'sc-rewrite-header-list
2193      'sc-titlecue-regexp
2194      'sc-use-only-preference-p
2195      ))))
2196
2197 \f
2198 ;; useful stuff
2199 (provide 'supercite)
2200 (run-hooks 'sc-load-hook)
2201
2202 ;;; supercite.el ends here