1;;; mh-scan.el --- MH-E scan line constants and utilities
2
3;; Copyright (C) 1993, 1995, 1997,
4;;  2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5
6;; Author: Bill Wohler <wohler@newt.com>
7;; Maintainer: Bill Wohler <wohler@newt.com>
8;; Keywords: mail
9;; See: mh-e.el
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation; either version 2, or (at your option)
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs; see the file COPYING.  If not, write to the
25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26;; Boston, MA 02110-1301, USA.
27
28;;; Commentary:
29
30;; This file contains constants and a few functions for interpreting
31;; scan lines.
32
33;;; Change Log:
34
35;;; Code:
36
37(require 'mh-e)
38
39
40
41;;; Scan Formats
42
43;; The following scan formats are passed to the scan program if the setting of
44;; `mh-scan-format-file' is t. They are identical except the later one makes
45;; use of the nmh `decode' function to decode RFC 2047 encodings. If you just
46;; want to change the column of the notations, use the `mh-set-cmd-note'
47;; function.
48
49(defvar mh-scan-format-mh
50  (concat
51   "%4(msg)"
52   "%<(cur)+%| %>"
53   "%<{replied}-"
54   "%?(nonnull(comp{to}))%<(mymbox{to})t%>"
55   "%?(nonnull(comp{cc}))%<(mymbox{cc})c%>"
56   "%?(nonnull(comp{bcc}))%<(mymbox{bcc})b%>"
57   "%?(nonnull(comp{newsgroups}))n%>"
58   "%<(zero) %>"
59   "%02(mon{date})/%02(mday{date})%<{date} %|*%>"
60   "%<(mymbox{from})%<{to}To:%14(friendly{to})%>%>"
61   "%<(zero)%17(friendly{from})%>  "
62   "%{subject}%<{body}<<%{body}%>")
63  "*Scan format string for MH.
64This string is passed to the scan program via the -format
65argument. This format is identical to the default except that
66additional hints for fontification have been added to the fifth
67column (remember that in Emacs, the first column is 0).
68
69The values of the fifth column, in priority order, are: \"-\" if
70the message has been replied to, t if an address on the To: line
71matches one of the mailboxes of the current user, \"c\" if the Cc:
72line matches, \"b\" if the Bcc: line matches, and \"n\" if a
73non-empty Newsgroups: header is present.")
74
75(defvar mh-scan-format-nmh
76  (concat
77   "%4(msg)"
78   "%<(cur)+%| %>"
79   "%<{replied}-"
80   "%?(nonnull(comp{to}))%<(mymbox{to})t%>"
81   "%?(nonnull(comp{cc}))%<(mymbox{cc})c%>"
82   "%?(nonnull(comp{bcc}))%<(mymbox{bcc})b%>"
83   "%?(nonnull(comp{newsgroups}))n%>"
84   "%<(zero) %>"
85   "%02(mon{date})/%02(mday{date})%<{date} %|*%>"
86   "%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>"
87   "%<(zero)%17(decode(friendly{from}))%>  "
88   "%(decode{subject})%<{body}<<%{body}%>")
89  "*Scan format string for nmh.
90This string is passed to the scan program via the -format arg.
91This format is identical to the default except that additional
92hints for fontification have been added to the fifth
93column (remember that in Emacs, the first column is 0).
94
95The values of the fifth column, in priority order, are: \"-\" if
96the message has been replied to, t if an address on the To: field
97matches one of the mailboxes of the current user, \"c\" if the Cc:
98field matches, \"b\" if the Bcc: field matches, and \"n\" if a
99non-empty Newsgroups: field is present.")
100
101
102
103;;; Regular Expressions
104
105;; Alphabetical.
106
107(defvar mh-scan-body-regexp "\\(<<\\([^\n]+\\)?\\)"
108  "This regular expression matches the message body fragment.
109
110Note that the default setting of `mh-folder-font-lock-keywords'
111expects this expression to contain at least one parenthesized
112expression which matches the body text as in the default of
113\"\\\\(<<\\\\([^\\n]+\\\\)?\\\\)\". If this regular expression is
114not correct, the body fragment will not be highlighted with the
115face `mh-folder-body'.")
116
117(defvar mh-scan-cur-msg-number-regexp "^\\( *[0-9]+\\+\\).*"
118  "This regular expression matches the current message.
119
120It must match from the beginning of the line. Note that the
121default setting of `mh-folder-font-lock-keywords' expects this
122expression to contain at least one parenthesized expression which
123matches the message number as in the default of
124
125  \"^\\\\( *[0-9]+\\\\+\\\\).*\".
126
127This expression includes the leading space and current message
128marker \"+\" within the parenthesis since it looks better to
129highlight these items as well. The highlighting is done with the
130face `mh-folder-cur-msg-number'. This regular expression should
131be correct as it is needed by non-fontification functions. See
132also `mh-note-cur'.")
133
134(defvar mh-scan-date-regexp "\\([0-9][0-9]/[0-9][0-9]\\)"
135  "This regular expression matches a valid date.
136
137It must not be anchored to the beginning or the end of the line.
138Note that the default setting of `mh-folder-font-lock-keywords'
139expects this expression to contain only one parenthesized
140expression which matches the date field as in the default of
141\"\\\\([0-9][0-9]/[0-9][0-9]\\\\)\"}. If this regular expression
142is not correct, the date will not be highlighted with the face
143`mh-folder-date'.")
144
145(defvar mh-scan-deleted-msg-regexp "^\\( *[0-9]+\\)D"
146  "This regular expression matches deleted messages.
147
148It must match from the beginning of the line. Note that the
149default setting of `mh-folder-font-lock-keywords' expects this
150expression to contain at least one parenthesized expression which
151matches the message number as in the default of
152
153  \"^\\\\( *[0-9]+\\\\)D\".
154
155This expression includes the leading space within the parenthesis
156since it looks better to highlight it as well. The highlighting
157is done with the face `mh-folder-deleted'. This regular
158expression should be correct as it is needed by non-fontification
159functions. See also `mh-note-deleted'.")
160
161(defvar mh-scan-good-msg-regexp  "^\\( *[0-9]+\\)[^D^0-9]"
162  "This regular expression matches \"good\" messages.
163
164It must match from the beginning of the line. Note that the
165default setting of `mh-folder-font-lock-keywords' expects this
166expression to contain at least one parenthesized expression which
167matches the message number as in the default of
168
169  \"^\\\\( *[0-9]+\\\\)[^D^0-9]\".
170
171This expression includes the leading space within the parenthesis
172since it looks better to highlight it as well. The highlighting
173is done with the face `mh-folder-msg-number'. This regular
174expression should be correct as it is needed by non-fontification
175functions.")
176
177(defvar mh-scan-msg-format-regexp "%\\([0-9]*\\)(msg)"
178  "This regular expression finds the message number width in a scan format.
179
180Note that the message number must be placed in a parenthesized
181expression as in the default of \"%\\\\([0-9]*\\\\)(msg)\". This
182variable is only consulted if `mh-scan-format-file' is set to
183\"Use MH-E scan Format\".")
184
185(defvar mh-scan-msg-format-string "%d"
186  "This is a format string for width of the message number in a scan format.
187
188Use \"0%d\" for zero-filled message numbers. This variable is only
189consulted if `mh-scan-format-file' is set to \"Use MH-E scan
190Format\".")
191
192(defvar mh-scan-msg-number-regexp "^ *\\([0-9]+\\)"
193  "This regular expression extracts the message number.
194
195It must match from the beginning of the line. Note that the
196message number must be placed in a parenthesized expression as in
197the default of \"^ *\\\\([0-9]+\\\\)\".")
198
199(defvar mh-scan-msg-overflow-regexp "^[?0-9][0-9]"
200  "This regular expression matches overflowed message numbers.")
201
202(defvar mh-scan-msg-search-regexp "^[^0-9]*%d[^0-9]"
203  "This regular expression matches a particular message.
204
205It is a format string; use \"%d\" to represent the location of the
206message number within the expression as in the default of
207\"^[^0-9]*%d[^0-9]\".")
208
209(defvar mh-scan-rcpt-regexp  "\\(To:\\)\\(..............\\)"
210  "This regular expression specifies the recipient in messages you sent.
211
212Note that the default setting of `mh-folder-font-lock-keywords'
213expects this expression to contain two parenthesized expressions.
214The first is expected to match the \"To:\" that the default scan
215format file generates. The second is expected to match the
216recipient's name as in the default of
217\"\\\\(To:\\\\)\\\\(..............\\\\)\". If this regular
218expression is not correct, the \"To:\" string will not be
219highlighted with the face `mh-folder-to' and the recipient will
220not be highlighted with the face `mh-folder-address'")
221
222(defvar mh-scan-refiled-msg-regexp  "^\\( *[0-9]+\\)\\^"
223  "This regular expression matches refiled messages.
224
225It must match from the beginning of the line. Note that the
226default setting of `mh-folder-font-lock-keywords' expects this
227expression to contain at least one parenthesized expression which
228matches the message number as in the default of
229
230  \"^\\\\( *[0-9]+\\\\)\\\\^\".
231
232This expression includes the leading space within the parenthesis
233since it looks better to highlight it as well. The highlighting
234is done with the face `mh-folder-refiled'. This regular
235expression should be correct as it is needed by non-fontification
236functions. See also `mh-note-refiled'.")
237
238(defvar mh-scan-sent-to-me-sender-regexp
239  "^ *[0-9]+.\\([bct]\\).....[ ]*\\(..................\\)"
240  "This regular expression matches messages sent to us.
241
242Note that the default setting of `mh-folder-font-lock-keywords'
243expects this expression to contain at least two parenthesized
244expressions. The first should match the fontification hint (see
245`mh-scan-format-nmh') and the second should match the user name
246as in the default of
247
248  ^ *[0-9]+.\\\\([bct]\\\\).....[ ]*\\\\(..................\\\\)
249
250If this regular expression is not correct, the notation hints
251will not be highlighted with the face
252`mh-mh-folder-sent-to-me-hint' and the sender will not be
253highlighted with the face `mh-folder-sent-to-me-sender'.")
254
255(defvar mh-scan-subject-regexp
256  "^ *[0-9]+........[ ]*...................\\([Rr][Ee]\\(\\[[0-9]+\\]\\)?:\\s-*\\)*\\([^<\n]*\\)"
257  "This regular expression matches the subject.
258
259It must match from the beginning of the line. Note that the
260default setting of `mh-folder-font-lock-keywords' expects this
261expression to contain at least three parenthesized expressions.
262The first is expected to match the \"Re:\" string, if any, and is
263highlighted with the face `mh-folder-followup'. The second
264matches an optional bracketed number after \"Re:\", such as in
265\"Re[2]:\" (and is thus a sub-expression of the first expression)
266and the third is expected to match the subject line itself which
267is highlighted with the face `mh-folder-subject'. For example,
268the default (broken on multiple lines for readability) is
269
270  ^ *[0-9]+........[ ]*...................
271  \\\\([Rr][Ee]\\\\(\\\\\\=[[0-9]+\\\\]\\\\)?:\\\\s-*\\\\)*
272  \\\\([^<\\n]*\\\\)
273
274This regular expression should be correct as it is needed by
275non-fontification functions.")
276
277(defvar mh-scan-valid-regexp "^ *[0-9]"
278  "This regular expression describes a valid scan line.
279
280This is used to eliminate error messages that are occasionally
281produced by \"inc\".")
282
283
284
285;;; Widths, Offsets and Columns
286
287(defvar mh-cmd-note 4
288  "Column for notations.
289
290This variable should be set with the function `mh-set-cmd-note'.
291This variable may be updated dynamically if
292`mh-adaptive-cmd-note-flag' is on.
293
294Note that columns in Emacs start with 0.")
295(make-variable-buffer-local 'mh-cmd-note)
296
297(defvar mh-scan-cmd-note-width 1
298  "Number of columns consumed by the cmd-note field in `mh-scan-format'.
299
300This column will have one of the values: \" \", \"D\", \"^\", \"+\" and
301where \" \" is the default value,
302
303  \"D\" is the `mh-note-deleted' character,
304  \"^\" is the `mh-note-refiled' character, and
305  \"+\" is the `mh-note-cur' character.")
306
307(defvar mh-scan-destination-width 1
308  "Number of columns consumed by the destination field in `mh-scan-format'.
309
310This column will have one of \" \", \"%\", \"-\", \"t\", \"c\", \"b\", or \"n\"
311in it.
312
313  \" \" blank space is the default character.
314  \"%\" indicates that the message in in a named MH sequence.
315  \"-\" indicates that the message has been annotated with a replied field.
316  \"t\" indicates that the message contains mymbox in the To: field.
317  \"c\" indicates that the message contains mymbox in the Cc: field.
318  \"b\" indicates that the message contains mymbox in the Bcc: field.
319  \"n\" indicates that the message contains a Newsgroups: field.")
320
321(defvar mh-scan-date-width 5
322  "Number of columns consumed by the date field in `mh-scan-format'.
323This column will typically be of the form mm/dd.")
324
325(defvar mh-scan-date-flag-width 1
326  "Number of columns consumed to flag (in)valid dates in `mh-scan-format'.
327This column will have \" \" for valid and \"*\" for invalid or
328missing dates.")
329
330(defvar mh-scan-from-mbox-width 17
331  "Number of columns consumed with the \"From:\" line in `mh-scan-format'.
332This column will have a friendly name or e-mail address of the
333originator, or a \"To: address\" for outgoing e-mail messages.")
334
335(defvar mh-scan-from-mbox-sep-width 2
336  "Number of columns consumed by whitespace after from-mbox in `mh-scan-format'.
337This column will only ever have spaces in it.")
338
339(defvar mh-scan-field-destination-offset
340  (+ mh-scan-cmd-note-width)
341  "The offset from the `mh-cmd-note' for the destination column.")
342
343(defvar mh-scan-field-from-start-offset
344  (+ mh-scan-cmd-note-width
345     mh-scan-destination-width
346     mh-scan-date-width
347     mh-scan-date-flag-width)
348  "The offset from the `mh-cmd-note' to find the start of \"From:\" address.")
349
350(defvar mh-scan-field-from-end-offset
351  (+ mh-scan-field-from-start-offset mh-scan-from-mbox-width)
352  "The offset from the `mh-cmd-note' to find the end of \"From:\" address.")
353
354(defvar mh-scan-field-subject-start-offset
355  (+ mh-scan-cmd-note-width
356     mh-scan-destination-width
357     mh-scan-date-width
358     mh-scan-date-flag-width
359     mh-scan-from-mbox-width
360     mh-scan-from-mbox-sep-width)
361  "The offset from the `mh-cmd-note' to find the start of the subject.")
362
363
364
365;;; Notation
366
367;; Alphabetical.
368
369(defvar mh-note-cur ?+
370  "The current message (in MH, not in MH-E) is marked by this character.
371See also `mh-scan-cur-msg-number-regexp'.")
372
373(defvar mh-note-copied ?C
374  "Messages that have been copied are marked by this character.")
375
376(defvar mh-note-deleted ?D
377  "Messages that have been deleted are marked by this character.
378See also `mh-scan-deleted-msg-regexp'.")
379
380(defvar mh-note-dist ?R
381  "Messages that have been redistributed are marked by this character.")
382
383(defvar mh-note-forw ?F
384  "Messages that have been forwarded are marked by this character.")
385
386(defvar mh-note-printed ?P
387  "Messages that have been printed are marked by this character.")
388
389(defvar mh-note-refiled ?^
390  "Messages that have been refiled are marked by this character.
391See also `mh-scan-refiled-msg-regexp'.")
392
393(defvar mh-note-repl ?-
394  "Messages that have been replied to are marked by this character.")
395
396(defvar mh-note-seq ?%
397  "Messages in a user-defined sequence are marked by this character.
398
399Messages in the \"search\" sequence are marked by this character as
400well.")
401
402
403
404;;; Utilities
405
406;;;###mh-autoload
407(defun mh-scan-msg-number-regexp ()
408  "Return value of variable `mh-scan-msg-number-regexp'."
409  mh-scan-msg-number-regexp)
410
411;;;###mh-autoload
412(defun mh-scan-msg-search-regexp ()
413  "Return value of variable `mh-scan-msg-search-regexp'."
414  mh-scan-msg-search-regexp)
415
416;;;###mh-autoload
417(defun mh-set-cmd-note (column)
418  "Set `mh-cmd-note' to COLUMN.
419Note that columns in Emacs start with 0."
420  (setq mh-cmd-note column))
421
422;;;###mh-autoload
423(defun mh-scan-format ()
424  "Return the output format argument for the scan program."
425  (if (equal mh-scan-format-file t)
426      (list "-format" (if (mh-variant-p 'nmh 'mu-mh)
427                          (list (mh-update-scan-format
428                                 mh-scan-format-nmh mh-cmd-note))
429                        (list (mh-update-scan-format
430                               mh-scan-format-mh mh-cmd-note))))
431    (if (not (equal mh-scan-format-file nil))
432        (list "-form" mh-scan-format-file))))
433
434(defun mh-update-scan-format (fmt width)
435  "Return a scan format with the (msg) width in the FMT replaced with WIDTH.
436
437The message number width portion of the format is discovered
438using `mh-scan-msg-format-regexp'. Its replacement is controlled
439with `mh-scan-msg-format-string'."
440  (or (and
441       (string-match mh-scan-msg-format-regexp fmt)
442       (let ((begin (match-beginning 1))
443             (end (match-end 1)))
444         (concat (substring fmt 0 begin)
445                 (format mh-scan-msg-format-string width)
446                 (substring fmt end))))
447      fmt))
448
449;;;###mh-autoload
450(defun mh-msg-num-width (folder)
451  "Return the width of the largest message number in this FOLDER."
452  (or mh-progs (mh-find-path))
453  (let ((tmp-buffer (get-buffer-create mh-temp-buffer))
454        (width 0))
455    (save-excursion
456      (set-buffer tmp-buffer)
457      (erase-buffer)
458      (apply 'call-process
459             (expand-file-name mh-scan-prog mh-progs) nil '(t nil) nil
460             (list folder "last" "-format" "%(msg)"))
461      (goto-char (point-min))
462      (if (re-search-forward mh-scan-msg-number-regexp nil 0 1)
463          (setq width (length (buffer-substring
464                               (match-beginning 1) (match-end 1))))))
465    width))
466
467;;;###mh-autoload
468(defun mh-msg-num-width-to-column (width)
469  "Return the column for notations given message number WIDTH.
470Note that columns in Emacs start with 0.
471
472If `mh-scan-format-file' is set to \"Use MH-E scan Format\" this
473means that either `mh-scan-format-mh' or `mh-scan-format-nmh' are
474in use. This function therefore assumes that the first column is
475empty (to provide room for the cursor), the following WIDTH
476columns contain the message number, and the column for notations
477comes after that."
478  (if (eq mh-scan-format-file t)
479      (max (1+ width) 2)
480    (error "%s %s" "Can't call `mh-msg-num-width-to-column' when"
481           "`mh-scan-format-file' is not set to \"Use MH-E scan Format\"")))
482
483(provide 'mh-scan)
484
485;; Local Variables:
486;; indent-tabs-mode: nil
487;; sentence-end-double-space: nil
488;; End:
489
490;; arch-tag: 5ab35d46-101e-443b-a2b6-5a908cf97528
491;;; mh-scan.el ends here
492