;;; mailabbrev.el --- abbrev-expansion of mail aliases
;; Copyright (C) 1985, 1986, 1987, 1992, 1993, 1996, 1997, 2000, 2001,
-;; 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+;; Free Software Foundation, Inc.
-;; Author: Jamie Zawinski <jwz@lucid.com>, now <jwz@jwz.org>
+;; Author: Jamie Zawinski <jwz@lucid.com; now jwz@jwz.org>
;; Maintainer: FSF
;; Created: 19 Oct 90
;; Keywords: mail
;; If you want multiple addresses separated by a string other than ", " then
;; you can set the variable mail-alias-separator-string to it. This has to
;; be a comma bracketed by whitespace if you want any kind of reasonable
-;; behaviour.
+;; behavior.
;;
;; Thanks to Harald Hanche-Olsen, Michael Ernst, David Loeffler, and
;; Noah Friedman for suggestions and bug reports.
;;;###autoload
(defun define-mail-abbrev (name definition &optional from-mailrc-file)
"Define NAME as a mail alias abbrev that translates to DEFINITION.
-If DEFINITION contains multiple addresses, separate them with commas."
+If DEFINITION contains multiple addresses, separate them with commas.
+
+Optional argument FROM-MAILRC-FILE means that DEFINITION comes
+from a mailrc file. In that case, addresses are separated with
+spaces and addresses with embedded spaces are surrounded by
+double-quotes."
;; When this is called from build-mail-abbrevs, the third argument is
;; true, and we do some evil space->comma hacking like /bin/mail does.
(interactive "sDefine mail alias: \nsDefine %s as mail alias for: ")
(setq definition (substring definition (match-end 0))))
(if (string-match "[ \t\n,]+\\'" definition)
(setq definition (substring definition 0 (match-beginning 0))))
- (let* ((result '())
- (L (length definition))
+ (let* ((L (length definition))
(start (if (> L 0) 0))
- end)
+ end this-entry result)
(while start
- ;; If we're reading from the mailrc file, then addresses are delimited
- ;; by spaces, and addresses with embedded spaces must be surrounded by
- ;; double-quotes. Otherwise, addresses are separated by commas.
- (if from-mailrc-file
- (if (eq ?\" (aref definition start))
- (setq start (1+ start)
- end (string-match "\"[ \t,]*" definition start))
- (setq end (string-match "[ \t,]+" definition start)))
- (setq end (string-match "[ \t\n,]*,[ \t\n,]*" definition start)))
- (let ((tem (substring definition start end)))
+ (cond
+ (from-mailrc-file
+ ;; If we're reading from the mailrc file, addresses are
+ ;; delimited by spaces, and addresses with embedded spaces are
+ ;; surrounded by non-escaped double-quotes.
+ (if (eq ?\" (aref definition start))
+ (setq start (1+ start)
+ end (and (string-match
+ "[^\\]\\(\\([\\][\\]\\)*\\)\"[ \t,]*"
+ definition start)
+ (match-end 1)))
+ (setq end (string-match "[ \t,]+" definition start)))
+ ;; Extract the address and advance the loop past it.
+ (setq this-entry (substring definition start end)
+ start (and end (/= (match-end 0) L) (match-end 0)))
+ ;; If the full name contains a problem character, quote it.
+ (and (string-match "\\(.+?\\)[ \t]*\\(<.*>\\)" this-entry)
+ (string-match "[^- !#$%&'*+/0-9=?A-Za-z^_`{|}~]"
+ (match-string 1 this-entry))
+ (setq this-entry (replace-regexp-in-string
+ "\\(.+?\\)[ \t]*\\(<.*>\\)"
+ "\"\\1\" \\2"
+ this-entry)))
+ (push this-entry result))
+ ;; When we are not reading from .mailrc, addresses are
+ ;; separated by commas. Try to accept a rfc822-like syntax.
+ ;; (Todo: extend rfc822.el to do the work for us.)
+ ((equal (string-match
+ "[ \t,]*\\(\"\\(?:[^\"]\\|[^\\]\\(?:[\\][\\]\\)*\"\\)*\"[ \t]*\
+<[-.!#$%&'*+/0-9=?A-Za-z^_`{|}~@]+>\\)[ \t,]*"
+ definition start)
+ start)
+ ;; If an entry has a valid [ "foo bar" <foo@example.com> ]
+ ;; form, use it literally . This also allows commas in the
+ ;; quoted string, e.g. [ "foo bar, jr" <foo@example.com> ]
+ (push (match-string 1 definition) result)
+ (setq start (and (/= (match-end 0) L) (match-end 0))))
+ (t
+ ;; Otherwise, read the next address by looking for a comma.
+ (setq end (string-match "[ \t\n,]*,[ \t\n]*" definition start))
+ (setq this-entry (substring definition start end))
;; Advance the loop past this address.
- (setq start (and end
- (/= (match-end 0) L)
- (match-end 0)))
+ (setq start (and end (/= (match-end 0) L) (match-end 0)))
;; If the full name contains a problem character, quote it.
- (when (string-match "\\(.+?\\)[ \t]*\\(<.*>\\)" tem)
- (if (string-match "[^- !#$%&'*+/0-9=?A-Za-z^_`{|}~]"
- (match-string 1 tem))
- (setq tem (replace-regexp-in-string
- "\\(.+?\\)[ \t]*\\(<.*>\\)" "\"\\1\" \\2"
- tem))))
- (push tem result)))
+ (and (string-match "\\(.+?\\)[ \t]*\\(<.*>\\)" this-entry)
+ (string-match "[^- !#$%&'*+/0-9=?A-Za-z^_`{|}~]"
+ (match-string 1 this-entry))
+ (setq this-entry (replace-regexp-in-string
+ "\\(.+?\\)[ \t]*\\(<.*>\\)" "\"\\1\" \\2"
+ this-entry)))
+ (push this-entry result))))
+
(setq definition (mapconcat (function identity)
(nreverse result)
mail-alias-separator-string)))
;; when the user types -.) Check the character's syntax in
;; the usual syntax table.
- (or (and (integerp last-command-char)
+ (or (and (integerp last-command-event)
;; Some commands such as M-> may want to expand first.
(equal this-command 'self-insert-command)
- (or (eq (char-syntax last-command-char) ?_)
+ (or (eq (char-syntax last-command-event) ?_)
;; Don't expand on @.
- (memq last-command-char '(?@ ?. ?% ?! ?_ ?-))))
+ (memq last-command-event '(?@ ?. ?% ?! ?_ ?-))))
;; Use this table so that abbrevs can have hyphens in them.
(with-syntax-table mail-abbrev-syntax-table
(funcall expand))))
alias))))))
(defun mail-abbrev-next-line (&optional arg)
- "Expand any mail abbrev, then move cursor vertically down ARG lines.
-If there is no character in the target line exactly under the current column,
-the cursor is positioned after the character in that line which spans this
-column, or at the end of the line if it is not long enough.
-If there is no line in the buffer after this one,
-a newline character is inserted to create a line
-and the cursor moves to that line.
-
-The command \\[set-goal-column] can be used to create
-a semipermanent goal column to which this command always moves.
-Then it does not try to move vertically. This goal column is stored
-in `goal-column', which is nil when there is none.
-
-If you are thinking of using this in a Lisp program, consider
-using `forward-line' instead. It is usually easier to use
-and more reliable (no dependence on goal column, etc.)."
+ "Expand a mail abbrev before point, then move vertically down ARG lines.
+This only expands an abbrev (if one is present) if called with
+point at the end of a line, or on whitespace before the end of a line.
+
+In terms of line motion, this behaves like `next-line', which see."
(interactive "p")
(if (looking-at "[ \t]*\n") (expand-abbrev))
(setq this-command 'next-line)
(with-no-warnings (next-line arg)))
(defun mail-abbrev-end-of-buffer (&optional arg)
- "Expand any mail abbrev, then move point to end of buffer.
-Leave mark at previous position.
-With arg N, put point N/10 of the way from the true end.
+ "Expand a mail abbrev before point, then move to the end of the buffer.
+This only expands an abbrev (if one is present) if called with
+point at the end of a line, or on whitespace before the end of a line.
-Don't use this command in Lisp programs!
-\(goto-char (point-max)) is faster and avoids clobbering the mark."
+In other respects, this behaves like `end-of-buffer', which see."
(interactive "P")
(if (looking-at "[ \t]*\n") (expand-abbrev))
(setq this-command 'end-of-buffer)
- (with-no-warnings
- (end-of-buffer arg)))
+ (with-no-warnings (end-of-buffer arg)))
(eval-after-load "sendmail"
'(progn