unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Alexander Pohoyda <alexander.pohoyda@gmx.net>
Subject: Re: New mail-related routines
Date: 26 Oct 2004 00:43:47 +0200	[thread overview]
Message-ID: <874qkibeqk.fsf@oak.pohoyda.family> (raw)
In-Reply-To: <ilu3c04b9w1.fsf@latte.josefsson.org>

Please comment on this code.  Thank you!

;;; The -hf suffix means Header Field.

(defconst mail-wsp-regexp "[\040\011]")
(defconst mail-crlf-regexp "[\015]?[\012]")

;; Header fields must be unfolded before using these regexps.  This
;; agrees with the RFC 2822, section 2.2.3, last paragraph.

;; Unstructured header fields
(defconst mail-hf-name-regexp "[\041-\071\073-\176]+")
(defconst mail-hf-body-regexp "[^\015\012]*")
(defconst mail-hf-regexp
  (format "^\\(%s\\)%s*:%s*\\(%s\\)%s*\\(%s\\)?"
	  mail-hf-name-regexp mail-wsp-regexp mail-wsp-regexp
	  mail-hf-body-regexp mail-wsp-regexp mail-crlf-regexp))

\f
;;;
;;; General-purpose mail functions
;;;

;; Merging this function with `rfc822-goto-eoh' failed, because
;; mbox-formatted messages start with "From name@example.org...",
;; which is neither a valid header field, nor the end of header.
(defun mail-body-start-position (&optional from to)
  "Return a position where the body of a message starts.

If called without arguments, the current buffer is assumed to be
narrowed to exactly one message.

This function may also be used to get the body start position of
a MIME entity in the region between FROM and TO."
  (save-excursion
    (goto-char (or from (point-min)))
    (save-match-data
      (if (or (search-forward (concat "\n" mail-header-separator "\n") to t)
	      (search-forward "\n\n" to t))
	  (point)
	(message "This entity has no body")
	(or to (point-max))))))

(defun mail-header-end-position (&optional from to)
  "Return a position where the header of a message ends.

If called without arguments, the current buffer is assumed to be
narrowed to exactly one message.

This function may also be used to get the header end position of
a MIME entity in the region between FROM and TO."
  (save-excursion
    (goto-char (mail-body-start-position (or from (point-min))
					 (or to (point-max))))
    (forward-line -1)
    (point)))

\f
;;;
;;; Header field functions
;;;

(defsubst mail-make-hf (name body)
  "Return \"NAME: BODY\" string."
  (when name (concat name ": " body)))

(defsubst mail-insert-hf (header-field)
  "Insert the HEADER-FIELD created by `mail-make-hf' function at point."
  (when header-field (insert header-field "\n")))

(defun mail-search-hf (name &optional from to)
  "Find a header field named NAME in the message header.
Set point at the beginning of the field found, and return point.
If the header field is not found, do not move the point and return nil.
The argument FROM defaults to `point-min' and the argument TO is
set to the message header end."
  (let ((found nil)
	(case-fold-search t))
    (save-excursion
      (goto-char (or from (point-min)))
      (save-match-data
	(when (re-search-forward (concat "^" name ":") to t)
	  (setq found (point-at-bol)))))
    (when found (goto-char found))))

(defun mail-hf-body-position ()
  "Return a position where the current header field body starts."
  (save-excursion
    (save-match-data
      (re-search-forward (format ":\\(%s*\\)" mail-wsp-regexp) nil t))))

(defun mail-hf-end-position ()
  "Return a position where the current header field ends."
  (save-excursion
    (save-match-data
      (while (progn
	       (forward-line)
	       (looking-at (format "%s+" mail-wsp-regexp))))
      (point))))

(defun mail-get-hf-at-point ()
  "Return the header field at point."
  (buffer-substring-no-properties (point) (mail-hf-end-position)))

(defun mail-get-hf (name &optional from to)
  "Return the whole header field called NAME as a string.

The argument FROM defaults to `point-min' and the argument TO is
set to the message header end.

The trailing CRLF is also included."
  (save-excursion
    (when (mail-search-hf name from to)
      (mail-get-hf-at-point))))

(defun mail-get-hf-name (header-field)
  "Return the name of the HEADER-FIELD string."
  (when header-field
    (setq header-field (mail-unfold-hf header-field))
    (save-match-data
      (when (string-match mail-hf-regexp header-field)
	(match-string-no-properties 1 header-field)))))

(defun mail-get-hf-body (header-field)
  "Return the body of the HEADER-FIELD string."
  (when header-field
    (setq header-field (mail-unfold-hf header-field))
    (save-match-data
      (when (string-match mail-hf-regexp header-field)
	(match-string-no-properties 2 header-field)))))

(defun mail-process-hfs-in-region (from to function)
  "Enumerate all header fields in the region between FROM and TO and
call FUNCTION on them."
  (save-excursion
    (goto-char from)
    (save-restriction
      (narrow-to-region from to)
      ;; RFC 2822, section 2.2.3.
      (while (re-search-forward "^[^ \t]+:" nil t)
	(beginning-of-line)
	;;(message "Processing `%s' header..."
	;;	 (mail-get-hf-name (mail-get-hf-at-point)))
	(funcall function (point) (mail-hf-end-position))
	;; Goto next header field
	(goto-char (mail-hf-end-position)))
      (- (point-max) from))))

(defun mail-sort-hfs-in-region (from to sort-list)
  "Sort header fields in the region between FROM and TO, using
SORT-LIST as a sequence."
  (save-excursion
    (goto-char from)
    (save-restriction
      (narrow-to-region from to)
      ;; Do the job.
      (let ((my-pos (point))
	    my-hf)
	(dolist (sorted-hf sort-list)
	  ;;(message "Sorting `%s' header..." sorted-hf)
	  (when (mail-search-hf sorted-hf)
	    (setq my-hf (mail-get-hf-at-point))
	    (delete-region (point) (mail-hf-end-position))
	    (goto-char my-pos)
	    (insert my-hf)
	    (setq my-pos (point))))))))

(defun mail-fold-hf (header-field)
  "See description of `mail-fold-region' function."
  (when header-field
    (with-temp-buffer
      ;;(message "Header to fold:\n%s" header-field)
      (insert header-field)
      (mail-fold-region (point-min) (point-max))
      (buffer-string))))

(defun mail-fold-region (from to &optional limit)
  "Fold header fields in the region between FROM and TO,
as defined by RFC 2822.  The LIMIT argument defaults to 76."
  (save-excursion
    (goto-char from)
    (save-restriction
      (narrow-to-region from to)
      (let ((limit (or limit 76))
	    start)
	(while (not (eobp))
	  (setq start (point))
	  (goto-char (min (+ (point) (- limit (current-column)))
			  (point-at-eol)))
	  (if (and (>= (current-column) limit)
		   (re-search-backward mail-wsp-regexp start t)
		   (not (looking-at (format "\n%s" mail-wsp-regexp))))
	      ;; Insert line break
	      (progn
		(insert "\n")
		(forward-char))
	    (if (re-search-backward mail-wsp-regexp start t)
		(forward-line)
	      ;; Token is too long, so we skip it
	      (re-search-forward mail-wsp-regexp nil t)
	      (backward-char)
	      (insert "\n")
	      (forward-char))))))))

(defun mail-unfold-hf (header-field &optional loose)
  "See description of `mail-unfold-region' function."
  (when header-field
    (with-temp-buffer
      ;;(message "Header to unfold:\n%s" header-field)
      (insert header-field)
      (mail-unfold-region (point-min) (point-max) loose)
      (buffer-string))))

(defun mail-unfold-region (from to &optional loose)
  "Unfold header fields in the region between FROM and TO, 
as defined by RFC 2822.

If LOOSE argument is non-nil, replace also all leading WSP
characters with just one SPACE."
  (save-excursion
    (goto-char from)
    (save-restriction
      (narrow-to-region from to)
      (save-match-data
	(while (re-search-forward
		(format "\\(%s\\)%s+" mail-crlf-regexp mail-wsp-regexp) nil t)
	  (if loose
	      (replace-match " " nil t)
	    (replace-match "" nil t nil 1)))))))


-- 
Alexander Pohoyda <alexander.pohoyda@gmx.net>
PGP Key fingerprint: 7F C9 CC 5A 75 CD 89 72  15 54 5F 62 20 23 C6 44

  parent reply	other threads:[~2004-10-25 22:43 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-10-18 21:57 New mail-related routines Alexander Pohoyda
2004-10-18 22:12 ` Stefan Monnier
2004-10-19  7:06   ` Alexander Pohoyda
2004-10-19 12:51     ` Stefan Monnier
2004-10-19 18:37     ` Alexander Pohoyda
2004-10-19 19:29       ` Stefan Monnier
2004-10-19 23:56         ` Alexander Pohoyda
2004-10-19 12:32 ` Reiner Steib
2004-10-19 17:47   ` Alexander Pohoyda
2004-10-19 20:02     ` Reiner Steib
2004-10-20  0:03       ` Alexander Pohoyda
2004-10-24 12:03 ` Simon Josefsson
2004-10-25 22:15   ` Alexander Pohoyda
2004-10-25 22:43   ` Alexander Pohoyda [this message]
2004-10-26 23:16     ` Kevin Rodgers
2004-10-27 16:04       ` Alexander Pohoyda

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=874qkibeqk.fsf@oak.pohoyda.family \
    --to=alexander.pohoyda@gmx.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).