1 ;;; tinyprocmail.el --- Emacs procmail minor mode. Lint code checker.
3 ;; This file is not part of Emacs
7 ;; Copyright (C) 1997-2007 Jari Aalto
9 ;; Maintainer: Jari Aalto
11 ;; Keywords: extensions
13 ;; To get information on this program, call M-x tinyprocmail-version.
14 ;; Look at the code with folding.el.
16 ;; This program is free software; you can redistribute it and/or modify it
17 ;; under the terms of the GNU General Public License as published by the Free
18 ;; Software Foundation; either version 2 of the License, or (at your option)
21 ;; This program is distributed in the hope that it will be useful, but
22 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with program; see the file COPYING. If not, write to the
28 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 ;; Boston, MA 02110-1301, USA.
31 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
38 ;; ....................................................... &t-install ...
39 ;; Put this file on your Emacs-Lisp load path, add following into your
40 ;; $HOME/.emacs startup file. This must be the very first entry before
41 ;; any keybindings take in effect.
43 ;; ;; - Tell which procmail version you're using, see procmail -v
44 ;; ;; - Uf you do not set this, tinyprocmail.el will call shell
45 ;; ;; to find out the procmail version. (slower)
47 ;; (setq tinyprocmail-:procmail-version "v3.11pre7")
48 ;; (add-hook 'tinyprocmail-:load-hook 'tinyprocmail-install)
49 ;; (require 'tinyprocmail)
51 ;; You can also use the preferred way: autoload
53 ;; (autoload 'turn-on-tinyprocmail-mode "tinyprocmail" "" t)
54 ;; (autoload 'turn-off-tinyprocmail-mode "tinyprocmail" "" t)
55 ;; (autoload 'tinyprocmail-mode "tinyprocmail" "" t)
56 ;; (add-hook 'tinyprocmail-:load-hook 'tinyprocmail-install)
58 ;; ;; Procmail files usually end to suffix "*.rc", like file-name.rc
59 ;; ;; Some older procmail files start with "rc.*", like rc.file-name
61 ;; (autoload 'aput "assoc")
62 ;; (aput 'auto-mode-alist
63 ;; "\\.\\(procmail\\)?rc$"
64 ;; 'turn-on-tinyprocmail-mode)
66 ;; This source file includes sample procmail test file for Lint. You
67 ;; can unpack it if you have `pgp' and `tar' commands in your system.
68 ;; When the file has been unpacked, load pm-lint.rc file into buffer,
69 ;; and follow instructions in the file.
71 ;; M-x tinyprocmail-install-files
73 ;; If you have any questions, use this function to contact author
75 ;; M-x tinyprocmail-submit-bug-report
80 ;; ..................................................... &t-commentary ...
86 ;; Procmail may revolutionize your daily email management. If you
87 ;; receive more than 10 spam messages per day, you may start to wonder
88 ;; if there were any automatic way to handle mail, so that spam never
89 ;; lands on a $MAIL mailbox. Procmail can be one answer. It can be
90 ;; used to pre-filter all incoming mailing list messages and sort them
91 ;; out separately without bloating the primary inbox. You may already
92 ;; use Gnus to read the mailing lists, but the mail splitting work is
93 ;; best left to procmail. Why? Because procmail is always running,
94 ;; while your Emacs and Gnus isn't. Procmail processes incoming
95 ;; messages as soon as they are received and takes care of them, like
96 ;; saving UBE (unsolicited bulk email) messages to
97 ;; separate folders. And when Gnus us fired up, you can read the
98 ;; sorted mailboxes immediately.
102 ;; Procmail is a mail processing utility for Unix (included also
103 ;; in Win32/Cygwin), which can help you filter your mail;
104 ;; sort incoming mail according to sender, Subject line, length
105 ;; of message, keywords in the message, etc; implement an
106 ;; ftp-by-mail server, and much more. Procmail is also a
107 ;; complete drop-in replacement for your MDA. (If this doesn't
108 ;; mean anything to you, you don't want to know.) Learn more
109 ;; about procmail at <http://www.procmail.org/>.
113 ;; ._UBE_ = Unsolicited Bulk Email.
114 ;; ._UCE_ = (subset of UBE) Unsolicited Commercial Email.
116 ;; _Spam_ = Spam describes a particular kind of Usenet posting (and
117 ;; canned spiced ham), but is now often used to describe many kinds of
118 ;; inappropriate activities, including some email-related events. It
119 ;; is technically incorrect to use "spam" to describe email abuse,
120 ;; although attempting to correct the practice would amount to tilting
123 ;; Overview of features
125 ;; o Minor mode for writing procmail recipes (use tab for indenting)
126 ;; o Linting procmail code: from a batch command line or
127 ;; interactively. In interactive mode, user can auto-correct recipes
129 ;; o Font-lock supported.
130 ;; o files that have extension .rc or name .procmailrc trigger
131 ;; turning on `tinyprocmail-mode' (see `auto-mode-alist'). Please
132 ;; check that the first line does not have anything that would
133 ;; override this, like "-*- text -*-".
137 ;; o M-x `tinyprocmail-mode' toggles Procmail recipe write mode
138 ;; o C-c ' L to Lint whole buffer interactively
139 ;; o C-u C-c ' L to Lint whole buffer and gathers info.
140 ;; o C-u C-u C-c ' L, same as above, but be less pedantic.
144 ;; o tinylib*.el Emacs/XEmacs/Cygwin support libraries
145 ;; o tinytab.el General programming mode. TAB key handling
146 ;; o tinycompile.el General parser for compile output. See Lint.
148 ;; Writing procmail code
150 ;; The coding functions are provided by other modules. The tab key
151 ;; advances 4 characters at a time, and minimalistic brace alignment
152 ;; is supported when you press tab before the ending brace.
154 ;; TAB tinytab-tab-key tinytab.el
156 ;; The RET autoindents, but this can be turned off by calling
158 ;; C-c ' RET tinytab-return-key-mode
160 ;; Whole regions can be adjusted with commands
162 ;; C-TAB tinytab-indent-by-div-factor -->
163 ;; A-S-TAB tinytab-indent-by-div-factor-back <--
164 ;; C-c TAB tinytab-indent-region-dynamically <-->
168 ;; When the procmail mode is active, the tab key does not produce a
169 ;; tab character, but sufficient amount of spaces. There is a reason
170 ;; for this, mostly due to Lint parser which has to move up and down
171 ;; columnwise when checking the code. The movements can't be done if
172 ;; the code includes tabs. If you need a literal tab in your regexps,
173 ;; you can get it with standard emacs way 'C-q` `TAB'.
175 ;; Aligning continuation lines with backslashes
177 ;; In procmail, you use backslashes a lot, like in the following example.
178 ;; The backslashes here are put after each line, but this construct is
179 ;; error prone, because if you later on add new `echo' commands or
180 ;; otherwise modify the content, you may forget to update the
185 ;; | (formail -rt | \
187 ;; echo "Error: you requested file"; \
188 ;; echo "that does not exist";\
191 ;; To fix this code block, you can use command C-c ' \ or
192 ;; `tinyprocmail-fix-backslashes-paragraph'. It would have been
193 ;; enough to write the _first_ backslash and then call C-c ' \
194 ;; and the rest of the backslashes would have been added below
199 ;; | (formail -rt | \
201 ;; echo "Error: you requested file"; \
202 ;; echo "that does not exist"; \
205 ;; Rules on how to write Procmail recipe
207 ;; In order to use the linting service, this package requires that
208 ;; you write your procmail code in following manner. These rules are
209 ;; needed, so that it would be possible to parse the procmail code
210 ;; efficiently and more easily.
214 ;; : # (old way) although legal procmail, illegal here. Use `:0'
218 ;; In order to autocorrect read flags from the buffer, the flag order
219 ;; must be decided: and here is a suggestion. The one presented
220 ;; in the procmail man page "HBDAaEehbfcwWir" is errourneous, because
221 ;; flags "aAeE" must be first, otherwise it causes error in procmail.
222 ;; The idea here is that the most important flags are
223 ;; put to the left, like giving priority 1 for `aAeE', which affect
224 ;; the receipe immedately. Priority 2 has been given to flag `f',
225 ;; which tells if receipe filters somthing. Also (h)eader and (b)ody
226 ;; should immediately follow `f', this is considered priority 3.
227 ;; In the middle there are other flags, and last flag is `c', which
228 ;; ends the receipe, or allows it to continue."
230 ;; :0 aAeE HBD fhbwWirc: LOCKFILE
232 ;; | | | | (c)ontinue or (c)opy flag last.
233 ;; | | | (w)ait and Other flags
234 ;; | | (f)ilter flag and to filter what: (h)ead or (b)ody
235 ;; | (H)eader and (B)ody match, possibly case sensitive (D)
236 ;; The `process' flags first. Signify (a)ad or (e)rror
239 ;; Every recipe starts with `:0' `flags:', but if you prefer `:0flags:'
240 ;; more, you can use following statement. This 'flag-together (or not)
241 ;; format is automatically retained when everytime you call lint.
243 ;; (setq tinyprocmail-:flag-and-recipe-start-style 'flags-together)
247 ;; The lockfile names must be longer than two characters. Shorter
248 ;; lockfile name trigger an error. Also lockfile must have
249 ;; extension $LOCKEXT or .lock or .lck; no other non-standard
250 ;; extensions are allowed. The lockfile name must be within
251 ;; charsert [-_$.a-zA-Z0-9/] and anything else is considered as
252 ;; a suspicious lock file name.
254 ;; :0 : c # Error, should this read :0 c: instead?
255 ;; :0 : file # Invalid, should read "file.$LOCKEXT"
256 ;; :0 : file.tmp # Invalid, non-standard extension.
257 ;; :0 : file=0 # Invalid filename (odd characters in name)
261 ;; Do not add inline commnts immediately to the right of the
262 ;; condition line. Although never procmail reciped mail allow
263 ;; the comment below, this parser does not recognize it and
264 ;; will flag it as an error. There another reason to avoid writing
265 ;; the comment into condition lines: if recipe is in another system
266 ;; that does not have the most recent procmail, the recpipe will break.
267 ;; All in all, put comments *before* recipe, just below it.
269 ;; * H B ?? regexp # valid procmail, llegal here: write "HB"
273 ;; The literal value on the right-hand side must be quoted with
274 ;; double quotes if a simple string is being assigned. If there
275 ;; are no double or single quotes, then Lint assumes that you forgot
276 ;; to add variable dollar($). Try to avoid extra spaces in the
277 ;; variable initialisation construct that use `:-'.
279 ;; DUMMY = yes # Warning, did you mean DUMMY = $yes ?
280 ;; VAR = ${VAR:-1} # No spaces allowed: "$ {" is illegal.
284 ;; Program `sendmail' must be named sendmail, but it can also be
285 ;; variable $SENDMAIL. Similarly, program `formail' must be named
286 ;; `formail' or it can be variable $FORMAIL. Use of $MY_SENDMAIL
287 ;; or $MY_FORMAIL are illegal and cause missing many lint checks.
289 ;; [commenting style]
291 ;; In recent procmail releases you're allowed to place comments
292 ;; inside condition lines. Lint will issue a warning about this
293 ;; practise if your procmail version does not support this. But
294 ;; while you may place comments inside conditions, they should be
295 ;; indented by some amount of spaces. The default indent is 4
298 ;; * condition1 --> * condition
299 ;; # comment # comment
300 ;; # comment # comment
301 ;; * condition2 * condition
303 ;; This is recommended for readability (separating conditions
304 ;; from comments) and Lint will try to fix these comment misplacements.
306 ;; [redirecting to a file]
308 ;; If you print something to file, then the shell redirection
309 ;; tokens, like `>', must have surrounding spaces. Otherwise they
310 ;; are not found from the procmail recipe code. (because > can be used in
314 ;; | echo > test.tmp # Ok. Do not use "echo>test.tmp"
316 ;; Linting procmail code
318 ;; Writing procmail recipes is very demanding, because you have
319 ;; to watch your writing all the time. Forgetting a flag or two,
320 ;; or adding unnecessary flag may cause your procmail code to
321 ;; work improperly. The Lint interface in this module requires
324 ;; o You write your procmail code in certain way. (see above)
325 ;; o buffer is writable and can be modified. This is due
326 ;; to fact that program moves up and down to the same column
327 ;; as previous or next line. In order to make such movements,
328 ;; the tabs must be expanded when necessary.
330 ;; To help *Linting* you procmail code, there are two functions
332 ;; C-c ' l tinyprocmail-lint-forward
333 ;; C-c ' L tinyprocmail-lint-buffer
335 ;; These functions check every recipe and offer corrective
336 ;; actions if anything suspicious is found. If you don't want to
337 ;; correct the recipes, you can pass prefix argument, which
338 ;; gathers Lint run to separate buffer. In parentheses you see
339 ;; the buffer that was tested and to the right you see the
340 ;; program and version number. In this buffer you can press
341 ;; Mouse-2 or RET to jump to the line.
343 ;; *** 1997-10-19 19:37 (pm-test.rc) tinyprocmail.el 1.10
345 ;; pm-test.rc:02: Error, Invalid or extra flags.
346 ;; pm-test.rc:10: Error, Invalid or extra flags.
347 ;; pm-test.rc:10: info, Redundant `Wc:' because `c:' implies W.
348 ;; pm-test.rc:11: String `>' found, recipe should have `w' flag.
349 ;; pm-test.rc:15: info, flag `H' is useless, because it is default.
351 ;; The output buffer can be sorted and you can move between blocks
354 ;; sl tinyprocmail-output-sort-by-line
355 ;; se tinyprocmail-output-sort-by-error
356 ;; b tinyprocmail-output-start
357 ;; e tinyprocmail-output-end
359 ;; Lint: auto-correct
361 ;; In many cases the Lint functions are able to autocorrect the
362 ;; code: answer `y' to auto-correct question at current point. If
363 ;; you want to correct the place yourself, abort the Linting
364 ;; with `C-g' and fix the indicated line.
368 ;; Most of the time the Lint knows what might be best, but
369 ;; there may be cases where you have very complex procmail code
370 ;; and you know exactly what you want. Here are the Lint
371 ;; directives that you can place immediately before the recipe
372 ;; start to prevent Lint from whining. The word `Lint:' can have
373 ;; any number of surrounding spaces as long as it is the first
374 ;; word after comment.
376 ;; # Lint: <Lint flags here>
379 ;; The comment must be in the previous line, the following is _not_
382 ;; # Lint: <Lint flags here>
383 ;; # I'm doing some odd things here and ....
386 ;; Here is list of recognized Lint directives. each directive must have
389 ;; o `-w'. In pipe "|" recipe, ignore exit code. If you don't give
390 ;; this directive, the missing "w" flag is suggested to put there.
391 ;; o `-i'. If you have recipe that, 1) has no "f" 2) has no ">"
392 ;; 3) has "|" action, then the recipe doesn't seem to store
393 ;; the stdin anywhere. This may be valid case e.g. if you use
394 ;; MH's rcvstore. You can suppress the "-i" flag check with
396 ;; o `-c'. This is used in conjunction with `-i' when you only
397 ;; do something as a side effect and you reaally don't want to use
400 ;; Lint: error messages
402 ;; The error messages should be self-explanatory, but if you
403 ;; don't understand the message, please refer to *pm-tips.txt*
404 ;; file available at Sourceforge project "pm-doc".
405 ;; See `pm-tips.txt' and section that talks about variable
408 ;; Lint: batch mode from command line
410 ;; You can also lint procmail files from command line prompt
413 ;; % emacs -batch -q -eval \
414 ;; '(progn (load "cl") \
415 ;; (push "~/elisp" load-path) \
416 ;; (load "tinyprocmail" ) \
417 ;; (find-file "~/pm/pm-test.rc") \
418 ;; (tinyprocmail-lint-buffer-batch)) '
420 ;; Change the filename "~/pm/pm-test.rc" to targetted for
421 ;; linting. The Lint results will appear in file
422 ;; `tinyprocmail-:lint-output-file' which is ~/pm-lint.out by
423 ;; default. Below you see a shell script to run the above command
424 ;; more easily. Rip code with `ti::package-rip-magic'
427 ;;* # pm-lint.sh -- LINT A procmail batch lint with emacs tinyprocmail.el
432 ;;* out=$HOME/pm-lint.lst
434 ;;* # notice all these 3 lines must be concatenaed together! There must be
435 ;;* # no \ continuation characters. to the right.
437 ;;* $EMACS -batch -q -eval
438 ;;* '(progn (load "cl") (push "~/elisp" load-path) (load "tinyprocmail")
439 ;;* (find-file "'"$file"'") (tinyprocmail-lint-buffer-batch) ) ' 2>&1 $out
441 ;;* # end of pm-lint.sh
445 ;; Just couple of words about the chosen regexps for procmail code.
446 ;; Notice, that if you make a mistake, the dollar($) in front of
447 ;; identifier is not highlighted. This should help with spotting
448 ;; errors by eye better.
450 ;; $VAR = ${VAR:-"no"}
452 ;; |Error, you must not place '$' to the left here.
454 ;; This dollar($) sign is not highlighted.
466 (eval-when-compile (ti::package-use-dynamic-compilation))
469 (defvar tinytab-mode nil)
470 (autoload 'tinycompile-parse-line-goto "tinycompile" "" t)
471 (autoload 'turn-on-tinytab-mode "tinytab" "" t)
472 (autoload 'turn-off-tinytab-mode "tinytab" "" t)
473 (autoload 'tinytab-mode "tinytab" "" t)
474 (ti::overlay-require-macro
476 ** tinyprocmail.el: overlay-* functions missing from this Emacs.")))
478 (ti::package-defgroup-tiny TinyProcmail tinyprocmail-: extensions
479 "Procmail log minor mode
482 o Minor mode for writing Procmail recipes (use tab for
484 o Linting procmail code: From batch command line or
485 interactively. In interactive mode yuser can auto-correct code
486 on the fly. Linting erformance is about 160 recipes in 15 seconds.
487 o Font-lock supported.
488 o files that have extension .rc or name .procmailrc trigger
489 turning on `tinyprocmail-mode' (By using `auto-mode-alist'). Please
490 check that the first line does not have anything that would
491 override this, like '-*- text -*-'")
496 ;;; ......................................................... &v-hooks ...
498 (defcustom tinyprocmail-:load-hook nil
499 "*Hook run when file has been loaded."
501 :group 'TinyProcmail)
503 (defcustom tinyprocmail-:lint-before-hook nil
504 "*Hook run before `tinyprocmail-lint-forward'."
506 :group 'TinyProcmail)
508 (defcustom tinyprocmail-:lint-do-hook
509 '(tinyprocmail-lint-recipe-start
510 tinyprocmail-lint-condition-lines)
511 "List of lint functions to check the recipe at point.
516 STD-FLAGS Standardized flag sequence.
518 Function should offer fixing recipe if `tinyprocmail-:lint-fix-mode' is activated
519 and it should write log if `tinyprocmail-:lint' is nil."
520 :type '(repeat function)
521 :group 'TinyProcmail)
523 (defcustom tinyprocmail-:lint-after-hook nil
524 "*Hook run after `tinyprocmail-lint-forward'."
526 :group 'TinyProcmail)
528 (defcustom tinyprocmail-:lint-after-hook nil
529 "Hook run when `tinyprocmail-lint-forward' is about to finish."
531 :group 'TinyProcmail)
533 ;;; ..................................................... &v-functions ...
535 (defcustom tinyprocmail-:flag-format-function
536 'tinyprocmail-flag-format-default
537 "Function to format given flags.
538 This function standardizes the flag order by calling
539 `tinyprocmail-flag-standardize'.
541 It must also respect the value of `tinyprocmail-:flag-and-recipe-start-style':
542 e.g. if given 'Afbwic' the standard function adds one
543 leading space so that the recipe looks like ':0 Afbwic'"
545 :group 'TinyProcmail)
547 ;;; .......................................................... &public ...
549 (defcustom tinyprocmail-:pipe-w-warning-ignore-regexp
550 ".*|[ \t]*\\(echo\\|.*vacation\\)"
551 "When checking pipe recipe and missing w flags, ignore matching regexp.
552 Say you have the following recipe:
555 | echo \"status info\" > file
557 Then the \"w\" is not essential at all. The default rexgexp ignores all
558 these `echo' pipes and doesn't complaint about missing 'w'. Be carefull,
559 if you set this regexp, so that you don't miss important `w' warnings."
561 :group 'TinyProcmail)
563 (defcustom tinyprocmail-:flag-and-recipe-start-style nil
564 "What is the receipe start style used.
565 If 'flags-together, then the receipe start llike looks like
569 If any other value, then receipe start looks like
573 (const flags-together)
575 :group 'TinyProcmail)
578 (defun tinyprocmail-procmail-version ()
579 "Call `procmail -v' to find out the version number.
580 procmail v3.22 2001/09/10
581 procmail v3.11pre7 1997/04/28 written and created by Stephen R. van den Berg
582 procmail v3.11pre4 1995/10/29 written and created by Stephen R. van den Berg"
583 (let* ((prg (executable-find "procmail")))
586 ** tinyprocmail.el: Warning, couldn't auto-set `tinyprocmail-:procmail-version'.")
587 (ti::string-match "^procmail[ \t]+v?\\([0-9]+[^ \t\n]+\\)"
588 1 (shell-command-to-string "procmail -v"))))))
590 (defcustom tinyprocmail-:procmail-version (tinyprocmail-procmail-version)
591 "The version number returned by `procmail -v'."
593 :group 'TinyProcmail)
595 (defcustom tinyprocmail-:font-lock-keywords
597 ;; Seeing embedded tabs in procmail is crucial because
598 ;; procmail doesn't know [ \t]. This regexp highlights bracketed
599 ;; regexp if it contains tab
603 '("\\[[^]\n]*\t[^]\n]*\\]" . font-lock-keyword-face)
605 '("#.*" . font-lock-comment-face)
607 ;; The regexp says: Start with `:0' or `:' followed by spaces
608 ;; and characters, but you MUST ent with non-space. This matches
613 ;; But also `tight' flags
616 '("^[\t ]*\\(:0? *[ a-zA-Z]+[^ #\n]+\\)" 1 font-lock-type-face)
617 '("^[\t ]*\\(:0\\)" 1 font-lock-type-face)
618 ;; Special condiion line
621 '("^[ \t]*\\*.*\\<\\(HB\\|BH\\)\\>.*[?][?]" 1 font-lock-reference-face)
622 '("^[ \t]*\\*.*\\<\\([BH]\\)\\>.*[?][?]" 1 font-lock-reference-face)
623 ;; Special variable assignments
644 1 'font-lock-reference-face)
645 ;; variable expansion condition or variable extrapolation
651 '("\\* *\\([$]\\)" 1 font-lock-reference-face)
652 '("\\(\\$\\){" 1 font-lock-reference-face)
653 '("\\<\\(\\$\\)[A-Za-z]" 1 font-lock-reference-face)
654 ;; Left hand variable assignments
657 '("\\([A-Z_][A-Z0-9_]+\\)[\t ]*=" 1 font-lock-keyword-face)
658 ;; Lonely right hand variables
659 ;; $VAR = $RIGHT_HAND
660 '("\\${?\\([A-Z0-9_]+\\)}?\\>" 1 font-lock-keyword-face)
661 ;; Standard programs called
664 "[^_a-zA-Z0-9]\\(" ;; Do not put \\< here
679 1 'font-lock-reference-face)
680 ;; External shell calls with backquote
681 '("`\\([^' \t\n]+\\)'" 1 font-lock-reference-face t))
682 "*Font lock keywords."
684 :group 'TinyProcmail)
686 (defcustom tinyprocmail-:lint-font-lock-keywords
688 '("`\\([^' \t\n]+\\)'" 1 font-lock-reference-face)
689 '("Error," 0 font-lock-keyword-face)
690 '("Warning," 0 font-lock-reference-face)
691 ;; No there is no mistake here. The "i" is in lowercase because
692 ;; when the errors are sorted, the order of the sort must be like this.
698 '("info," 0 font-lock-comment-face))
699 "*Font lock keywords."
701 :group 'TinyProcmail)
703 (defcustom tinyprocmail-:auto-mode-alist
704 '(("\\.rc\\'\\|^rc\\.\\|procmailrc" . turn-on-tinyprocmail-mode))
705 "Items to add to `auto-mode-alist' to call `turn-on-tinyprocmail-mode'."
708 (regexp :tag "Regexp to match filename")
709 (const 'tinyprocmail-mode)))
712 (defcustom tinyprocmail-:lint-fix-mode 'semi
713 "*The mode of fixing code.
714 'auto Automatic fixing.
715 'semi Ask permission to fix.
721 :group 'TinyProcmail)
723 (defcustom tinyprocmail-:lint-log-verbose 'pedantic
724 "If nil, then do not log new features available only in latest procmail.
725 If 'pedantic, warn about all possible things that may not work in older
730 :group 'TinyProcmail)
732 (defcustom tinyprocmail-:lint-log nil
733 "*If non-nil receord lint check to `tinyprocmail-:lint-output-buffer'."
735 :group 'TinyProcmail)
740 ;;; ......................................................... &private ...
742 (defvar tinyprocmail-:overlay nil
745 (defvar tinyprocmail-:overlay-second nil
748 (defvar tinyprocmail-:lint-output-buffer "*Procmail Lint*"
749 "Log buffer for Lint.")
751 (defvar tinyprocmail-:lint-output-file "~/pm-lint.out"
752 "Where `tinyprocmail-lint-buffer-batch' should save the results.")
754 (defvar tinyprocmail-:mode-output-map nil
755 "Map useed in `tinyprocmail-:lint-output-buffer'.")
757 (defvar tinyprocmail-:mode-output-easymenu nil
758 "Ooutput mode menu.")
761 (ti::macrof-version-bug-report
764 tinyprocmail-:version-id
765 "$Id: tinyprocmail.el,v 2.51 2007/05/01 17:20:53 jaalto Exp $"
766 '(tinyprocmail-:version-id)))
770 ;;; ############################################################ &mode ###
774 ;;;###autoload (autoload 'tinyprocmail-mode "tinyprocmail" "" t)
775 ;;;###autoload (autoload 'turn-on-tinyprocmail-mode "tinyprocmail" "" t)
776 ;;;###autoload (autoload 'turn-off-tinyprocmail-mode "tinyprocmail" "" t)
777 ;;;###autoload (autoload 'tinyprocmail-commentary "tinyprocmail" "" t)
778 ;;;###autoload (autoload 'tinyprocmail-version "tinyprocmail" "" t)
782 (ti::macrof-minor-mode-wizard
783 "tinyprocmail-" " PM" "\C-c'" "PM" 'TinyProcmail "tinyprocmail-:"
784 "Procmail coding minor mode.
786 Code writing: `tinytab-mode' on \\[tinytab-mode]
788 Mode description (main mode)
789 \\{tinyprocmail-:mode-prefix-map}"
791 "Procmail recipe coding"
793 (progn ;Some mode specific things? No?
796 (setq comment-start "#"
797 comment-start-skip "#+[ \t]*"
799 indent-tabs-mode nil)
800 ;; Should we turn on the font-lock me too?
801 ;; global-font-lock-mode is available in new emacs versions.
803 ;; Font-lock must be turned on FIRST, then set
804 ;; `font-lock-keywords'
805 (font-lock-mode-maybe 1)
806 (tinyprocmail-font-lock-keywords
807 tinyprocmail-:font-lock-keywords 'mode-font-lock)
808 (put 'tinyprocmail-mode 'tit tinytab-mode) ;Save previous
810 (turn-on-tinytab-mode)))
812 (tinyprocmail-font-lock-keywords
813 tinyprocmail-:font-lock-keywords 'mode-font-lock 'restore)
814 (if (and tinytab-mode (get 'tinyprocmail-mode 'tit))
815 (turn-off-tinytab-mode))
816 (tinyprocmail-overlay-hide))))
820 tinyprocmail-:mode-easymenu-name
821 ["Forward" tinyprocmail-forward t]
822 ["Backward" tinyprocmail-backward t]
823 ["Forward strict" tinyprocmail-forward-strict t]
824 ["Backward strict" tinyprocmail-backward-strict t]
826 ["Lint forward" tinyprocmail-lint-forward t]
827 ["Lint buffer" tinyprocmail-lint-buffer t]
828 ["Lint buffer and save output" tinyprocmail-lint-buffer-batch t]
829 ["Lint output, buffer display" tinyprocmail-output-display t]
830 ["Lint output, buffer clear" tinyprocmail-output-clear t]
831 ["Lint output, kill file" tinyprocmail-output-file-kill t]
835 ["Hide region" tinyprocmail-hide-comment-text-region t]
836 ["Show region" tinyprocmail-show-comment-text-region t]
837 ["Hide recipe" tinyprocmail-hide-comment-text-recipe t]
838 ["Show recipe" tinyprocmail-show-comment-text-recipe t])
841 ["Return key toggle" tinytab-return-key-mode t]
842 ["Fix backslashes at point" tinyprocmail-fix-backslashes-paragraph t]
843 ["Package version" tinyprocmail-version t]
844 ["Package commentary" tinyprocmail-commentary t])
846 ["Mode off" turn-off-tinyprocmail-mode t])
848 (define-key map "\\" 'tinyprocmail-fix-backslashes-paragraph)
849 (define-key map "?" 'tinyprocmail-mode-help)
850 (define-key map "Hm" 'tinyprocmail-mode-help)
851 (define-key map "Hc" 'tinyprocmail-commentary)
852 (define-key map "Hv" 'tinyprocmail-version)
853 (define-key root-map "\C-n" 'tinyprocmail-forward)
854 (define-key root-map "\C-p" 'tinyprocmail-backward)
855 (define-key root-map [(end)] 'tinyprocmail-forward)
856 (define-key root-map [(home)] 'tinyprocmail-backward)
857 (define-key root-map [(control end)] 'tinyprocmail-forward-strict)
858 (define-key root-map [(control home)] 'tinyprocmail-backward-strict)
859 (define-key map "_" 'tinyprocmail-hide-comment-text-region)
860 (define-key map "-" 'tinyprocmail-show-comment-text-region)
861 (define-key map ":" 'tinyprocmail-hide-comment-text-recipe)
862 (define-key map "." 'tinyprocmail-show-comment-text-recipe)
863 (define-key map "l" 'tinyprocmail-lint-forward)
864 (define-key map "L" 'tinyprocmail-lint-buffer)
865 (define-key map "r" 'tinyprocmail-standardize-recipe-start)
866 (define-key map "\C-m" 'tinytab-return-key-mode)
867 ;; Uppercase to prevent from errors.
868 (define-key map "B" 'tinyprocmail-lint-buffer-batch)
869 (define-key map "K" 'tinyprocmail-output-file-kill)
870 (define-key map "d" 'tinyprocmail-output-display)
871 (define-key map "c" 'tinyprocmail-output-clear)
872 (define-key map "o" 'tinyprocmail-overlay-hide)
873 (define-key map "?" 'tinyprocmail-describe-mode))))
875 ;;;###autoload (autoload 'tinyprocmail-output-mode "tinyprocmail" "" t)
876 ;;;###autoload (autoload 'turn-on-tinyprocmail-output-mode "tinyprocmail" "" t)
877 ;;;###autoload (autoload 'turn-off-tinyprocmail-output-mode "tinyprocmail" "" t)
881 (ti::macrof-minor-mode-wizard
882 "tinyprocmail-output-" " PM-Lint" "\C-c'" "PM-Lint"
883 'TinyProcmail "tinyprocmail-output-:"
885 "Browsing Procmail lint output. See \\[tinyprocmail-lint]
889 \\{tinyprocmail-output-:mode-prefix-map}"
892 (when (and (interactive-p) ;On when user calls us directly
893 ;; Mode is Now turned on, check Lint buffer and confirm
894 tinyprocmail-output-mode
895 (null (ti::re-search-check (concat "^" (regexp-quote "*** "))))
897 "No Prcomail Lint output found. Are you sure? ")))
898 (setq tinyprocmail-output-mode nil)
900 (tinyprocmail-font-lock-keywords
901 tinyprocmail-:lint-font-lock-keywords 'lint-font-lock))
902 "Procmail Lint output mode"
904 tinyprocmail-output-:mode-easymenu-name
905 ["Find error" tinycompile-parse-line-goto t]
906 ["Beginning of output" tinyprocmail-output-start t]
907 ["End of output" tinyprocmail-output-end t]
908 ["Save output" tinyprocmail-output-file-save t]
909 ["Clear output" tinyprocmail-output-clear t]
911 ["Sort by line number" tinyprocmail-output-sort-by-line t]
912 ["Sort by error" tinyprocmail-output-sort-by-error t]
914 ["Mode on for all pm buffers" turn-on-tinyprocmail-mode-all-buffers t]
915 ["Mode off for all pm buffers" turn-off-tinyprocmail-mode-all-buffers t]
916 ["Mode help" tinyprocmail-output-mode-help t]
917 ["Mode off" tinyprocmail-output-mode t])
920 (define-key root-map [mouse-2] 'tinycompile-parse-line-goto)
921 (define-key root-map [(button2)] 'tinycompile-parse-line-goto))
922 ;; Define keys like in Compile
923 (define-key map "\C-c\C-c" 'tinycompile-parse-line-goto)
924 (define-key map "\C-m" 'tinycompile-parse-line-goto)
925 (define-key map "b" 'tinyprocmail-output-start)
926 (define-key map "e" 'tinyprocmail-output-end)
927 (define-key map "sl" 'tinyprocmail-output-sort-by-line)
928 (define-key map "se" 'tinyprocmail-output-sort-by-error)
929 (define-key map "S" 'tinyprocmail-output-file-save)
930 (define-key map "C" 'tinyprocmail-output-clear)
931 (define-key map "?" 'tinyprocmail-output-mode-help))))
933 ;;; ......................................................... &install ...
935 ;;; ----------------------------------------------------------------------
937 ;;;###autoload (autoload 'tinyprocmail-install-files "tinyprocmail" t t)
938 (ti::macrof-install-pgp-tar tinyprocmail-install-files "tinyprocmail.el")
940 ;;; ----------------------------------------------------------------------
943 (defun turn-off-tinyprocmail-mode-all-buffers (&optional verb)
944 "Call `turn-on-tinyprocmail-mode-all-buffers' with parameter `off'. VERB."
947 (turn-on-tinyprocmail-mode-all-buffers 'on verb))
949 ;;; ----------------------------------------------------------------------
952 (defun turn-on-tinyprocmail-mode-all-buffers (&optional off verb)
953 "Turn on or OFF function `tinyprocmail-mode' for all procmail buffers. VERB.
954 Procmail files start with `rc.' or end to `.rc' and file content
958 (dolist (buffer (buffer-list))
959 (with-current-buffer buffer
960 (when (and (if off tinyprocmail-mode (null tinyprocmail-mode))
963 "^rc\\.\\|\\.rc$\\|procmailrc"
965 (ti::re-search-check "^:0"))
966 (if verb (message "TinyProcmail: Mode turned %s in %s"
967 (if off "off" "on") (buffer-name)))
969 (turn-off-tinyprocmail-mode)
970 (turn-on-tinyprocmail-mode))))))
972 ;;; ----------------------------------------------------------------------
974 (defun tinyprocmail-install-auto-mode-alist (&optional uninstall)
975 "Update `auto-mode-alist' to know about procmail *.rc files."
978 (ti::assoc-replace-maybe-add
980 tinyprocmail-:auto-mode-alist
983 (ti::assoc-replace-maybe-add
985 tinyprocmail-:auto-mode-alist)
987 (message "TinyProcmail: uninstalled")))))
989 ;;; ----------------------------------------------------------------------
991 (defun tinyprocmail-install (&optional uninstall verb)
992 "Install package, or optionally UNINSTALL. VERB."
995 ;; It is crucial that these two hooks are in this order
996 ;; First runs `tinyprocmail-lint-malformed-start-recipe' and only after
997 ;; that the receipes can be found.
998 (ti::add-hooks 'tinyprocmail-:lint-before-hook
999 '(tinyprocmail-standardize-recipe-start
1000 tinyprocmail-lint-malformed-start-recipe)
1002 (ti::add-hooks 'tinyprocmail-:lint-after-hook
1003 '(tinyprocmail-lint-malformed-var-defs
1004 tinyprocmail-lint-malformed-misc
1005 tinyprocmail-lint-malformed-brace
1006 tinyprocmail-lint-find-wrong-escape-codes
1007 tinyprocmail-lint-find-2spaces
1008 tinyprocmail-lint-list-lint-directives)
1010 (tinyprocmail-install-auto-mode-alist uninstall)
1011 (turn-on-tinyprocmail-mode-all-buffers uninstall verb))
1013 ;;; ----------------------------------------------------------------------
1015 (defun tinyprocmail-find-file-hook ()
1016 "Turn on tipm mode if you're viewing log file."
1017 (when (and (not tinyprocmail-mode)
1018 (ti::re-search-check "^:0$")) ;It's procmail ok
1019 (tinyprocmail-mode 1)))
1023 ;;; ########################################################### ¯o ###
1027 ;;; ----------------------------------------------------------------------
1029 (defmacro tinyprocmail-o (&rest body)
1030 "Move overlay to point and protect BODY. Overlay is hiddedn after body."
1034 (tinyprocmail-overlay (point))
1036 (tinyprocmail-overlay-hide))))
1038 ;;; ----------------------------------------------------------------------
1040 (defmacro tinyprocmail-output-macro (&rest body)
1041 "Go to `tinyprocmail-:lint-output-buffer' and do BODY.
1042 If buffer does not exist, do nothing."
1044 (let ((buffer (get-buffer tinyprocmail-:lint-output-buffer)))
1046 (with-current-buffer buffer
1049 ;;; ----------------------------------------------------------------------
1051 (put 'tinyprocmail-fix-macro 'lisp-indent-function 1)
1052 (defmacro tinyprocmail-fix-macro (message &rest body)
1053 "Fix. Display MESSAGE and do BODY."
1055 (when (or (eq tinyprocmail-:lint-fix-mode 'auto)
1056 (and (eq tinyprocmail-:lint-fix-mode 'semi)
1057 (tinyprocmail-o (y-or-n-p (, message)))))
1060 ;;; ----------------------------------------------------------------------
1061 ;; fmacro = function create macro
1063 (defmacro tinyprocmail-fmacro-move (back method)
1064 "Make move function using BACK and METHOD."
1066 (format "tinyprocmail-%s-%s"
1070 (symbol-name (` (, method)))))))
1074 (tinyprocmail-forward (quote (, back)) (quote (, method)))))))
1079 ;;; ............................................................. misc ...
1081 ;;; ----------------------------------------------------------------------
1083 (defsubst tinyprocmail-comment-line-p ()
1084 "Check if this ine is full comment line. Use `save-excursion'."
1087 (looking-at "^[ \t]*#")))
1089 ;;; ----------------------------------------------------------------------
1091 (defsubst tinyprocmail-comment-line-pp ()
1092 "Check if this ine is full comment line at current point forward."
1093 (looking-at "^[ \t]*#"))
1095 ;;; ----------------------------------------------------------------------
1097 (defsubst tinyprocmail-string-valid-p (string &optional type)
1098 "Check is STRING is valid variable. Find any supicious character.
1100 STRING variable or read filename.
1101 TYPE if 'path; then check as path."
1104 (string-match "^[-_a-zA-Z0-9.$\\/@]+$" string))
1106 (string-match "^[_a-zA-Z0-9]+$" string))))
1108 ;;; ----------------------------------------------------------------------
1110 (defun tinyprocmail-supported-p (feature)
1111 "Check if FEATURE is supported by `tinyprocmail-:procmail-version'."
1112 (let* ((v (or tinyprocmail-:procmail-version "")))
1114 ((eq feature 'condition-middle-comment)
1115 (string-match "3.11pre7" v))
1116 ((error "Invalid feature %s" feature)))))
1118 ;;; ----------------------------------------------------------------------
1120 (defun tinyprocmail-lint-directive-1 (&optional start-point)
1121 "Search Lint directive line backward from current point or START-POINT.
1122 # Ignore checking of mising -w flag in this case
1129 str directive flags."
1131 (if start-point (goto-char start-point))
1133 (when (and (re-search-backward "^[ \t]*:0" nil t)
1135 (looking-at "^[ \t]*#[ \t]*Lint:\\(.*\\)"))
1138 ;;; ----------------------------------------------------------------------
1140 (defsubst tinyprocmail-lint-directive-p (directive directive-flags)
1141 "Search Lint DIRECTIVE from DIRECTIVE-FLAGS."
1142 (when (stringp directive-flags)
1143 (string-match (concat " " (regexp-quote directive)) directive-flags)))
1145 ;;; ----------------------------------------------------------------------
1147 (defun tinyprocmail-overlay (point)
1148 "Move overlay to POINT."
1149 (when (and (fboundp 'make-overlay)
1150 (fboundp 'move-overlay)
1151 (fboundp 'overlay-put))
1152 (or tinyprocmail-:overlay
1153 (setq tinyprocmail-:overlay (make-overlay 1 1)))
1154 (or tinyprocmail-:overlay-second
1155 (setq tinyprocmail-:overlay-second (make-overlay 1 1)))
1156 (dolist (elt '((owner tipm)
1159 (before-string ">")))
1160 (multiple-value-bind (property value) elt
1161 (overlay-put tinyprocmail-:overlay property value)
1162 (if (eq property 'before-string)
1163 (overlay-put tinyprocmail-:overlay-second
1164 'after-string " <<"))))
1167 (move-overlay tinyprocmail-:overlay
1168 (line-beginning-position)
1171 (goto-char (line-end-position))
1172 (move-overlay tinyprocmail-:overlay
1173 (line-beginning-position)
1175 (current-buffer)))))
1177 ;;; ----------------------------------------------------------------------
1179 (defun tinyprocmail-overlay-hide ()
1180 "Move overlay out of sight."
1182 (when (fboundp 'move-overlay)
1183 (dolist (ov '(tinyprocmail-:overlay tinyprocmail-:overlay-second))
1184 (when (and (boundp ov)
1185 (setq ov (symbol-value ov)))
1186 (move-overlay ov 1 1)
1187 (overlay-put ov 'before-string "")
1188 (overlay-put ov 'after-string "")))))
1190 ;;; ----------------------------------------------------------------------
1192 (defun tinyprocmail-font-lock-keywords (keywords property &optional restore)
1193 "Use font-lock KEYWORDS and store original to PROPERTY. RESTORE original."
1194 (let ((sym 'font-lock-keywords))
1197 (set sym (get 'tinyprocmail-mode property))
1198 (put 'tinyprocmail-mode property (symbol-value sym))
1199 (set sym keywords)))))
1201 ;;; ----------------------------------------------------------------------
1203 (defun tinyprocmail-log (point string &optional point-min)
1204 "Log POINT and STRING to lint buffer if `tinyprocmail-:lint' is non-nil.
1208 POINT The current error point
1209 POINT-MIN Where is the logical `point-min' which we use to count
1210 the line numbers. Defualts to (point-min) but in case
1211 you're using narrow, this should be `point-min' before
1212 narrowing to check the recipe condition."
1213 (when tinyprocmail-:lint-log
1214 ;; Some safety measures
1215 (when (and point (not (integerp point)))
1216 (error "arg POINT is not integer"))
1217 (when (and string (not (stringp string)))
1218 (error "arg STRING is not stringp"))
1219 (let* ((buffer (get-buffer-create tinyprocmail-:lint-output-buffer))
1220 (name (buffer-name))
1225 (or point-min (point-min))
1229 ;;; (if (eq point 4266) (ti::d! LINE point point-min (bolp) ))
1230 (with-current-buffer buffer
1232 (if (and point string)
1239 (insert string))))))
1241 ;;; ----------------------------------------------------------------------
1243 (defun tinyprocmail-log-start ()
1244 "Start log by adding header into list log buffer."
1250 "*** %s (%s) %s tinyprocmail.el %s\n%s")
1251 (ti::date-standard-date 'minutes)
1253 (or tinyprocmail-:procmail-version "")
1254 (ti::string-match "[0-9][0-9.]+" 0 tinyprocmail-:version-id)
1256 (if (buffer-file-name)
1257 (concat "cd " (file-name-directory (buffer-file-name)) "\n")
1260 ;;; ----------------------------------------------------------------------
1262 (defsubst tinyprocmail-recipe-start-p (&optional line)
1263 "Check if current LINE is recipe start line."
1265 (string-match "^[ \t]*:0" line)
1268 (looking-at "^[ \t]*:0"))))
1270 ;;; ----------------------------------------------------------------------
1272 (defun tinyprocmail-condition-line-p (&optional line)
1273 "Check if current LINE is condition line."
1275 (string-match "^[ \t]*\\*" line)
1278 (or (looking-at "^[ \t]*\\*")
1280 ;; Peek previous line
1283 ;; line-end << suppose point is here
1285 (looking-at "^[ \t*]+.*[\\]"))))))
1287 ;;; ----------------------------------------------------------------------
1289 (defun tinyprocmail-assignment-line-p (&optional line)
1290 "Check if current LINE has assignment."
1292 (string-match "^[^=]+=" line)
1295 (or (looking-at "^[^=]+=")
1297 ;; Peek previous line
1300 ;; line-end << suppose point is here
1302 (or (looking-at "^[^=]+=.*[\\][ \t]*$")
1304 (tinyprocmail-skip-continuation-backward)
1306 (looking-at "^[^=]+="))))))))
1308 ;;; ----------------------------------------------------------------------
1310 (defmacro tinyprocmail-flag-string ()
1311 "Return base flag string."
1312 (` "aAeEHBDfhbwWirc:"))
1314 ;;; ----------------------------------------------------------------------
1316 (defsubst tinyprocmail-flag-p (char-string)
1317 "Check if one CHAR-STRING is valid flag."
1318 (ti::string-match-case char-string (tinyprocmail-flag-string)))
1320 ;;; ----------------------------------------------------------------------
1322 (defsubst tinyprocmail-recipe-start-require ()
1323 "Flag error if not at recipe start."
1324 (unless (tinyprocmail-recipe-start-p)
1325 (error "Not recipe start line")))
1327 ;;; ----------------------------------------------------------------------
1329 (defun tinyprocmail-brace-p (&optional line)
1330 "Check if cursor is under brace or in brace LINE. Return 'beg, 'end or nil."
1336 ((looking-at "[ \t]*{")
1338 ((looking-at "[ \t]*}")
1341 (or (if (char= (following-char) ?{) 'beg)
1342 (if (char= (following-char) ?}) 'end)))))
1344 ;;; ----------------------------------------------------------------------
1346 (defun tinyprocmail-action-line-ok-p ()
1347 "Point must be at the end of conditions.
1348 This checks if the actions line is ok."
1350 (let* ((opoint (point))
1352 ;; Suppose there is no action line at all
1358 ;; :0[*] point is somewhere here, previous condition end.
1360 ;; - Eat all newlines and comments backward
1361 (setq point (point))
1362 (if (tinyprocmail-recipe-start-p)
1364 (when (tinyprocmail-skip-comments-backward)
1365 (if (tinyprocmail-condition-line-p)
1367 (setq point (point)))
1369 (skip-chars-forward " \t")
1371 ;; :0 :0 :0 :0 :0 :0
1372 ;; ! | { mbox /dev/null $mbox
1374 (when (not (looking-at "[|!/][^#\n]+\\|[$]?[A-Z]\\|{"))
1378 ;;{{{ move: primitives
1380 ;;; ----------------------------------------------------------------------
1382 (defun tinyprocmail-next-empty-line ()
1383 "Return point of next empty code line."
1384 (let* ((list (ti::re-search-point-list
1385 '("^[ \t]*$" "^[ \t]*#")
1386 'beginning-of-line)))
1388 (apply 'min list))))
1390 ;;; ----------------------------------------------------------------------
1392 (defun tinyprocmail-skip-regexp (regexp &optional backward)
1393 "Skip all lines that match `looking-at' REGEXP. Optionally BACKWARD."
1395 (while (and (not (eobp))
1396 (looking-at regexp))
1403 (skip-chars-backward " \t")
1404 ;; Go to first char in the line
1405 (skip-chars-forward " \t")))))
1407 ;;; ----------------------------------------------------------------------
1409 (defun tinyprocmail-skip-comments-forward ()
1410 "Skip all comments and whitespace lines."
1411 (tinyprocmail-skip-regexp "[ \t]*#\\|[ \t]*$"))
1413 ;;; ----------------------------------------------------------------------
1415 (defun tinyprocmail-skip-comments-backward ()
1416 "Skip all comments and whitespace lines."
1417 (tinyprocmail-skip-regexp "[ \t]*#\\|[ \t]*$" 'back))
1419 ;;; ----------------------------------------------------------------------
1421 (defun tinyprocmail-skip-continuation-forward ()
1422 "Skip lines that end to backslash."
1423 (tinyprocmail-skip-regexp ".*[\\][ \t]*$"))
1425 ;;; ----------------------------------------------------------------------
1427 (defun tinyprocmail-skip-continuation-backward ()
1428 "Skip lines that end to backslash."
1429 (tinyprocmail-skip-regexp ".*[\\][ \t]*$" 'back))
1431 ;;; ----------------------------------------------------------------------
1433 (defun tinyprocmail-move-to-next-condition-line ()
1434 "Move to next line in condition.
1436 t Sitting on condition line after move.
1437 nil Not a condition line after move"
1438 (let* ((cont-p (looking-at ".*[\\][ \t]*$")))
1439 (if (or (looking-at ":0")
1443 (looking-at "^[ \t]*:")))
1446 (when (and (not (looking-at "[ \t]*\\*")) ;; Beginning of recipe
1448 (forward-line -1) ;Start from original line
1449 (inline (tinyprocmail-skip-continuation-forward))
1452 ;; # comment inside condition
1453 ;; * another condition.
1454 (when (looking-at "[ \t]*#")
1455 (inline (tinyprocmail-skip-comments-forward))))
1456 (skip-chars-forward " \t")
1457 (looking-at "[ \t]*\\*")))
1459 ;;; ----------------------------------------------------------------------
1461 (defun tinyprocmail-move-to-condition-end ()
1462 "Go to condition end. Point must inside condition or recipe start."
1465 ((tinyprocmail-brace-p)
1467 ((and (tinyprocmail-recipe-start-p)
1470 (not (looking-at "^[ \t]*\\*"))))
1471 ;; Handle special case
1476 (skip-chars-forward " \t"))
1478 (while (and (not (eobp))
1479 (or (tinyprocmail-move-to-next-condition-line)
1480 ;; - count empty lines too. This is usually users
1481 ;; mistake and error in procmail, but
1482 ;; we must find condition end.
1483 ;; - We accept only _1_ empty line between conditions
1484 ;; Other than that must be real big error.
1492 (and (looking-at "^[ \t]*$")
1495 ;; Must be condition line
1496 (looking-at "[ \t]*\\*")))))
1497 (skip-chars-forward " \t"))))))
1499 ;;; ----------------------------------------------------------------------
1501 (defun tinyprocmail-move-to-macthing-brace (&optional no-adjust)
1502 "Go to { or } brace when sitting on } or {.
1504 NO-ADJUST If non-nil, when goind to ending brace, do not
1505 put cursor on brace, but to next line."
1506 (unless (get 'tinyprocmail-:mode-name 'syntax-table)
1507 (let* ((otable (syntax-table))
1509 (modify-syntax-entry ?{ "(" table)
1510 (modify-syntax-entry ?} ")" table)
1511 (put 'tinyprocmail-:mode-name 'syntax-table table)))
1513 (let* ((otable (syntax-table)))
1514 (set-syntax-table (get 'tinyprocmail-:mode-name 'syntax-table))
1517 ((char= (following-char) ?{)
1525 (set-syntax-table otable))))
1527 ;;; ----------------------------------------------------------------------
1529 (defun tinyprocmail-move-to-recipe-end ()
1530 "Go to recipe block end. Return recipe bound: (beg . end) ."
1532 (if (not (tinyprocmail-recipe-start-p))
1533 (tinyprocmail-backward-strict))
1536 (tinyprocmail-move-to-condition-end)
1537 (if (tinyprocmail-brace-p)
1538 (tinyprocmail-move-to-macthing-brace 'no-adjust)
1539 (or (re-search-forward "^[ \t]*$" nil t)
1540 (error "TinyProcmail: Can't find recipe end.")))
1541 (cons beg (point))))
1544 ;;{{{ move: interactive
1546 ;;; ............................................................ &move ...
1548 (tinyprocmail-fmacro-move nil strict)
1549 (tinyprocmail-fmacro-move back strict)
1551 ;;; ----------------------------------------------------------------------
1553 (defun tinyprocmail-fix-backslashes-paragraph ()
1554 "Fix backslashes at point"
1556 (ti::buffer-backslash-fix-paragraph))
1558 ;; ----------------------------------------------------------------------
1560 (defun tinyprocmail-backward (&optional method verb)
1561 "Find previous procmail recipe. See METHOD in `tinyprocmail-forward'. VERB."
1564 (tinyprocmail-forward 'back method verb))
1566 ;;; ----------------------------------------------------------------------
1568 (defun tinyprocmail-forward (&optional back method verb)
1569 "Find next procmail recipe.
1573 BACK Search backward
1574 METHOD if 'any then search commented recipe too.
1575 if 'strict then only left flushed recipes.
1576 nil searches ^WHITESPACE:
1581 nil or non-nil if moved."
1583 (let* ((opoint (point))
1585 ((eq method 'strict)
1590 "^[ \t]*\\(:\\)"))))
1595 (if (re-search-backward re nil t)
1596 (goto-char (match-beginning 1))
1598 (message "TinyProcmail: No more recipes backward."))
1603 (if (re-search-forward re nil t)
1604 (goto-char (match-beginning 1))
1606 (message "TinyProcmail: No more recipes forward."))
1613 ;;; ............................................................ &hide ...
1615 ;;; ----------------------------------------------------------------------
1617 (defun tinyprocmail-hide-comment-text-region (beg end &optional show)
1618 "In region BEG END, hide or SHOW comment text ."
1619 (interactive "r\nP")
1620 (ti::narrow-safe beg end
1621 (goto-char (min beg end))
1622 (setq show (not show))
1623 (with-buffer-modified
1624 (while (re-search-forward "#.*" nil t)
1625 (add-text-properties
1626 (1+ (match-beginning 0)) (match-end 0)
1627 (list 'invisible show))))))
1629 ;;; ----------------------------------------------------------------------
1631 (defun tinyprocmail-show-comment-text-recipe ()
1632 "See `tinyprocmail-hide-comment-text-recipe'."
1634 (tinyprocmail-hide-comment-text-recipe 'show))
1636 ;;; ----------------------------------------------------------------------
1638 (defun tinyprocmail-hide-comment-text-recipe (&optional show)
1639 "Hide or SHOW comment text in current recipe. point must be in recipe."
1641 (let* ((region (save-excursion (tinyprocmail-move-to-recipe-end))))
1642 (tinyprocmail-hide-comment-text-region
1643 (car region) (cdr region) show)))
1645 ;;; ----------------------------------------------------------------------
1647 (defun tinyprocmail-show-comment-text-region (beg end)
1648 "See `tinyprocmail-hide-comment-text-region'. Use Region BEG END."
1650 (tinyprocmail-hide-comment-text-region beg end 'show))
1655 ;;; .......................................................... &output ...
1657 ;;; ----------------------------------------------------------------------
1659 (defun tinyprocmail-output-end ()
1660 "Go to endline of output block."
1662 (if (looking-at "^[ \t]*$")
1663 (skip-chars-forward " \t\n"))
1664 (re-search-forward "^[ \t]*$"))
1666 ;;; ----------------------------------------------------------------------
1668 (defun tinyprocmail-output-start ()
1669 "Go to start line of output block."
1671 (unless (re-search-backward "^\\*\\*")
1672 (error "Invalid buffer format, No ** found.")))
1674 ;;; ----------------------------------------------------------------------
1676 (defun tinyprocmail-output-line-start ()
1677 "Go to start of first code line."
1678 (tinyprocmail-output-start)
1680 (if (looking-at "cd \\([^ \t\n]+\\)")
1684 ;;; ----------------------------------------------------------------------
1686 (defun tinyprocmail-output-region ()
1687 "Return output region block '(beg . end)."
1689 (let* ((beg (tinyprocmail-output-line-start)))
1690 (tinyprocmail-output-end)
1691 (cons beg (point)))))
1693 ;;; ----------------------------------------------------------------------
1695 (defun tinyprocmail-output-sort-by-error (&optional reverse)
1696 "Sort block by error. Optionally REVERSE."
1698 (let* ((region (tinyprocmail-output-region)))
1699 (ti::save-line-column-macro nil nil
1702 "^[^:]+:[^:]+:\\([^,]+\\).*$" "\\1"
1706 ;;; ----------------------------------------------------------------------
1708 (defun tinyprocmail-output-sort-by-line (&optional reverse)
1709 "Sort block by line number. Optionally REVERSE."
1711 (let* ((region (tinyprocmail-output-region)))
1712 (ti::save-line-column-macro nil nil
1713 (sort-lines reverse (car region) (cdr region)))))
1715 ;;; ----------------------------------------------------------------------
1717 (defun tinyprocmail-output-file-save (&optional file)
1718 "Write `tinyprocmail-:lint-output-buffer' to `tinyprocmail-:lint-file' using FILE."
1721 (if (null (ti::set-buffer-safe tinyprocmail-:lint-output-buffer))
1722 (error "No `%s' buffer found." tinyprocmail-:lint-output-buffer)
1723 (write-region (point-min) (point-max)
1724 (or file tinyprocmail-:lint-output-file)))))
1726 ;;; ----------------------------------------------------------------------
1728 (defun tinyprocmail-output-file-kill ()
1729 "Kill `tinyprocmail-:lint-file' if it exists."
1731 (if (file-exists-p tinyprocmail-:lint-output-file)
1732 (delete-file tinyprocmail-:lint-output-file)))
1734 ;;; ----------------------------------------------------------------------
1736 (defun tinyprocmail-output-clear (&optional verb)
1737 "Clear `tinyprocmail-:lint-output-buffer' buffer if it exists. VERB."
1742 ((ti::set-buffer-safe tinyprocmail-:lint-output-buffer)
1745 (message "TinyProcmail: %s cleared"
1746 tinyprocmail-:lint-output-buffer))))))
1750 ;;{{{ Lint: Flag functions
1752 ;;; ............................................................ &flag ...
1754 ;;; ----------------------------------------------------------------------
1756 (defun tinyprocmail-flag-read (string)
1757 "Read flags; including lock char, from STRING. If no flags read, return nil."
1759 ;; We have to count space, beacuse
1761 ;; :0 E fh c: is valid recipe.
1763 (setq string (replace-regexp-in-string "[ \t]+" "" string))
1764 (when (or (string-match "^[ \t]*:0?\\([^0#\n:]*:\\)" string)
1765 (string-match "^[ \t]*:0?\\([^0#\n]+\\)" string))
1766 (match-string 1 string)))
1768 ;;; ----------------------------------------------------------------------
1770 (defun tinyprocmail-flag-standardize (string)
1771 "Standardize flag order.
1772 The STRING must not contain anything else but flags.
1773 Call `tinyprocmail-flag-read' first.
1777 string Standardized order.
1778 symbol Standardized order, flags, ok, but flags were uniqueied:
1779 e.g. 'fhih' --> 'fhi'
1780 1 Error, The input string had invalid flags.
1781 2 Error, flag conflict, aAeE used simultaneously"
1782 (let* ((flags (tinyprocmail-flag-string))
1783 (hash (make-vector (length flags) nil))
1784 (len (length string))
1792 (ti::dotimes counter 0 (1- (length string))
1793 (setq ch (substring string counter (1+ counter)))
1794 (when (string-match ch flags) ;This is case sensitive match
1795 ;; Get the hash position
1799 ;; pos = 2 if the ch was 'f'
1800 (setq pos (match-beginning 0))
1801 ;; Increment the logical length: The character was valid
1803 ;; filter duplicates: If the hash doen't have this character already
1804 ;; then add it to new string.
1805 (when (null (elt hash pos))
1806 ;; Mark the flas as used in hash table
1808 (if (string-match ch "aAeE")
1810 (if (string-match ch "wW")
1811 (incf conflict2)))))
1812 ;; Map the hash and see what flags it set
1813 (ti::dotimes counter 0 (1- (length hash))
1814 (if (elt hash counter)
1815 (setq ret (concat ret (substring flags counter (1+ counter))))))
1817 ((or (> conflict1 1)
1819 ;; There can be only one: aAeE
1821 ((eq len (length ret))
1822 ;; All flags checked and they were valid
1825 ;; All flags ok, but there were duplicates, which were
1829 ;; Error: invalid flags
1832 ;;; ----------------------------------------------------------------------
1834 (defun tinyprocmail-flag-format-default (flags)
1835 "Default FLAGS format: ' STANDARDIZED-ORDER'. FLAGS must not have spaces."
1837 (setq new (tinyprocmail-flag-standardize flags))
1838 (if (or (stringp new)
1840 (setq new (symbol-name new))))
1841 (if (not (eq tinyprocmail-:flag-and-recipe-start-style
1845 (error "Invalid flags %s --> %s " flags new))))
1847 ;;; ----------------------------------------------------------------------
1849 (defun tinyprocmail-flag-kill (&optional replace)
1850 "Kill flags in the line and optionally REPLACE and STANDARDIZE with SPACE."
1851 (let* ((eol (line-end-position))
1855 (when (and replace tinyprocmail-:flag-format-function)
1856 (setq replace (funcall tinyprocmail-:flag-format-function replace)))
1858 (when (re-search-forward ":0" eol t)
1860 (setq list (inline (ti::re-search-point-list '(":" "#") nil eol))
1861 end (if list (apply 'min list) eol))
1862 (delete-region beg end)
1865 (insert replace)))))
1867 ;;; ----------------------------------------------------------------------
1869 (defun tinyprocmail-flag-order-lint ()
1870 "Lint flag order at point. Return (FLAGS . STD-FLAGS) or nil."
1871 (let* ((line (ti::remove-properties (ti::read-current-line)))
1872 (pedantic (eq tinyprocmail-:lint-log-verbose 'pedantic))
1876 (when (prog1 (setq flags1 (tinyprocmail-flag-read line))
1877 (unless flags1 ;No flags in this recipe
1878 (setq flags1 "" flags2 "")))
1879 ;; User can write this, which we just ignore if the first
1884 (if (string-match "^[$]" flags1)
1889 "info, flag variable `%s' was not checked." flags1))
1891 (setq flags2 (tinyprocmail-flag-standardize flags1))
1892 ;; How would the standardization go?
1895 (setq str (format "Warning, duplicate flags found: `%s'" flags1))
1896 (tinyprocmail-log (point) str)
1897 (setq flags2 (symbol-name flags2))
1898 (tinyprocmail-fix-macro (concat str " Correct ")
1899 (tinyprocmail-flag-kill flags2)))
1901 (setq str (format "Error, invalid or extra flags: `%s'" flags1))
1902 (tinyprocmail-log (point) str)
1903 (tinyprocmail-fix-macro (concat str "(C-g to quit)"))
1906 (setq str "Error, flag conflict; some simultaneous 'aAeEwW'")
1907 (tinyprocmail-log (point) str)
1908 (tinyprocmail-fix-macro (concat str "(C-g to quit)"))
1910 ((and (not (string= flags1 flags2)) pedantic)
1914 "Pedantic, flag order style is not standard `%s', was `%s'"
1916 (tinyprocmail-log (point) str)
1917 (tinyprocmail-fix-macro (concat str " Correct ")
1918 (tinyprocmail-flag-kill flags2))))))
1919 (if (and flags1 flags2)
1920 (cons flags1 flags2))))
1922 ;;; ----------------------------------------------------------------------
1924 (defun tinyprocmail-conition-comment-move-up ()
1925 "Move comment upward and kill it."
1931 ((looking-at "^[ \t]*\\(#.*\\)")
1932 (goto-char (match-beginning 1))
1933 (setq str (match-string 1)
1934 col (current-column))
1935 (ti::buffer-kill-line))
1936 ((looking-at ".*\\(#.*\\)")
1937 (setq str (match-string 1))
1938 (ti::replace-match 1)
1939 (if (looking-at "^[ \t]+")
1940 (setq col (length (match-string 0))))))
1942 (if (null (re-search-backward "^[ \t#]*$" nil t))
1945 ;; COL is the indentation of the code.
1946 (insert (if col (make-string col ?\ ) "") str "\n")
1949 ;;; ----------------------------------------------------------------------
1951 (defun tinyprocmail-condition-comment-embedded (&optional point point-min)
1952 "Handle embedded comment inside condition line. Point must be on comment.
1953 POINT is used for lint log. POINT-MIN is (point-min) by default."
1954 (let* ((supp (tinyprocmail-supported-p 'condition-middle-comment))
1955 (col (current-column))
1959 (or point (setq point opoint))
1960 (or point-min (setq point-min (point-min)))
1964 (looking-at "^[ \t]*\\*.*#"))
1967 "Error, comments not allowed in condition line."
1969 (tinyprocmail-fix-macro
1970 "Comments not allowed in condition, Move upwards "
1972 (tinyprocmail-conition-comment-move-up))))
1976 "Error, embedded comment. Not supported by your procmail."
1978 (tinyprocmail-fix-macro "Error, embedded comment. Move upward "
1979 (tinyprocmail-conition-comment-move-up)))
1980 (t ;supported by this procmail
1981 (forward-line -1) ;Peek previous line
1982 (move-to-column col t)
1988 info, embedded comment, please indent it by 4 spaces (readability)."
1991 (forward-line 1) (move-to-column col t)
1992 (tinyprocmail-fix-macro "[recommendation] Indent comment "
1994 ((looking-at "\\([ \t]+\\)#")
1997 ;; # comment << here
1998 (tinyprocmail-log point "info, embedded comment does not line up."
2001 (forward-line 1) (move-to-column col 'force)
2002 (tinyprocmail-fix-macro "Line up comment "
2003 (insert (match-string 1))))))
2004 ;; Back to normal line
2006 (if (null no-move) ;The comment line was killed.
2009 ;;; --------------------------------------------------------- &comment ---
2011 (defun tinyprocmail-lint-condition-comments (&optional point-min)
2012 "Check condition area and comments.
2013 Buffer must be narrowed to condition lines.
2016 * condition * condition
2018 * condition * condition
2022 POINT-MIN This is the value of logical `point-min' before calling
2023 the function. If aller narrowed to recipe, it must pass
2024 this variable, otherwise narrowed region's `point-min'
2025 is used to report error lines.
2030 non-nil there are embedded comments"
2032 (while (re-search-forward "#" nil t)
2034 (tinyprocmail-condition-comment-embedded (point) point-min)))
2036 ;;; ----------------------------------------------------------------------
2038 (defun tinyprocmail-lint-recipe-start (flags std-flags)
2039 "Lint recipe start: flags and lockfile. Use FLAGS and STD-FLAGS."
2040 (let* ((lock-p (string-match ":" (or flags "")))
2041 (pedantic (eq tinyprocmail-:lint-log-verbose 'pedantic))
2042 (pipe-w-re tinyprocmail-:pipe-w-warning-ignore-regexp)
2044 (point-min (point-min))
2045 (bol (line-beginning-position))
2046 (direc (tinyprocmail-lint-directive-1))
2047 (dw (tinyprocmail-lint-directive-p "-w" direc))
2048 (di (tinyprocmail-lint-directive-p "-i" direc))
2049 (dc (tinyprocmail-lint-directive-p "-c" direc))
2065 ;;; var-or-literal-p
2075 (tinyprocmail-move-to-condition-end)
2076 (setq cond-end (point)
2078 pipe-p (looking-at "[ \t]*|")
2079 brace-p (tinyprocmail-brace-p)
2080 ;;; var-or-literal-p (looking-at "[$a-z]")
2081 forward-p (if (looking-at "!") (point))
2082 fwd-invalid-p (if (looking-at "!.*,") (point))
2083 ;; Illegal construct
2085 ;; mailbox1 mailbox2
2086 mbox-many-p (if (looking-at "[^|!{:\n]+[ \t]+[^ \t\n]") (point))
2087 mbox-p (if (looking-at "[^|!{:\n]+[ \t]*$") (point))
2088 dev-null-p (if (looking-at "/dev/null") (point))
2089 ;; The actions line must be one of the following
2097 invalid-action-p (if (tinyprocmail-action-line-ok-p) cond-end)
2098 ;; VAR=| formail -zX'Subject:'
2099 assignment-p (if (looking-at ".*=|")
2101 ;;; (ti::d! mbox-p dev-null-p (ti::read-current-line))
2102 ;; ....................................................... mailbox ...
2104 (setq str "Error, prematuere end of buffer.")
2105 (tinyprocmail-log (point) str)
2106 (tinyprocmail-fix-macro (concat "[cannont-fix] " str)))
2107 (when (and mbox-many-p
2108 (null assignment-p))
2109 (setq str "Error, Multiple mailboxes not allowed.")
2110 (tinyprocmail-log cond-end str)
2111 (tinyprocmail-fix-macro (concat "[cannot-fix] " str)))
2112 (when invalid-action-p
2114 (goto-char invalid-action-p)
2115 (skip-chars-forward " \t\n")
2118 (setq str "Error, empty lines before brace are not allowed.")
2119 (tinyprocmail-log invalid-action-p str)
2120 (tinyprocmail-fix-macro (concat str " Delete ")
2125 (skip-chars-backward " \t\n")
2129 (setq str "Error, Invalid action line; no |!$A-Z or brace found.")
2130 (tinyprocmail-log invalid-action-p str)
2132 (goto-char invalid-action-p)
2133 (tinyprocmail-fix-macro (concat "[cannot-fix] " str)))))))
2134 ;; ....................................................... forward ...
2136 (setq str "Error, invalid forward line. Maybe extra colons.")
2137 (tinyprocmail-log fwd-invalid-p str)
2138 (tinyprocmail-fix-macro (concat str " Remove ")
2140 (while (re-search-forward "," (line-end-position) t)
2141 (ti::replace-match 0))))
2144 (goto-char forward-p)
2145 (when (looking-at ".*!.*\\(\\<-t\\> *\\)")
2146 (setq str "Warning, forward does not need -t switch")
2147 (tinyprocmail-fix-macro (concat str " Remove ")
2148 (ti::replace-match 1)))
2149 (when (looking-at ".*!.*\\(\\<-oi\\> *\\)")
2151 info, ! -oi, may be unnecessary. It's default in New prcomail.")
2152 (tinyprocmail-fix-macro (concat str " Remove ")
2153 (ti::replace-match 1)))))
2154 ;; ............./........................................ check >> ...
2155 ;; Narrowed to condition region
2158 ;; * condition * condition
2159 ;; # comment | pipe actions. \\
2160 ;; * more actions \\
2163 (goto-char cond-end)
2164 ;;;; (ti::d! (point) brace-p (ti::read-current-line))
2166 ((tinyprocmail-recipe-start-p)
2171 (tinyprocmail-skip-continuation-forward)
2173 (skip-chars-backward " \t\n") ;The end of narrow point
2174 ;;; (ti::d! "skip" (point))
2175 ;; ################################################## Narrow-begin ###
2176 (ti::narrow-safe bol (point)
2177 (when (setq formail-p (ti::re-search-check "formail"))
2178 (setq formail-d-p (ti::re-search-check " -[^ ]*D "))) ;; perhaps -rD
2179 ;; | (formail -rA "Header: val" \
2182 (setq paren-p (ti::re-search-check "("))
2183 (setq sendmail-p (ti::re-search-check "sendmail")
2184 ;; Is there any condition lines?
2185 condition-p (ti::re-search-check "^[ \t]*\\*")
2186 size-test-p (ti::re-search-check "^[ \t]*\\*[ !$]*[<>]"))
2187 ;;; (ti::d! cond-end "pm-narrow: formail, sendmail" formail-p condition-p)
2188 ;; \> is procmail word boundary.
2189 (when (save-excursion
2190 (goto-char cond-end)
2191 ;; If line continues:
2196 ;; Accept "out >>FILE" and "OUT >>$FILE", but single
2197 ;; token must have leading space
2199 ;; If the > token is directly in the cond-end line
2202 (or (looking-at ".*>") ;; on the same line
2203 (re-search-forward "[^\\]> " nil t) ;; maybe continued
2204 (re-search-forward ">>" nil t)))
2205 (setq redirection-p (point))
2206 ;;; (ti::d! (progn (goto-char redirection-p) (ti::read-current-line)))
2208 ;; ................................................. empty-lines ...
2210 (while (and (not (eobp))
2211 (re-search-forward "^[ \t]*$" nil t))
2214 (point) "Error, no empty lines allowed inside condition block"
2216 (tinyprocmail-fix-macro
2217 "Error, empty line not allowed inside condition. Remove "
2219 (ti::buffer-kill-line))
2222 ;; .................................................... comments ...
2226 (tinyprocmail-lint-condition-comments point-min)))
2228 point "Pedantic, Condition lines have embedded comments."
2230 ;; #################################################### Narrow-end ###
2232 ;; ............................................. no-condition-line ...
2234 (setq fix nil str "" str2 flags)
2235 (dolist (ch '("H" "B" "D"))
2236 (when (ti::string-match-case ch str2)
2239 str2 (ti::replace-match 0 nil str2))))
2242 (format "Warning, no condition line, but flags `%s' found."
2244 (tinyprocmail-log opoint str)
2245 (tinyprocmail-fix-macro (concat str " Remove ")
2248 ;; ......................................... nested {} block start ...
2249 ;; Most of the flags don't make sense in the outer block level
2251 ;; H, B, A, a, E, e, and D affect the conditions and thus are
2252 ;; meaningful when the action is to open a brace. H, B, and D would be
2253 ;; meaningless, of course, on any unconditional recipe, but they should
2254 ;; not cause error messages.
2256 (setq fix nil str "" str2 flags)
2257 ;; Exclude all. Base is aAeEfhbHBDwWirc:
2258 (setq list '("f" "h" "b" "i" "r" "w" "W" ":"))
2259 ;; if you are using c to launch a clone, then w, W, and a
2260 ;; local lockfile can be meaningful.
2264 ((string-match "c" flags)
2265 '("f" "h" "b" "i" "r"))
2267 '("f" "h" "b" "i" "r" "w" "W"))))
2270 (when (ti::string-match-case ch str2)
2273 str2 (ti::replace-match 0 nil str2))))
2276 (format "Warning, start of {} block has unnecessary flags `%s'"
2278 (tinyprocmail-log opoint str)
2279 (tinyprocmail-fix-macro (concat str " Remove ")
2283 (not (string-match "c" flags)))
2284 (setq str "Error, start of {} block has lockfile, but no `c' flag.")
2285 (tinyprocmail-log opoint str)
2286 (tinyprocmail-fix-macro (concat "[cannot-fix]" str))))
2287 ;; ...................................................... size[<>] ...
2289 (let* (case-fold-search)
2290 (when (string-match "H" flags)
2291 (setq str "Error, size test doesn't use `H' flag. (use H ?? <)")
2292 (tinyprocmail-log size-test-p str)
2293 (tinyprocmail-fix-macro (concat str " Remove ")
2295 flags (replace-regexp-in-string "H" "" flags))))
2296 (when (string-match "B" flags)
2297 (setq str "Error, size test doesn't use `B' flag. (use B ?? <)")
2298 (tinyprocmail-log size-test-p str)
2299 (tinyprocmail-fix-macro (concat str " Remove ")
2301 flags (replace-regexp-in-string "B" "" flags))))))
2302 ;; .................................................... assignment ...
2303 ;; VAR=| cat something
2305 (when (and (string-match "c" flags)
2307 (setq str "Warning, flag `c' is useless in assignment =|")
2308 (tinyprocmail-log opoint str)
2309 (tinyprocmail-fix-macro (concat str " Remove ")
2311 flags (replace-regexp-in-string "c" "" flags))))
2312 (when (and (string-match "i" flags)
2314 (setq str "Warning, flag `i' is not recommended in assignment =|")
2315 (tinyprocmail-log opoint str)
2316 (tinyprocmail-fix-macro (concat str " Remove ")
2318 flags (replace-regexp-in-string "i" "" flags))))
2320 (setq str "Warning, lockfile \":\" is useless in assignment =|")
2321 (tinyprocmail-log opoint str)
2322 (tinyprocmail-fix-macro (concat str " Remove ")
2324 flags (replace-regexp-in-string ":" "" flags))))
2326 (goto-char assignment-p)
2327 (when (looking-at ".*`")
2328 (setq str "Error, backquotes mess things up in assignment =|")
2329 (tinyprocmail-log (point) str)
2330 (tinyprocmail-fix-macro (concat "[cannot-fix] " str )))))
2331 ;; ..................................................... formail-D ...
2332 (when (and formail-p
2336 (when (string-match "f" flags)
2338 opoint "Error, formail -D used. Flag `f' is a mistake.")
2339 (tinyprocmail-fix-macro "Formail -D used, remove `f' flag? "
2341 flags (replace-regexp-in-string "f" "" flags))))
2342 (when (not (string-match "W" flags))
2344 opoint "Warning, formail -D used but no `W' flag found.")
2345 (tinyprocmail-fix-macro "Formail -D found, add `W' flag? "
2346 (setq fix-line t flags (concat "W" flags))))
2347 (when (not (ti::string-match-case "h" flags))
2348 (tinyprocmail-log opoint "Error, formail -D used. No flag `h' found.")
2349 (tinyprocmail-fix-macro "Formail -D found, Add `h' flag? "
2350 (setq fix-line t flags (concat "h" flags))))
2351 (when (ti::string-match-case "b" flags)
2353 opoint "Error, formail -D used. SHould not have `b'.")
2354 (tinyprocmail-fix-macro "Formail -D found, remove `b' flag? "
2355 (setq fix-line t flags (ti::string-regexp-delete "b" flags))))
2357 (setq str "Warning, formail -D used but no lockfile.")
2358 (tinyprocmail-log opoint str)
2359 (tinyprocmail-fix-macro (concat str " Add. ")
2360 (setq fix-line t flags (concat ":" flags)))))
2361 ;; ............................................ Check f and h or b ...
2363 (when (and (ti::string-match-case "f" flags)
2364 (null (or (ti::string-match-case "b" flags)
2365 (ti::string-match-case "h" flags))))
2368 "Warning, `f', but no h;b;hb found. What's up here? (readability) ")
2369 (when (eq tinyprocmail-:lint-fix-mode 'semi)
2371 (y-or-n-p "Flag `f' requires `h' or `b' (yes=h, no=b) "))
2372 (setq flags (concat "h" flags))
2373 (setq flags (concat "b" flags)))
2375 ;; ......................................................... :0 fc ...
2376 (when (and (ti::string-match-case "f" flags)
2377 (ti::string-match-case "c" flags))
2378 (setq str "info, Redundant `c' in `f' recipe.")
2379 (tinyprocmail-log (point) str)
2380 (tinyprocmail-fix-macro (concat str " Correct ")
2382 (setq flags (replace-regexp-in-string "c" "" flags))))
2383 (when fix (tinyprocmail-flag-kill flags))
2384 ;; ..................................................... "|" and w ...
2385 ;; Every "|" action should have "w" flag
2389 (null (string-match "w" flags))
2391 (or (null pipe-w-re)
2393 (goto-char cond-end)
2394 (not (looking-at pipe-w-re)))))
2396 Warning, recipe with \"|\" may need `w' flag. (recommended) ")
2397 (tinyprocmail-log opoint str)
2398 (tinyprocmail-fix-macro (concat str " Add ")
2400 flags (concat "w" flags))))
2401 ;; ................................................. check formail ...
2402 (when (and formail-p
2404 (null paren-p) ;; If this, then "f" is not needed
2407 ;; Don't require "f" flag in this case
2409 ;; | $FORMAIL -A "header" >> mbox
2411 (null redirection-p)
2412 (not (string-match "f" flags)))
2413 (setq str "Warning, Formail used but no `f' flag found.")
2414 (tinyprocmail-log opoint str)
2415 (tinyprocmail-fix-macro (concat str " Add ")
2416 (setq fix-line t flags (concat "f" flags))))
2417 ;; .............................................. f and no-formail ...
2418 ;; If there was MH "rcvstore" Then "i" should not be there.
2419 (when (and (null redirection-p)
2423 (not (ti::string-match-case "f" flags))
2424 (not (ti::string-match-case "i" flags))
2426 (setq str "Warning, recipe with \"|\", but no \">\" may need `i' flag.")
2427 (tinyprocmail-log opoint str)
2428 (tinyprocmail-fix-macro (concat str " Add ")
2430 flags (concat "i" flags))))
2431 ;; i is meaningless if nested condition follows
2435 (ti::string-match-case "i" flags)
2437 (setq str "info, flag `i' is meaningless on top of nested block.")
2438 (tinyprocmail-log opoint str)
2439 (tinyprocmail-fix-macro (concat str " Remove ")
2441 flags (replace-regexp-in-string "i" "" flags))))
2442 (when (and (null redirection-p)
2444 (not (ti::string-match-case "f" flags))
2445 (not (ti::string-match-case "c" flags))
2446 (ti::string-match-case "i" flags)
2450 Warning, no \">\" in \"|\" recipe 'i' kills message. May need `c'.")
2451 (tinyprocmail-log opoint str)
2452 (tinyprocmail-fix-macro (concat str " Add ")
2453 (setq fix-line t flags (concat "c" flags))))
2455 ;; ............................................. check H without B ...
2456 (let (case-fold-search)
2457 (when (and (string-match "H" flags)
2458 (null (ti::string-match-case "B" flags)))
2459 (setq str "info, flag `H' is useless, because it is default.")
2460 (tinyprocmail-log opoint str)
2461 (tinyprocmail-fix-macro (concat str " Remove ")
2463 flags (replace-regexp-in-string "H" "" flags)))))
2464 ;; ....................................................... check W ...
2465 ;; :0 c: somefile is same as :0 Wc: somefile but ONLY on nesting
2469 (ti::string-match-case "W" flags)
2470 (ti::string-match-case "c" flags))
2472 "info, redundant `Wc:', `c:' already implies W in {} block.")
2473 (tinyprocmail-log opoint str)
2474 (tinyprocmail-fix-macro (concat str " Correct")
2476 flags (replace-regexp-in-string "W" "" flags))))
2477 ;; ................................................. need lockfile ...
2478 (when (and redirection-p
2483 (setq str "Warning, recipe seems to store to folder, may need lock.")
2484 (tinyprocmail-log opoint str)
2485 (tinyprocmail-fix-macro (concat str " Add ")
2487 flags (concat flags ":"))))
2488 ;; Missing lockfile, but not if /dev/null
2498 (setq str "Warning, message dropped to folder, it may need a lock.")
2499 (tinyprocmail-log mbox-p str)
2502 (tinyprocmail-fix-macro (concat str " Add ")
2504 flags (concat flags ":")))))
2505 ;; .......................................................... MBOX ...
2507 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... dev/null . .
2510 (setq str "Warning, /dev/null doesn't need lock")
2511 (tinyprocmail-log mbox-p str)
2514 (tinyprocmail-fix-macro (concat str " Remove ")
2516 flags (replace-regexp-in-string ":" "" flags)))))
2517 (unless (ti::string-match-case "h" flags)
2518 (setq str "Info, /dev/null may be more efficient with `h' flag")
2519 (tinyprocmail-log mbox-p str)
2522 (tinyprocmail-fix-macro (concat str " Add ")
2524 flags (concat flags "h"))))))
2525 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... mbox name . .
2529 (when (looking-at ".*LOGFILE\\|MAILDIR\\|FORMAIL\\|SENDMAIL")
2530 (setq str (ti::read-current-line (point)))
2531 (setq str (format "Warning, suspicious mbox filename `%s'" str))
2532 (tinyprocmail-log mbox-p str)
2533 (tinyprocmail-fix-macro (concat "[cannot-fix] " str))))
2534 (when (and flags (string-match "i" flags))
2535 (setq str "Warning, flag `i' is dangerous when dropping to folder.")
2536 (tinyprocmail-log opoint str)
2537 (tinyprocmail-fix-macro (concat str " Remove ")
2539 flags (replace-regexp-in-string "i" "" flags))))
2540 (when (and flags (ti::string-match-case "hb\\|bh" flags))
2542 Warning, flag combo `hb' is useless when dropping to folder.")
2543 (tinyprocmail-log opoint str)
2544 (tinyprocmail-fix-macro (concat str " Remove ")
2546 flags (replace-regexp-in-string "hb\\|bh" "" flags)))))
2547 ;; .................................................... check lock ...
2550 (skip-chars-forward "[ \t]")
2551 (when (looking-at "[ \t]*:0[^:\n]*\\(:\\)\\( [a-z]\\)[a-z]?[ \t\n#]+")
2552 (setq str (save-match-data (ti::string-remove-whitespace
2554 ;; change :0 HB: c to :0 HBc:
2557 (format "Possibly Flag used as lock `%s'" (ti::read-current-line)))
2559 ((tinyprocmail-flag-p str)
2560 (tinyprocmail-fix-macro
2561 (format "Only One char `%s' lockfile, Move to flag section? " str)
2562 (ti::replace-match 2)
2563 (setq flags (concat str flags)
2566 (tinyprocmail-fix-macro
2567 (format "[cannot-fix] Odd one char `%s' lockfile, C-g to quit."
2569 ;; ................................................. lockfile name ...
2570 (when (looking-at "[ \t]*:0[^#:\n]*\\(:\\)\\([ \t]*\\)\\([^ \t\n#]+\\)")
2571 (setq lock-file (match-string 3))
2572 ;;; (ti::d! "LOCK-FILE" lock-file)
2573 (when (save-match-data
2574 ;; \ = dos styled path
2575 ;; / = unix styled path
2578 ;; If lock file has variable expansion, then we won't
2580 (not (string-match "[$]" lock-file))
2581 (not (tinyprocmail-string-valid-p lock-file 'path))))
2584 (format "Unusual characters in lockfile name `%s'" lock-file))
2585 (tinyprocmail-fix-macro
2586 "[cannot-fix] Lockfile has unusual characters. C-g to quit"))
2587 ;; :0: $FILE --> can't know if it has .lock
2589 ((and (not (save-match-data (string-match "\\." lock-file)))
2590 (not (string-match "[$]LOCKEXT" lock-file))
2591 (save-match-data (string-match "^[$]" lock-file)))
2595 "info, could't check extention .lock in lockfile `%s'"
2598 (and (not (string-match "\\." lock-file))
2599 (not (string-match "[$]LOCKEXT" lock-file))))
2600 (setq str (format "no $LOCKEXT extension in lockfile `%s'" lock-file))
2601 (tinyprocmail-log (point) str)
2602 (tinyprocmail-fix-macro (concat str " Add ")
2603 (goto-char (match-end 0))
2604 (insert "$LOCKEXT")))
2607 (when (string-match "\\(.*\\)\\." lock-file)
2608 (setq file (match-string 1 lock-file)))
2609 (not (string-match "\\.lock\\|\\.lck\\|[$]LOCKEXT" lock-file))))
2613 "Non-standard lockfile extension. (use $LOCKEXT) `%s'" lock-file))
2614 (tinyprocmail-fix-macro
2615 (format "Non-standard lockfile extension. Change to $LOCKEXT ")
2616 (setq lock-file (concat file "$LOCKEXT"))
2617 (ti::replace-match 3 lock-file))))) ;; When end
2619 (when fix-line (tinyprocmail-flag-kill flags)))))
2624 ;;; ........................................................... &other ...
2626 ;;; ----------------------------------------------------------------------
2628 (defun tinyprocmail-lint-malformed-brace ()
2630 (while (re-search-forward "^[ \t]*{" nil t)
2631 (when (and (char= ?\{ (preceding-char)) ; {} or {var
2632 (not (looking-at "[ \t\n]")))
2633 (tinyprocmail-log (point) "Error, no space after `{' .")
2634 (tinyprocmail-fix-macro "No space after { Add one?"
2637 (when (and (looking-at ".*\\(}\\)")
2639 (not (looking-at ".*[ \t\n]+\\(}\\)"))))
2640 (tinyprocmail-log (point) "Error, no space before `}' .")
2641 (tinyprocmail-fix-macro "No space before } Add one?"
2642 (ti::replace-match 1 " }")))))
2644 ;;; ----------------------------------------------------------------------
2646 (defun tinyprocmail-lint-malformed-misc ()
2647 "Check varaious other things."
2648 (let* (;;; (pedantic (eq tinyprocmail-:lint-log-verbose 'pedantic))
2650 (re "echo\\|cat\\|tail\\|head\\|sed\\|perl\\|awk\\|perl\\|[-]")
2652 ;; Detect "dummy `echo`", missing "=" or ";"
2654 ;; But following is valid.
2656 ;; LOG = "text start
2658 (while (re-search-forward
2659 "^[ \t]*\\([^!|#\n=;]+\\)\\([ \t]+\\)[\"`]" nil t)
2660 (setq str (match-string 0))
2661 (when (save-match-data
2663 (not (string-match re (match-string 1)))
2664 (not (string-match "\"[ \t]*$\\|\"[ \t]*#" str))
2665 (not (tinyprocmail-condition-line-p))))
2668 (format "Warning, After `%s', there is no \"=\" or \";\""
2670 (tinyprocmail-log (point) str)
2671 (tinyprocmail-fix-macro (concat str " Add = ")
2672 (ti::replace-match 2 " = "))))
2673 (goto-char opoint)))
2675 ;;; ----------------------------------------------------------------------
2677 (defun tinyprocmail-lint-malformed-var-defs ()
2678 "Check variable definitions and assignments."
2680 ;;; (pedantic (eq tinyprocmail-:lint-log-verbose 'pedantic))
2686 (while (re-search-forward "^[ \t]*[^#\n].*=" nil t)
2688 (unless (tinyprocmail-comment-line-pp)
2689 ;; Backslash at the end (continuation)
2691 ;;; (setq cont-p (looking-at ".*[\\][ \t]*$"))
2692 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... .. date ...
2693 ;; It's slow to call `date' and there is already 10x faster
2694 ;; ways to derive the message date: See pm-jadate.rc
2696 ;; MONTHNAME = `date +%y-%m`
2697 (when (looking-at ".*=`.*date")
2700 "Info, calling `date' is 10x slower"
2701 " than reading From_ hdr."))
2702 (tinyprocmail-log (point) str)
2703 (tinyprocmail-fix-macro str))
2704 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... perl-var . .
2705 ;; $perl-styled-var = "value"
2706 (when (looking-at "^[ \t]*\\([$]\\).*=")
2707 (tinyprocmail-log (point) "Error, perl styled variable assignment.")
2708 (tinyprocmail-fix-macro "Remove extra Perl styled assignment to `$' "
2709 (ti::replace-match 1)))
2710 ;; ... ... ... ... ... ... ... ... ... ... ... illegal-var-name . .
2712 (when (and (looking-at "^[ \t]*\\([^ \t\n]+\\)[ \t]*=")
2713 (setq str (match-string 1))
2715 ;; Must start with alpha otherwise reject
2716 (string-match "^[A-Z]" str)
2717 (not (tinyprocmail-comment-line-p))
2718 (not (tinyprocmail-string-valid-p str)))
2719 ;; This will unfortunately misclassify line,
2720 ;; which includes '=', like
2722 ;; SPAM_REGEXP_FILE ="\
2723 ;; filename.*=.*\.(pif|rar|zoo|arj|exe|bat)"
2726 "Warning, odd characters in variable `%s'." str))
2727 (tinyprocmail-log (point) str)
2728 (tinyprocmail-fix-macro str))
2729 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... . literal . .
2732 (when (looking-at "^[^=\n]+=[ \t]*\\([a-z]\\|[0-9]+[-.a-z]\\)")
2735 "Warning, no right hand variable found. ([$\"`'] .. missing)")
2736 (tinyprocmail-fix-macro "Add missing `$' to right hand variable? "
2737 (ti::replace-match 1 (concat "$" (match-string 1))) ))
2738 ;; ............................................. odd name(right) ...
2739 ;; var = $odd%&_name nok
2741 ;; var = $var-$var ok
2743 "^[ \t]*[a-z].*=[ \t]*[$]\\([a-z]+[^-/_a-z0-9$ \t\n#]+\\)")
2746 "Error, Odd variable name to the right `%s'."
2748 (tinyprocmail-log (point) str)
2749 (tinyprocmail-fix-macro str))
2750 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... tilde(~) . .
2751 (when (looking-at "^.*=.*\\(~\\)/")
2752 (tinyprocmail-log (point)
2753 "Error, csh's tilde(~) is not supported, Use $HOME.")
2754 (tinyprocmail-fix-macro "Non-supported: Substitute ~ with $HOME "
2755 (ti::replace-match 1 "$HOME")))
2756 ;; ... ... ... ... ... ... ... ... ... ... ... mismatch(` -- ') . .
2757 (when (or (looking-at "^.*=[ \t]*`.*\\('\\)[ \t]*#.*$") ;; comment
2759 (looking-at "^.*=[ \t]*`.*\\('\\)[ \t]*$"))
2762 "Error, assignent, starting backtick, but ends to single quote.")
2763 (tinyprocmail-log (point) str)
2764 (tinyprocmail-fix-macro (concat str " Kill ' ")
2765 (ti::replace-match 1 "`")))
2766 ;; ...................................................... var-init ...
2769 ;; Note that the colon in this case is ok.
2770 ;; D = {D}:/directory/file.txt
2773 "[ \t]*\\([^ \t{]+\\)[ \t]*"
2774 "=.*$\\({\\)[ \t]*\\([^ \t}:]*\\)\\([ \t]*\\):\\(.\\)"))
2775 (setq var1 (match-string 1)
2776 var2 (match-string 3)
2777 str (match-string 4)
2778 op (match-string 5))
2779 (when (save-match-data (not (string= "" str)))
2780 (tinyprocmail-log (point) "Error, space before init operator.")
2781 (tinyprocmail-fix-macro "Kill Space before init operator."
2782 (ti::replace-match 4)))
2783 (when (save-match-data (ti::nil-p var2))
2784 (tinyprocmail-log (point)
2785 "Error, no right hand init variable found.")
2786 (tinyprocmail-fix-macro
2787 (format "Error, no right hand `%s'. Add " var1 )
2788 (goto-char (1+ (match-beginning 2)))
2791 ;; Writing VAR = ${$VAR is a mistake. Notice 2nd $
2792 (when (save-match-data (and var2 (string-match "^[$]" var2)))
2796 "Error, in init sequence, `%s' has extra $ ." var2))
2797 (tinyprocmail-fix-macro
2798 (format "`%s' contains extra $. Correct " var2)
2799 (save-match-data (setq
2801 (replace-regexp-in-string "^[$]" "" var2)))
2802 (ti::replace-match 3 var2)))
2803 (when (save-match-data
2804 (or (and var1 (not (tinyprocmail-string-valid-p var1)))
2805 (and var2 (not (tinyprocmail-string-valid-p var2)))))
2809 "Warning, in init sequence `%s' or `%s' has illegal characters."
2810 (or var1 "<?>") (or var2 "<?>")))
2811 (tinyprocmail-log (point) str)
2812 (tinyprocmail-fix-macro str))
2813 ;; 1998-04 I used to believe this calls shell, but it doesn't.
2814 ;; D = ${D:-`date`} So the following recipe is *commented out*
2816 (when (and (save-match-data (looking-at ".*[-+][ \t]*`"))
2821 "Pedantic, `` is not a recommended initialize "
2822 "practise (uses shell)."))))
2823 (when (save-match-data (not (string= var1 var2)))
2824 (tinyprocmail-log (point)
2825 "Warning, variables don't match in init sequence.")
2826 (tinyprocmail-fix-macro
2827 "[cannot-fix] Left var1 and right var2 don't match."))
2828 (when (save-match-data (not (string-match "[-+]" op)))
2831 (format "Error, invalid init operator `%s'. Not [-+] " op))
2832 (tinyprocmail-fix-macro
2833 (format "[cannot-fix] Invalid init operator `%s' "
2837 ;;; ----------------------------------------------------------------------
2839 (defun tinyprocmail-lint-malformed-start-recipe ()
2840 "Check ': ' or '0:' recipes."
2841 (let* ((space (if (eq 'flags-together
2842 tinyprocmail-:flag-and-recipe-start-style)
2846 (while (setq list (ti::re-search-point-list '("^[ \t]*0:" "^[ \t]*:[^0]")
2847 'beginning-of-line))
2848 (goto-char (apply 'min list))
2850 ((looking-at "^[ \t]*\\(0:\\)")
2851 (tinyprocmail-log (point)
2852 "Error, recipe start is invalid, should be `:0'.")
2853 (tinyprocmail-fix-macro "recipe error, fix to `:0' "
2854 (ti::replace-match 1 ":0")))
2856 ;; The :[^0] matches \n, and that's why we use looking-at.
2857 (if (looking-at "[ \t]*:")
2858 (goto-char (match-end 0))
2859 (backward-char 1)) ;There was newline
2861 ((looking-at "\\([ \t]*[1-9]\\|[ \t]+0\\)") ; ': 0' or
2862 (setq str "Warning, Suspicious recipe start, use standard `:0'.")
2863 (tinyprocmail-log (point) str)
2864 (tinyprocmail-fix-macro (concat str " Correct ")
2865 (ti::replace-match 1 "0")))
2867 (setq str "Warning, Suspicious recipe start, use standard `:0'.")
2868 (tinyprocmail-log (point) str)
2869 (tinyprocmail-fix-macro str
2870 (delete-horizontal-space)
2872 (if (not (looking-at " "))
2876 ;;; ----------------------------------------------------------------------
2878 (defun tinyprocmail-lint-condition-line-1 ()
2879 "Check one condition line. Point must be over start(*)."
2880 (let* ((point (point))
2881 (var-test-p (looking-at ".*[?][?]"))
2882 (shell-test-p (looking-at "\\*[! \t]+[?]"))
2888 ;; ....................................................... empty ...
2889 (when (looking-at "\\*[ \t]*$")
2890 (setq str "Error, Nothing in condition line.")
2891 (tinyprocmail-log (point) str)
2892 (tinyprocmail-fix-macro (concat "[Cannot-fix] " str))
2893 (throw 'done 'nothing))
2894 ;; ............................................ missing caret(^) ...
2897 ;;; (ti::d! (buffer-substring (point) (line-end-position)))
2898 (when (or (looking-at "\\*[ \t!]*\\([-A-Z]+\\):")
2899 (let (case-fold-search) ;be case sensitive
2902 "\\*[ \t!]*\\(FROM_DAEMON\\|FROM_MAILER\\|TO_?\\)"
2907 "Warning, `%s' does not have (^) in condition line."
2909 (tinyprocmail-log (point) str)
2910 (tinyprocmail-fix-macro (concat str " Add ")
2911 (goto-char (match-beginning 1))
2913 ;; .......................................................... TO ...
2914 ;; * ^TOregexp is a mistake, should use ^TOregexp\> or something
2916 (when (let (case-fold-search)
2917 (and (looking-at ".*\\(TO[^([\n]+\\)")
2919 (and (not (looking-at ".*[\\]>[ \t]*$"))
2920 (not (looking-at ".*[$]"))
2922 ;; Next line must not a condition
2926 ;; * more-restrictive-condition
2928 (not (save-excursion
2930 (looking-at "[ \t]*\\*")))))
2931 ;; Try to find similar contruct from buffer.
2932 ;; If found then user needs swap the order of
2935 ;; 1) TOaddr and later) TOadd-another
2940 (regexp-quote (match-string 1)) nil t))))
2944 "Warning, `%s' is not unique, another similar TO found."
2946 (tinyprocmail-log (point) str)
2947 (tinyprocmail-fix-macro (concat "[cannot-fix]" str)))
2948 ;; ....................................................... extra ...
2950 ;; * |regexp|regexp << Ooops, extra (*)
2951 (when (and (looking-at "\\*[ \t]*|")
2952 (save-excursion ;Peek previous
2954 (looking-at "[\\][ \t]*$")))
2955 (setq str "Warning, extra \"*\" before fist regexp \"|\".")
2956 (tinyprocmail-log (point) str)
2957 (tinyprocmail-fix-macro (concat str " Remove `*' ")
2959 (throw 'done 'extra))) ;Nothing more to check
2960 ;; ........................................... expansion missing ...
2961 ;; Very common mistake, you forgot the beginning $
2966 ;; * $!$REGEXP is ok
2967 ;; * $(^TO$REGEXP) is ok
2968 ;; * $-${VAR}^0 is ok
2969 (when (and (not (looking-at "\\*[ \t]*[$][-$!^(]"))
2970 (looking-at "\\*[ \t!]*[$]\\([^ \t\n]+\\)[^$\n]*$")
2974 (format "Error, No eval($) in condition found. (%s)"
2976 (tinyprocmail-log (point) str)
2977 (tinyprocmail-fix-macro (concat str " Add `$' ")
2981 ;; A bit more complex
2982 ;; * !^(To|Cc|Bcc):.*$LOGNAME
2988 ;; And $HOME variable is accepted without eval
2989 (when (and (not var-test-p)
2992 "\\*[^$\n]+\\(.\\)[$]\\([A-Z][^ \t\n]+\\)+[^$\n]$")
2994 (not (string-match "[\"]" (match-string 1))))
2996 (not (string-match "[$]HOME" (match-string 2)))))
2999 (format "Error, No eval($) in complex condition found. (%s)"
3001 (tinyprocmail-log point str)
3002 (tinyprocmail-fix-macro (concat str " Add `$' ")
3006 ;; ............................................. missing eval($) ...
3007 ;; User forgot the interpolation in the line
3009 ;; * ! VAR ?? $eval-this-var
3011 ;; --> * ! VAR ?? $ $eval-this-var
3013 ;; It's impossible to catch this however
3015 ;; * ! VAR ?? $eval-this $eval-second
3020 ;; * FROM??^foo@bar$
3022 (when (and (looking-at "\\*\\(.*\\)[?]\\([?]\\)[^$\n]+[$][^$]+$")
3023 (save-match-data ;; has $
3024 (not (string-match "[$]" (match-string 1))))
3025 (save-match-data ;; but must not ne at the end
3026 (not (string-match "[$]$" (match-string 1))))
3027 (save-match-data ;; literal newline in prenthesis ($)
3028 (not (string-match "($)" (match-string 1)))))
3029 (setq tmp (match-beginning 2))
3030 (setq str "Warning, missing eval($) operator in variable context.")
3031 (tinyprocmail-log (point) str)
3032 (tinyprocmail-fix-macro (concat str " Add ")
3033 (goto-char (1+ tmp))
3036 ;; Warn about: *$ $VAR ?? test
3037 ;; Not Warn about: *$ $VAR ?? $test
3039 ;; But this is ok: *$ $SUPREME^0 ^From:(some match)
3040 (when (and (not (looking-at "^.+[^][0-9] "))
3041 (looking-at "^.+\\([$]\\).*[$].*[?][?]"))
3042 (setq str "Warning, Possible $ misuse (doubled) to the left of ??")
3043 (tinyprocmail-log (point) str)
3044 (tinyprocmail-fix-macro (concat "cannot-fix]" str)))
3045 ;; * VAR ?? test is legal in procmail, don't require
3048 ;;; (when (and (looking-at "[^$\n]+[?][?]") ;; * VAR ?? test
3049 ;;; (not (looking-at ".*[HB][! \t]+[?]")) ;; * HB ?? regexp
3051 ;;; (tinyprocmail-log
3053 ;;; "Possibly missing left hand $ in ?? variable test.")
3054 ;;; (ti::d! (ti::read-current-line))
3055 ;;; (tinyprocmail-fix-macro "Maybe missing left hand $. C-g to quit."
3056 ;;; (forward-char 1)
3059 ;; .................................................... empty ?? ...
3060 (when (looking-at ".*[?][?][ \t\n]*$")
3061 (tinyprocmail-log (point) "Error, Nothing follows ?? ")
3062 ;; Can't autofix this one.
3064 (tinyprocmail-fix-macro
3065 "[cannot-fix] Error, Nothing follows ??, C-g to quit. "))
3066 ;; ............................................... suspicious ?? ...
3067 ;; VAR ?? . is not right
3068 (when (looking-at ".*[?][?][ \t]*\\(\\.\\)[ \t]*$")
3071 "Warning, '?? .' will fail on null variable. Prefer '(.|$)'")
3072 (tinyprocmail-fix-macro
3073 "'?? .' is not preferred, use '(.|$)' instead "
3074 (ti::replace-match 1 "(.|$)")))
3075 ;; ............................................... trailing star ...
3076 ;; * regexp.* but * \/regexp.* is okay.
3078 ;; Skip continuation
3079 (while (and (not (eobp))
3080 (looking-at ".*\\[ \t]*"))
3083 (setq end (line-end-position))
3085 (if (re-search-forward "\/" end t)
3087 (when (and (null match-p) (looking-at "^[^ \t\]+\\*[ \t]*$"))
3090 "info, maybe useless regexp `*' at the end condition.")
3091 (tinyprocmail-fix-macro "[cannot-fix] Useless `*' at the end."))))))
3093 ;;; ----------------------------------------------------------------------
3095 (defun tinyprocmail-lint-condition-lines (&rest args)
3096 "Check all condition lines. Ignore ARGS. Point must be inside condition."
3097 (while (and (not (eobp)) (tinyprocmail-move-to-next-condition-line))
3098 (tinyprocmail-lint-condition-line-1)))
3100 ;;; ----------------------------------------------------------------------
3102 (defun tinyprocmail-standardize-recipe-start ()
3103 "Check whole buffer and change recipe start to ':0 FLAGS'.
3105 `tinyprocmail-:flag-and-recipe-start-style'"
3107 (let* ((style tinyprocmail-:flag-and-recipe-start-style)
3112 (while (tinyprocmail-forward)
3114 ;; We're sitting on : go to 2 char forward
3117 ((eq style 'flags-together)
3118 (when (looking-at "\\([ \t]\\)[^#]")
3119 (ti::replace-match 1)
3122 (unless (looking-at "[ \t\n]")
3129 "TinyProcmail: standardized %d recipe start line(s). Style: %s"
3133 "Flags separated from :0")))
3136 (message "TinyProcmail: Can't find any recipes from buffer."))))
3138 ;;; ----------------------------------------------------------------------
3140 (defun tinyprocmail-lint-list-lint-directives ()
3141 "Find all recipes that have Lint directives.
3142 This function puts the results to `tinyprocmail-:lint-output-buffer'.
3143 Function activates only of `tinyprocmail-:lint-log-verbose' is 'pedantic."
3147 (when (eq tinyprocmail-:lint-log-verbose 'pedantic)
3148 (while (re-search-forward "^[ \t]*#[ \t]*Lint:[ \t]*\\(.*\\)" nil t)
3149 (setq options (match-string 1) point (match-beginning 1))
3151 (setq flags (tinyprocmail-flag-read (ti::read-current-line)))
3154 (format "info, Lint options `%s'. recipe flags `%s'."
3157 ;;; ----------------------------------------------------------------------
3159 (defun tinyprocmail-lint-find-wrong-escape-codes ()
3160 "Find misused \\t and \\n characters."
3163 ;; (re-search-forward "\\[[^]\n\\]+\\([\\]t\\)" nil t)
3164 (while (re-search-forward "\\([\\][tn]\\)" nil t)
3165 (unless (save-match-data (tinyprocmail-comment-line-p))
3166 (setq val (match-string 1))
3169 (format "Error, escape code `%s' is not known to procmail." val))
3170 (tinyprocmail-log (point) str)
3172 ((save-match-data (string-match "t" val))
3173 (tinyprocmail-fix-macro (concat str " Correct ")
3174 (ti::replace-match 1 "\t")))
3176 (tinyprocmail-fix-macro (concat "[cannot-fix] " str))))))))
3178 ;;; ----------------------------------------------------------------------
3180 (defun tinyprocmail-lint-find-2spaces ()
3181 "Find misused [ ] contructs. User meant space and TAB."
3187 (while (re-search-forward "\\[\\([^]\n]+\\)\\]" nil t)
3188 (setq set (match-string 1)
3189 not-comment (save-match-data
3190 (not (tinyprocmail-comment-line-p)))
3191 not-backtics (save-excursion
3193 (save-match-data (not (looking-at ".*`")))))
3194 ;; Skip lines like: dummy = `echo "[this value here]" > file`
3195 (when (and not-comment not-backtics)
3197 (when (string-match " \\( +\\)" set)
3200 "Warning, two spaces inside regexp [], maybe you mean TAB.")
3201 (tinyprocmail-log (point) str)
3202 (tinyprocmail-fix-macro "Warning, two spaces, change to TAB "
3204 (setq set (ti::replace-match 1 "\t" set)))))
3206 (ti::replace-match 1 set))
3207 (when (and (or (tinyprocmail-condition-line-p)
3208 (tinyprocmail-assignment-line-p))
3210 (string-match " [^ ]+ " set))
3211 (setq str "Warning, two spaces inside regexp [].")
3212 (tinyprocmail-fix-macro (concat "[cannot-fix] " str)))))))
3217 ;;; ----------------------------------------------------------------------
3219 (defun tinyprocmail-output-display ()
3220 "Show `tinyprocmail-:lint-output-buffer' buffer."
3223 ((null (get-buffer tinyprocmail-:lint-output-buffer))
3224 (error "No `tinyprocmail-:lint-output-buffer'"))
3226 (ti::save-excursion-macro
3227 (display-buffer tinyprocmail-:lint-output-buffer)
3228 (select-window (get-buffer-window tinyprocmail-:lint-output-buffer))
3230 (re-search-backward "^\\*\\*" nil t) ;; start of lint section
3231 (unless (eq major-mode 'tinyprocmail-output-mode)
3232 (turn-on-tinyprocmail-output-mode))
3233 (font-lock-mode-maybe 1)
3234 (ti::string-syntax-kill-double-quote)))))
3236 ;;; ----------------------------------------------------------------------
3238 (defun tinyprocmail-lint-forward (&optional mode verb)
3239 "Lint the code forward.
3241 MODE If nil, step through recipes and correct them interactively.
3242 If non-nil then Write log _with_ pedantic messages.
3243 If 2 x \\[universal-argument] then Write log without pedantic.
3244 VERB Verbose messages."
3246 (let* ((check-list tinyprocmail-:lint-do-hook)
3247 (tinyprocmail-:lint-fix-mode tinyprocmail-:lint-fix-mode)
3248 (tinyprocmail-:lint-log tinyprocmail-:lint-log)
3249 (tinyprocmail-:lint-log-verbose tinyprocmail-:lint-log-verbose)
3251 (time (current-time))
3258 (setq tinyprocmail-:lint-fix-mode nil)
3259 (if (equal mode '(16))
3260 ;; suppess 'pedantic
3261 (setq tinyprocmail-:lint-log-verbose nil)))
3262 (setq tinyprocmail-:lint-log (if mode t nil))
3263 (tinyprocmail-log-start)
3265 (run-hooks 'tinyprocmail-:lint-before-hook)
3267 (while (tinyprocmail-forward)
3268 (setq point (point))
3270 (when (and verb mode)
3271 (message "TinyProcmail: Linting %d %s" count (ti::read-current-line)))
3272 ;; There is no point of checking the recipe if the Flags are
3273 ;; not all right. The functions usually modify flags,
3274 ;; and that is imposible without proper flags.
3275 (setq ret (tinyprocmail-flag-order-lint))
3277 (dolist (func check-list)
3279 (funcall func (car ret) (cdr ret) )))
3282 (dolist (func tinyprocmail-:lint-after-hook)
3285 (tinyprocmail-overlay-hide)
3286 ;; Sort the output buffer. The results are in random order, because
3287 ;; many different list function have been run one after another.
3288 (when tinyprocmail-:lint-log
3289 (tinyprocmail-output-macro (tinyprocmail-output-sort-by-line)))
3290 (setq secs (ti::date-time-difference (current-time) time)
3292 (message "TinyProcmail: Lint done. recipe Count = %d time: %02d:%02d "
3293 count time (- secs (* time 60)))
3294 (when mode (tinyprocmail-output-display))
3295 (goto-char opoint)))
3297 ;;; ----------------------------------------------------------------------
3299 (defun tinyprocmail-lint-buffer (&optional mode verb)
3300 "Lint whole buffer. See MODE and VERB from `tinyprocmail-lint-forward'."
3305 (tinyprocmail-lint-forward mode verb)))
3307 ;;; ----------------------------------------------------------------------
3309 (defun tinyprocmail-lint-buffer-batch (&optional clear file)
3310 "Batch Lint buffer and write results to `tinyprocmail-:lint-output-file'.
3313 CLEAR If non-nil, clear output buffer
3314 FILE Save results to this file"
3317 (tinyprocmail-output-clear))
3318 ;; Delete empty lines from the beginning to get line numbers
3320 (ti::pmin) (skip-chars-forward " \t\n")
3321 (unless (eq (point) (point-min))
3322 (delete-region (point-min) (point)))
3323 (tinyprocmail-lint-buffer '(4))
3324 (tinyprocmail-output-file-save file))
3327 ;;{{{ Examples or other files
3328 ;;; ........................................................ &Examples ...
3329 ;; Here is example file for the Lint. It also demonstrated what kind of
3330 ;; errors can be trapped.
3333 ;; r--r--r-- 240/222 14764 Jun 22 15:05 1998 pm-lint.rc 1.26
3335 ;; -----BEGIN PGP MESSAGE-----
3337 ;; Comment: Base64 signed. File: pkg.tar uncompresses to approx. 20K
3339 ;; owHtWUtvW0ty9kUwCMQs80A2AVo0rVdEWpIfuSOP7UiyfKXElgxbvp47lmw1z2mS
3340 ;; PToP5jxE8V57l0UGyCIIMFkEmMkmCDCL/IGs8sKsB5gECJAJ5gcMsggQZBckVdV9
3341 ;; +jx4DmmS9y6CXMKwyHO6v66urq76qup7117+0jeu3fn5f/zR3/7Nv//zJ7/2W3c+
3342 ;; +ujjP/7ofz57/6c/XfzJwQ/j/nf+5Wc//NEPfvar3/vPv2/+99/96Kd/de/7//hP
3343 ;; P/mNv/zB/V/84Y+b//oPv/zxX9z4vePv//zln/3Xj/+t+4s/v/Gbv358/Cd//exX
3344 ;; 2t8II1t61+DTd5uO9KJWYF37yj4b8Ll9+zb9vXV3Q/29dZf+wrfbd+/AO/jvFgza
3345 ;; un0Lnm/CsN+5tvHViZR+4jDiASz5Xc6dyK8e57uWy73q92ozG+bv/5HPdfa7K9dX
3346 ;; WWoGrNlkL7jbdwTbd7kVshPpDZ+5LeGwlWeBD0qQzirDwSwSYcQ60hE1jdI4tLcz
3347 ;; UOuXbLO1dZdtfvObH9/cuHtza4ttbm1v3Nm+c4cpdbP9qz5r1K4DAGOPRGgFsh9J
3348 ;; 38MnCyc9GTL4Z5ZhHT/QQvW5dcG7IhWuxZ743GYRzIGpNBqE8Jmg4dyzmcUdhz1t
3349 ;; XrFIgoiubwt6/IR20hPM8r1IeBFOH8iox/aaMQ0/p/G4pWY77nREsNxiO04kAo9H
3350 ;; 8lI4w3U29GOAB6kXQiFYzx8QIOkojLtdkF+J4PILEcJCQSAs3GbIcCWYVr6O1ste
3351 ;; j3uw1Sd+l614vidWazV6nujn6PiE9fXJKD2RPH7s2CyIvRYMRNU5jj+QXpeBvUfC
3352 ;; hZ0SSFvAY3YhHSckoUEy2Qfdua6wJQx0hkx2CDAKhgwU2hEiUTNMhwfJ0tuMHUbL
3353 ;; NrNjt497BKuoHRy/OGH32VtxJaO3Gcy3uIPWbB+2dMkDydsOqBJ+1mogBvt05/kC
3354 ;; LFT3L+oLC/jgOvMv1tkfxD5YT3bEZva1F7ttEWRfwyG4YjkzpA2GFknrIqyphRow
3355 ;; MAuzHwR+sM6eicABzQ4dUA4PQ9n1SMMZZEeCyXBHTxNqmofKVEIysO3GHGpRemlK
3356 ;; T0Yjaml8AV+2m6e1Ohq59GJh19+nilBjnxx/ghqMxFXEsh8aAdbb9MQArFPUpxzO
3357 ;; Cp/rrO/EeAtcVBHcUrImvC2iVSsT+9wGmzl/T1Nf8cADI15nYU/AdY5DYbfYyxCu
3358 ;; cnoj2YAPi/tvENLmeyMDnBudwP5VFHDWQDG24IYmpsU87ooRWRhC5A8+BE8k4BaB
3359 ;; a0KPA8r3+3DOkR+M7oSNzJbeJXekbeaw7TqrlyxbPTOzJPrNkqm//YFT83O3m7ww
3360 ;; zfNRO1sjS3x7dCiM2ySHhxOY7XvLEfi+yOqVHO8irxAvvUbrLPYuPH/gpXpa1Eg3
3361 ;; 8rcR3Jjocqf8GJubk4fSWPBg7hCdgbB6/vKIFmgn6EnPM6Np7Hl+rCthC1535f6q
3362 ;; tmvwmHsclWGLCCIAOlww3Q+94AXhTpOd6DtNp8i4CmYClO936FLV1C6S0QUB4OaR
3363 ;; L2qxo9zOMovdbxwcP92/SeEle8lz4jQ4X7qRu+WD5Koe2xB8ezzgFlzREG9aovM8
3364 ;; wv2in0D/+2Lv+Pk+THBiUZvHOyZO8v475R31eewd7O/9/nbqVjoO7zILoyq4Fogw
3365 ;; KnZsbzArsSIAsLimPWNRJKJ4foQhlXydLewETU6D5vjWhSI0I1JtfwCONkYTyJgL
3366 ;; AHjyXhfw+iVQ5wnU+VwaZ0uwc4FswhivESp5g+Q7UhZIAzaUGG7bv5o8A24YsHfU
3367 ;; CWxA7SMze5bpO9UAWo1wS5TL5x2wZrh7Dl465FbJ6eb3UAriAzMjspAXq8WepgKB
3368 ;; 6QzUZLq4n4lwXV3XARG7NvBL8l9wnRIGBocco6MFZpt4N3iLBFdJtlWp3Aly6Y1t
3369 ;; sZx+ZjUNxon8ItUtM41iGKDB5MnYivaoq9po6c8aJQgfDEIu/N1iA/nWTvM7JUj4
3370 ;; tb7YqM9p/MAIwAXa1TsE30DOVlHzkC2yZIotLIdnIvqofIvlkGSOyutom2xVAUB0
3371 ;; ugBlfNevdjyx54EZhCEH5t+MKoGaEWF9GArRgqYvx6HBa42Yh4SYUzWrM7A38b+t
3372 ;; eYMEW2oHeL2Lp8bU4yxvJz+QleiL97Xp59j+bLPYLNOYWm3MRHKJ4FwivyuASASU
3373 ;; oRr/uFHTOO/nwVCnB7TFlinzVJjzBXi2hJ6p+sa5sQNJNrg3HCYompqQnHq2Us+d
3374 ;; Jh5x2JeW9CGFocGGYRqMBqRGjw+f7BchTD3D9zH7ATmuMH9WVKylChEDcNysi0lM
3375 ;; hzuhSEiUBnohMNEJfQoEarHrCbEEOv109xjIeOPo5ZMnb/H7+1pBMD3ipi0ub3qx
3376 ;; 47xPJDS7M6/AUkSITJEsKaEgBUAzOsE59Dp+FsTlQzQEF5Mj0emA3gRtEszhvLdM
3377 ;; PKmWtYoM4NymEMrPRRnXo+dUU0q2iAH3/ECJ02Ir+POAPXzIvpXEGXagbfZbbHNj
3378 ;; YyO11w8B3s0B7+aBdyuA5+VdXXHVD0suAlU9MDqAcxCWBIYw8IPCHVhjncB339pc
3379 ;; uDr+jL0QjuA2hjGLByJaebOapD1F0EX2+Pnx07ePdvafHh9VEZEZUA9gpAiaHSkc
3380 ;; u5LgSDJMzZ6Z0s/K2mohXTIuSSVOhaXUrLWqJVC16O2Ip5/eVMlUvhqQATu9qeBa
3381 ;; WbzpDz056yb5+mbE2yWHbpRqxIsGmsYCnewJT78Iewm1DHnfUuVRQDQehj6JF/M9
3382 ;; ZwhpnbAuBJYSJujuNWNnJlyPNScj2mTQ05qWqQR9AniYR18xdTFaaHXMSuGEjRzB
3383 ;; 7UrVEkpbJKUuPc4IW5j4aVKI4KEMM/VDo3m+CkoXqSbMi/YqS+UHYwjAxhPqaAZZ
3384 ;; qyr8EZEuDNqB9BvFqplfesOlD+GTflMjZs8FEvs1llsauJPGA3lWcmBYD2Kn0aid
3385 ;; nUZna6HvCuLWa/h7Z3fvbELiU4mPWcKpUtKrw5P9F8929vax7AmvTr36dGBeibBe
3386 ;; QVhvnLDGjPN2TDam1Mhen7VGV6G0MbtQpfGiD5NdD8P16zNlxlj+T2sHHT9GVxng
3387 ;; U7pBqWXnqmasDoZB65ITIQHq5+YGJMVf6svo72e1+arfpkwy6gB17EhpjFJRlZ4z
3388 ;; FK8wA5Jgq2qWuIqEh1fKLIdhtATB/CqLUzpvT8FAy2ZnK2hMkJJ7NmSJq3nEVuT2
3389 ;; ESjxz2ZS9oymXVEvsd1APosrXJ+AllZJUjxOyWgZLLHkCiRFJamLtaw9qsIkh91C
3390 ;; qEpJK+072axRTo69kx5vLN0k8CzGjGaJzs2Wutc3pggGtAQ5iRm6nkRiJIyJqLlE
3391 ;; /h2jS0Zk8wFLmfmXDV2GvTAD9vWFhd040ikOtQM6MvELGNPawuI4C7nYA7TeMIr1
3392 ;; EcDUfB5JqNTTfICOCGSkfLPHIWkKY7A/8FAmZVpYUG6yeotTbLDFXpHsbaH8YBXo
3393 ;; lJiY8k2EHHcQPR6y+oO6Kv+ZonNmCdJ7eCH7VPcNS2pCpw/moqHa3oXbj4ZEUMpy
3394 ;; D8/PDdAFqjwRa5url4lg5vVmrfhkq/Yl3FO6qtqxlQku3LawbWFCVbhZkFAPT8Zt
3395 ;; trT3Sgk1WDtM1NWGdOTWhJFrLNsY084rTycTqFsaagakrawOJ2x9q2zrhe2P2bja
3396 ;; LXHQuA+3HB3AaEiZg05iqt2ElBa8xehBPhM2h/hhrZsgqlo1ND7ZWK+jL1/HD4jG
3397 ;; NYOoIlu5B6Sn70iLR4KAwhSj02m3273eYDKWPhPVefK9DuAZWst3yuZPMV3slwpw
3398 ;; b0r9siW4sXCKTbqfTdrrmD7aPXZEw9V1Jv+UrQKnqkqyE0iaBDmIfMJH/BCeLlu6
3399 ;; VpTsq9NrH+w+Gkh1aKOlmFnl0IKYPNmqWNbSnMt4q/lFGI+fLZea9Wa8JmzpULVF
3400 ;; qs+x0D7JSdm5Gn4+2arVRVeNULj/Qd93uCIJXpogt3LK/8yPWSiEYgXYpORdocoM
3401 ;; HTaEd0EM8YGKKjQ9P/kQF8E2D7AACncm+Qojv8/wQPUE+v/xk51PXmAit9uT9WRn
3402 ;; DXr6wbdfb+9cLrNBD8mkHfj9PhL/yMemDviUhwm0rO4JFnrPPoxh5732cqbdq4o0
3403 ;; sJEgh56At3s59HkKp2bbrdHwLYQNCaASEy9mG+lXL09YclqbIxxrnKZdZp1Kkl72
3404 ;; bhaXf8Q2tz5mjYPjp/s3LQ720nLDrrSLSI9xLx1GaWqmC41W3vtgyLn0rQJWZ4wz
3405 ;; XUcRgM9xm7elIyO8Djq+EudlvXvte71Cle6FdPtwcaLhkAyyk1TiOSanEb8wnJ8l
3406 ;; 0c72S0umwCOBlkLUBF9ox4KML9WRlZk8X9uN1HBQYnbKkdBBHWSuxLrJGSRxXswc
3407 ;; bNHhsWOiX1K0z3vOL+GSaGkHlZdkUGKaWH1p9oGu1yZP0eQqM8lkeYXJA30dsSkU
3408 ;; xt0uxZr0fLZnvBdYB4IcnhypcrbsRdynmiGEsnpW5HqhR4Vztllz8AFb+RLOQJac
3409 ;; gVQ6AYsHg0dRM4HmsGNS46RJw/t9wQM07EEgI7SnMKWHfhz1Y6xTDQcYQhIYdH9Y
3410 ;; AUtbPXQVwYko/7jOBiI5D1aX9dzhDnIaCfxuwN1W2CvsYsIZtIdMJ3iXkA0/PWCB
3411 ;; dYkBgjJMs199GLK4dDK4eO6UVi9DQLNB8MAfmjiMa6vdcJv6MkDJjAsZyKodOTNv
3412 ;; Kb8B6/Bo78nLR/vP99j9yavmFzVuVDLiFRx/UVQFlullyFlhSRO65yZcqc2S/Vd5
3413 ;; DTmL1yBLzBG0yvs2I1sk7GbSLmyVVLGSCPEKaGuz+YBZ24nuXumvX6g572vzzZ1y
3414 ;; B6nnb4Kami63Al/RXasi0hriz4E5YsXp5JgBb4UvkppkLFPS0pGW6q1qDMQf6ans
3415 ;; pQ7XJGhqFHCRFK49cQmjqC9YJL5UwgoHklqGlL36HVwQa2IDP8tcTdnmzckxLpJP
3416 ;; P4pvExEqkhS8kJnam9o7UW6sytUTvdRPjlV/oX4PKHWeaZh36W5BfHkJGikWmt4k
3417 ;; Q1NpZrDJzKlijthslLa58ys3Gs/3P9n/9rMMAygfb2pOjcU3jwPf3W6tTTt1jpmg
3418 ;; oBU9afXDZ61kp00xr9n44tOd5+/fZPsgFYngU93RaIyoafwWJ01fnBOgwRbfrJz4
3419 ;; 7/asd7uWtVqq86lNjGqBfUdcNdNqRJmJKRplGAB6ZLYPCXPe9BbZQ5byr8+vyDbQ
3420 ;; QXcD0YfQzID8Kz7Wwjah14S7GISZmDOqD5cdkfeHxVYaq9jM5Z7p5U638NPPHh0+
3421 ;; r1p5zmgHmnz4sLK6gJ6EupGYzDr+IITBI6fLGmChyYuy5FlD2X7cdkD9jQbyOHTU
3422 ;; juhElXAs44aqjS6Q3R4SO4iwlzyQvO0oOhkWrZhp1MYI7OzCwtsqWStiF+oaSJHj
3423 ;; sA6ceAtLWch1qbiaUuAF/J+9BH8P41/z5udnJAQyO0VosUIFRgypHmSdpmXMhBOK
3424 ;; VqWMrWqvQ0GVCYBEW8VMbWTP5i6zXVIjgFbZ/xR4YP60LTTzHOIkQB6pJ56dxzs5
3425 ;; foulqcYYJEjPRQAsyRMDql8Kz3L8UDU5AD2PhyM24Sn+3cpdO1ga4z91KPsuhrmo
3426 ;; FQBn+QrO/9rXn68/X3/+335UaVlx/7RrF/K+pbovES9UFinfSbIEXcfP93DRnxUj
3427 ;; xWvGztAbTw5jkO6wsM8tMRn0tKZlKkGfAB7m0VfgayS9GLaCC62OWSmcsJGjtL0B
3428 ;; a4TSFoWGrRG2MPHThGPwUIZdLzNDf/gqZWVGE+ZFe5Wl8gNXwHpKQgfNIGuVubET
3429 ;; SRWX8oN22H0Sq2Z+6Q2XPoRP+k2NmKNhq5NHsLMxjahngW8RjzVdHWSs7DQatbPT
3430 ;; 6GzN0JY1/L2zu3c2gepV4iMJOlVKenV4sv/i2c7ePraN4NWpV58OzCsR1isI640T
3431 ;; 1phx3o7JxpQa2euz1ugq1FDLLlRpvMhhZNfDMuLrM2XGSAvb3LqIpHURAlePkZsE
3432 ;; RBa9nGXbsesOQTfnwur5rA6GQeuSEyEB6ufmBuh59DL5fjZXxsGWsIRHjKk1kqu5
3433 ;; On1MhmR5WImewzjsS0v6cVicwbaZVTVLXEWC0jCzXAcyrhIE86sEBOib6AKBTMFA
3434 ;; y2ZnK6qfqWrTq3nEVuT2ESjxz2ZS9oymXVEvsd14fPhkH1e4PgFNX4EjP4MHfJoD
3435 ;; Ky2BRdQqJNX3sTAiLWuPqjBVPo5QlZJW2neyWaMcj7uFk7mxdLNlqsHzNYvQuemK
3436 ;; dmkJIelEQN4dsszQ9SQSY1kuETVXS3vH6JJRfpOUd78K6DLshRmwry8s7MaRKvwP
3437 ;; fKyWdGTiFzCmZWqQsB+JDZg4KcgvLER+V1A1diCjnjJ2alQ8QEcEMlLxlBowYQz2
3438 ;; Bx7KtKMWFpSbrN7iFBtssVckOxAk8oNVoFNiYso2EXLcQWDKXn9QX2ftmHqhHmV+
3439 ;; mSVI7+GF7Iek8kJZFvFPH2TsfeZgLtx+NCSCEpZ1OfzcAI5VnyJ3TBsxmQhmXm/W
3440 ;; ik+2al/CPW3pmh86tjLBhdsWNqTQiSsNNwsS6uHJuM2W9l4poQZrh4m6NZuO3Jow
3441 ;; co1Ga6eaOK88nUygbmmoGZC2sjqcsPWtsq0Xtj9m42q3xEHjPtxydACjIWUOOkld
3442 ;; YdVBGT3IZwJ7TdJaTxu81Cqm8cnGeh19+UztNIgqspV7QHr6jrR4JHItOMDodNrt
3443 ;; dq83mIylz4QEAbvuAJ6htXynbP4U08V+qQD3ptQvW1KN0ibdzybttYyuG70cZfqq
3444 ;; 5J9izwNvFYY8GGZUlWQnkDQJchD5hI/4ITxdtpazvVHGO732we6jgazqa80qhxbE
3445 ;; tMKsimUtzbmMt5pfhPH4GZeXrjdrR3fp0LvkjrTxHP8X
3447 ;; -----END PGP MESSAGE-----
3451 (add-hook 'tinyprocmail-:mode-define-keys-hook
3452 'tinyprocmail-mode-define-keys)
3454 (add-hook 'tinyprocmail-output-:mode-define-keys-hook
3455 'tinyprocmail-output-mode-define-keys)
3457 (provide 'tinyprocmail)
3458 (run-hooks 'tinyprocmail-:load-hook)
3460 ;;; tinyprocmail.el ends here