]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinyprocmail.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinyprocmail.el
1 ;;; tinyprocmail.el --- Emacs procmail minor mode. Lint code checker.
2
3 ;; This file is not part of Emacs
4
5 ;;{{{ Id
6
7 ;; Copyright (C)    1997-2007 Jari Aalto
8 ;; Author:          Jari Aalto
9 ;; Maintainer:      Jari Aalto
10 ;; Created:         1997-09
11 ;; Keywords:        extensions
12 ;;
13 ;; To get information on this program, call M-x tinyprocmail-version.
14 ;; Look at the code with folding.el.
15
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)
19 ;; any later version.
20 ;;
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
24 ;; for more details.
25 ;;
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.
30 ;;
31 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
32
33 ;;}}}
34 ;;{{{ Install
35
36 ;;; Install:
37
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.
42 ;;
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)
46 ;;
47 ;;      (setq tinyprocmail-:procmail-version "v3.11pre7")
48 ;;      (add-hook 'tinyprocmail-:load-hook 'tinyprocmail-install)
49 ;;      (require 'tinyprocmail)
50 ;;
51 ;; You can also use the preferred way: autoload
52 ;;
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)
57 ;;
58 ;;      ;;  Procmail files usually end to suffix "*.rc", like file-name.rc
59 ;;      ;;  Some older procmail files start with "rc.*", like rc.file-name
60 ;;
61 ;;      (autoload 'aput "assoc")
62 ;;      (aput 'auto-mode-alist
63 ;;            "\\.\\(procmail\\)?rc$"
64 ;;            'turn-on-tinyprocmail-mode)
65 ;;
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.
70 ;;
71 ;;      M-x tinyprocmail-install-files
72 ;;
73 ;;  If you have any questions, use this function to contact author
74 ;;
75 ;;       M-x tinyprocmail-submit-bug-report
76
77 ;;}}}
78 ;;{{{ Documentation
79
80 ;; ..................................................... &t-commentary ...
81
82 ;;; Commentary:
83
84 ;;  Preface, Sep 1997
85 ;;
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.
99 ;;
100 ;;  What is Procmail?
101 ;;
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/>.
110 ;;
111 ;;  Some terms
112 ;;
113 ;;      ._UBE_ = Unsolicited Bulk Email.
114 ;;      ._UCE_ = (subset of UBE) Unsolicited Commercial Email.
115 ;;
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
121 ;;      at windmills
122 ;;
123 ;;  Overview of features
124 ;;
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
128 ;;          on the fly.
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 -*-".
134 ;;
135 ;;      Quick reference
136 ;;
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.
141 ;;
142 ;;      Required packages
143 ;;
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.
147 ;;
148 ;;  Writing procmail code
149 ;;
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.
153 ;;
154 ;;          TAB     tinytab-tab-key                         tinytab.el
155 ;;
156 ;;      The RET autoindents, but this can be turned off by calling
157 ;;
158 ;;          C-c ' RET   tinytab-return-key-mode
159 ;;
160 ;;      Whole regions can be adjusted with commands
161 ;;
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    <-->
165 ;;
166 ;;  Tabs and spaces
167 ;;
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'.
174 ;;
175 ;;  Aligning continuation lines with backslashes
176 ;;
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
181 ;;      backslashes.
182 ;;
183 ;;          :0 fh
184 ;;          * condition
185 ;;          | (formail -rt | \
186 ;;             cat -; \
187 ;;             echo "Error: you requested file"; \
188 ;;             echo "that does not exist";\
189 ;;             ) | $SENDMAIL -t
190 ;;
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
195 ;;      the same cloumn.
196 ;;
197 ;;          :0 fh
198 ;;          * condition
199 ;;          | (formail -rt |                                    \
200 ;;             cat -;                                           \
201 ;;             echo "Error: you requested file";                \
202 ;;             echo "that does not exist";                      \
203 ;;             ) | $SENDMAIL -t
204 ;;
205 ;;  Rules on how to write Procmail recipe
206 ;;
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.
211 ;;
212 ;;       [recipe start]
213 ;;
214 ;;          :   # (old way) although legal procmail, illegal here. Use `:0'
215 ;;
216 ;;       [flag order]
217 ;;
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."
229 ;;
230 ;;          :0 aAeE HBD fhbwWirc: LOCKFILE
231 ;;             |    |   |  |   |
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
237 ;;             receipe.
238 ;;
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.
242 ;;
243 ;;          (setq tinyprocmail-:flag-and-recipe-start-style 'flags-together)
244 ;;
245 ;;       [lockfile]
246 ;;
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.
253 ;;
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)
258 ;;
259 ;;       [condition line]
260 ;;
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.
268 ;;
269 ;;          * H B ?? regexp # valid procmail, llegal here: write "HB"
270 ;;
271 ;;       [Variables]
272 ;;
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 `:-'.
278 ;;
279 ;;          DUMMY  = yes        # Warning, did you mean DUMMY = $yes ?
280 ;;          VAR    = ${VAR:-1}  # No spaces allowed: "$ {" is illegal.
281 ;;
282 ;;       [program names]
283 ;;
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.
288 ;;
289 ;;       [commenting style]
290 ;;
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
296 ;;      spaces.
297 ;;
298 ;;          * condition1    --> * condition
299 ;;          # comment               # comment
300 ;;          # comment               # comment
301 ;;          * condition2        * condition
302 ;;
303 ;;      This is recommended for readability (separating conditions
304 ;;      from comments) and Lint will try to fix these comment misplacements.
305 ;;
306 ;;       [redirecting to a file]
307 ;;
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
311 ;;      regexps).
312 ;;
313 ;;          :0 :
314 ;;          | echo > test.tmp       # Ok. Do not use "echo>test.tmp"
315 ;;
316 ;;  Linting procmail code
317 ;;
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
322 ;;      that
323 ;;
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.
329 ;;
330 ;;      To help *Linting* you procmail code, there are two functions
331 ;;
332 ;;          C-c ' l         tinyprocmail-lint-forward
333 ;;          C-c ' L         tinyprocmail-lint-buffer
334 ;;
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.
342 ;;
343 ;;          *** 1997-10-19 19:37 (pm-test.rc) tinyprocmail.el 1.10
344 ;;          cd /users/foo/pm/
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.
350 ;;
351 ;;      The output buffer can be sorted and you can move between blocks
352 ;;      with commands
353 ;;
354 ;;          sl      tinyprocmail-output-sort-by-line
355 ;;          se      tinyprocmail-output-sort-by-error
356 ;;          b       tinyprocmail-output-start
357 ;;          e       tinyprocmail-output-end
358 ;;
359 ;;  Lint: auto-correct
360 ;;
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.
365 ;;
366 ;;  Lint: directives
367 ;;
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.
375 ;;
376 ;;          # Lint: <Lint flags here>
377 ;;          :0 FLAGS
378 ;;
379 ;;      The comment must be in the previous line, the following is _not_
380 ;;      recognized.
381 ;;
382 ;;          # Lint: <Lint flags here>
383 ;;          #   I'm doing some odd things here and ....
384 ;;          :0 FLAGS
385 ;;
386 ;;      Here is list of recognized Lint directives. each directive must have
387 ;;      leading space.
388 ;;
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
395 ;;          this directive.
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
398 ;;          (c) copy flag.
399 ;;
400 ;;  Lint: error messages
401 ;;
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
406 ;;      definitions.
407 ;;
408 ;;  Lint: batch mode from command line
409 ;;
410 ;;      You can also lint procmail files from command line prompt
411 ;;      like this.
412 ;;
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)) '
419 ;;
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'
425 ;;
426 ;;* #!/bin/sh
427 ;;* # pm-lint.sh -- LINT A procmail batch lint with emacs tinyprocmail.el
428 ;;* #
429 ;;* file=$1
430 ;;* #
431 ;;* EMACS=emacs
432 ;;* out=$HOME/pm-lint.lst
433 ;;* #
434 ;;* #  notice all these 3 lines must be concatenaed together! There must be
435 ;;* #  no \ continuation characters. to the right.
436 ;;* #
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
440 ;;* #
441 ;;* # end of pm-lint.sh
442 ;;
443 ;;  Highlighting
444 ;;
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.
449 ;;
450 ;;          $VAR = ${VAR:-"no"}
451 ;;          |===
452 ;;          |Error, you must not place '$' to the left here.
453 ;;          |
454 ;;          This dollar($) sign is not highlighted.
455
456 ;;}}}
457
458 ;;; Change Log:
459
460 ;;; Code:
461
462 ;;{{{ setup: require
463
464 (require 'tinylibm)
465
466 (eval-when-compile (ti::package-use-dynamic-compilation))
467
468 (eval-and-compile
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
475     (message "\
476   ** tinyprocmail.el: overlay-* functions missing from this Emacs.")))
477
478 (ti::package-defgroup-tiny TinyProcmail tinyprocmail-: extensions
479   "Procmail log minor mode
480   Overview of features
481
482 o   Minor mode for writing Procmail recipes (use tab for
483     indenting)
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 -*-'")
492
493 ;;}}}
494 ;;{{{ setup: public
495
496 ;;; ......................................................... &v-hooks ...
497
498 (defcustom tinyprocmail-:load-hook nil
499   "*Hook run when file has been loaded."
500   :type  'hook
501   :group 'TinyProcmail)
502
503 (defcustom tinyprocmail-:lint-before-hook nil
504   "*Hook run before `tinyprocmail-lint-forward'."
505   :type  'hook
506   :group 'TinyProcmail)
507
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.
512
513 Call arguments:
514
515  FLAGS          Read flags
516  STD-FLAGS      Standardized flag sequence.
517
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)
522
523 (defcustom tinyprocmail-:lint-after-hook nil
524   "*Hook run after `tinyprocmail-lint-forward'."
525   :type  'hook
526   :group 'TinyProcmail)
527
528 (defcustom tinyprocmail-:lint-after-hook nil
529   "Hook run when `tinyprocmail-lint-forward' is about to finish."
530   :type  'hook
531   :group 'TinyProcmail)
532
533 ;;; ..................................................... &v-functions ...
534
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'.
540
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'"
544   :type  'function
545   :group 'TinyProcmail)
546
547 ;;; .......................................................... &public ...
548
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:
553
554   :0 hi:
555   | echo \"status info\" > file
556
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."
560   :type  'string
561   :group 'TinyProcmail)
562
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
566
567   :0flags:
568
569 If any other value, then receipe start looks like
570
571   :0 flags:"
572   :type '(choice
573           (const flags-together)
574           (const nil))
575   :group 'TinyProcmail)
576
577 (eval-and-compile
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")))
584       (if (null prg)
585           (message "\
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"))))))
589
590 (defcustom tinyprocmail-:procmail-version (tinyprocmail-procmail-version)
591   "The version number returned by `procmail -v'."
592   :type  'string
593   :group 'TinyProcmail)
594
595 (defcustom tinyprocmail-:font-lock-keywords
596   (list
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
600    ;;
601    ;;   [   \n]
602    ;;   ^   ^^^highlighted.
603    '("\\[[^]\n]*\t[^]\n]*\\]"       . font-lock-keyword-face)
604
605    '("#.*"                          . font-lock-comment-face)
606    ;;   Recipe start :0
607    ;;   The regexp says: Start with `:0' or `:' followed by spaces
608    ;;   and characters, but you MUST ent with non-space. This matches
609    ;;   `spaced' flags:
610    ;;
611    ;;       :0 B fh wi
612    ;;
613    ;;   But also `tight' flags
614    ;;
615    ;;       :0Bfhwi
616    '("^[\t ]*\\(:0? *[ a-zA-Z]+[^ #\n]+\\)" 1 font-lock-type-face)
617    '("^[\t ]*\\(:0\\)"                      1 font-lock-type-face)
618    ;;  Special condiion line
619    ;;
620    ;;  * ! BH ?? regexp
621    '("^[ \t]*\\*.*\\<\\(HB\\|BH\\)\\>.*[?][?]"  1 font-lock-reference-face)
622    '("^[ \t]*\\*.*\\<\\([BH]\\)\\>.*[?][?]"     1 font-lock-reference-face)
623    ;;  Special variable assignments
624    (list
625     (concat
626      "[^_a-zA-Z0-9]"
627      "\\("
628      "VERBOSE"
629      "\\|DELIVERED"
630      "\\|COMSAT"
631      "\\|LOG"
632      "\\|EXITCODE"
633      "\\|LOGFILE"
634      "\\|LOGABSTRACT"
635      "\\|MAILDIR"
636      "\\|HOST"
637      "\\|FROM_DAEMON"
638      "\\|FROM_MAILER"
639      "\\|TO_?"
640      "\\|FORMAIL"
641      "\\|SENDMAIL"
642      "\\)"
643      "[^_a-zA-Z0-9]")
644     1 'font-lock-reference-face)
645    ;;  variable expansion condition or variable extrapolation
646    ;;
647    ;;     * $
648    ;;
649    ;;     ${var}
650    ;;
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
655    ;;  $VAR = "value"
656    ;;  LOG = "value"
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
662    (list
663     (concat
664      "[^_a-zA-Z0-9]\\(" ;; Do not put \\< here
665      "test"
666      "\\|awk"
667      "\\|cat"
668      "\\|cut"
669      "\\|echo"
670      "\\|formail"
671      "\\|[ezba]*?grep"
672      "\\|head"
673      "\\|perl"
674      "\\|python"
675      "\\|sed"
676      "\\|sendmail"
677      "\\|tail"
678      "\\)[^_a-zA-Z0-9]")
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."
683   :type  'sexp
684   :group 'TinyProcmail)
685
686 (defcustom tinyprocmail-:lint-font-lock-keywords
687   (list
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.
693    ;;
694    ;;   Error
695    ;;   Pedantic
696    ;;   Warning
697    ;;   info
698    '("info,"             0 font-lock-comment-face))
699   "*Font lock keywords."
700   :type 'sexp
701   :group 'TinyProcmail)
702
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'."
706   :type '(repeat
707           (list
708            (regexp :tag "Regexp to match filename")
709            (const 'tinyprocmail-mode)))
710   :group  'TinyMbx)
711
712 (defcustom tinyprocmail-:lint-fix-mode 'semi
713   "*The mode of fixing code.
714 'auto   Automatic fixing.
715 'semi   Ask permission to fix.
716 nil     no fixing."
717   :type '(choice
718           (const nil)
719           (const auto)
720           (const semi))
721   :group 'TinyProcmail)
722
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
726 procmail releases."
727   :type '(choice
728           (const nil)
729           (const pedantic))
730   :group  'TinyProcmail)
731
732 (defcustom tinyprocmail-:lint-log nil
733   "*If non-nil receord lint check to `tinyprocmail-:lint-output-buffer'."
734   :type 'boolean
735   :group 'TinyProcmail)
736
737 ;;}}}
738 ;;{{{ setup: private
739
740 ;;; ......................................................... &private ...
741
742 (defvar tinyprocmail-:overlay nil
743   "Overlay used.")
744
745 (defvar tinyprocmail-:overlay-second nil
746   "Overlay used.")
747
748 (defvar tinyprocmail-:lint-output-buffer "*Procmail Lint*"
749   "Log buffer for Lint.")
750
751 (defvar tinyprocmail-:lint-output-file "~/pm-lint.out"
752   "Where `tinyprocmail-lint-buffer-batch' should save the results.")
753
754 (defvar tinyprocmail-:mode-output-map nil
755   "Map useed in `tinyprocmail-:lint-output-buffer'.")
756
757 (defvar tinyprocmail-:mode-output-easymenu  nil
758   "Ooutput mode menu.")
759
760 (eval-and-compile
761   (ti::macrof-version-bug-report
762    "tinyprocmail.el"
763    "tiprocmail"
764    tinyprocmail-:version-id
765    "$Id: tinyprocmail.el,v 2.51 2007/05/01 17:20:53 jaalto Exp $"
766    '(tinyprocmail-:version-id)))
767
768 ;;}}}
769
770 ;;; ############################################################ &mode ###
771
772 ;;{{{ mode
773
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)
779
780 (eval-and-compile
781
782   (ti::macrof-minor-mode-wizard
783    "tinyprocmail-" " PM" "\C-c'"  "PM" 'TinyProcmail "tinyprocmail-:"
784    "Procmail coding minor mode.
785
786 Code writing: `tinytab-mode' on \\[tinytab-mode]
787
788 Mode description (main mode)
789 \\{tinyprocmail-:mode-prefix-map}"
790
791    "Procmail recipe coding"
792
793    (progn                              ;Some mode specific things? No?
794      (cond
795       (tinyprocmail-mode
796        (setq comment-start       "#"
797              comment-start-skip  "#+[ \t]*"
798              comment-end         ""
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.
802        ;;
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
809        (unless tinytab-mode
810          (turn-on-tinytab-mode)))
811       (t
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))))
817
818    "Procmail mode"
819    (list                                ;arg 10
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]
825     "----"
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]
832     "----"
833     (list
834      "Comment Hiding"
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])
839     (list
840      "Misc"
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])
845     "----"
846     ["Mode off"                    turn-off-tinyprocmail-mode            t])
847    (progn
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))))
874
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)
878
879 (eval-and-compile
880
881   (ti::macrof-minor-mode-wizard
882    "tinyprocmail-output-" " PM-Lint" "\C-c'"  "PM-Lint"
883    'TinyProcmail "tinyprocmail-output-:"
884
885    "Browsing Procmail lint output. See \\[tinyprocmail-lint]
886
887 Mode description
888
889 \\{tinyprocmail-output-:mode-prefix-map}"
890    "tinyprocmail"
891    (progn
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 "*** "))))
896                 (null (y-or-n-p
897                        "No Prcomail Lint output found. Are you sure? ")))
898        (setq tinyprocmail-output-mode nil)
899        (error "Aborted."))
900      (tinyprocmail-font-lock-keywords
901       tinyprocmail-:lint-font-lock-keywords 'lint-font-lock))
902    "Procmail Lint output mode"
903    (list                                ;arg 10
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]
910     "----"
911     ["Sort by line number"       tinyprocmail-output-sort-by-line    t]
912     ["Sort by error"             tinyprocmail-output-sort-by-error   t]
913     "----"
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])
918    (progn
919      (if (ti::emacs-p)
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))))
932
933 ;;; ......................................................... &install ...
934
935 ;;; ----------------------------------------------------------------------
936 ;;;
937 ;;;###autoload (autoload 'tinyprocmail-install-files "tinyprocmail" t t)
938 (ti::macrof-install-pgp-tar tinyprocmail-install-files  "tinyprocmail.el")
939
940 ;;; ----------------------------------------------------------------------
941 ;;;
942 ;;;###autoload
943 (defun turn-off-tinyprocmail-mode-all-buffers (&optional verb)
944   "Call `turn-on-tinyprocmail-mode-all-buffers' with parameter `off'. VERB."
945   (interactive)
946   (ti::verb)
947   (turn-on-tinyprocmail-mode-all-buffers 'on verb))
948
949 ;;; ----------------------------------------------------------------------
950 ;;;
951 ;;;###autoload
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
955 must match `^:0'."
956   (interactive "P")
957   (ti::verb)
958   (dolist (buffer (buffer-list))
959     (with-current-buffer buffer
960       (when (and (if off tinyprocmail-mode (null tinyprocmail-mode))
961                  buffer-file-name
962                  (string-match
963                   "^rc\\.\\|\\.rc$\\|procmailrc"
964                   buffer-file-name)
965                  (ti::re-search-check "^:0"))
966         (if  verb (message "TinyProcmail: Mode turned %s in %s"
967                            (if off "off" "on") (buffer-name)))
968         (if off
969             (turn-off-tinyprocmail-mode)
970           (turn-on-tinyprocmail-mode))))))
971
972 ;;; ----------------------------------------------------------------------
973 ;;;
974 (defun tinyprocmail-install-auto-mode-alist (&optional uninstall)
975   "Update `auto-mode-alist' to know about procmail *.rc files."
976   (cond
977    (uninstall
978     (ti::assoc-replace-maybe-add
979      'auto-mode-alist
980      tinyprocmail-:auto-mode-alist
981      'remove))
982    (t
983     (ti::assoc-replace-maybe-add
984      'auto-mode-alist
985      tinyprocmail-:auto-mode-alist)
986     (if (interactive-p)
987         (message "TinyProcmail: uninstalled")))))
988
989 ;;; ----------------------------------------------------------------------
990 ;;;
991 (defun tinyprocmail-install (&optional uninstall verb)
992   "Install package, or optionally UNINSTALL. VERB."
993   (interactive "P")
994   (ti::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)
1001                  uninstall)
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)
1009                  uninstall)
1010   (tinyprocmail-install-auto-mode-alist uninstall)
1011   (turn-on-tinyprocmail-mode-all-buffers uninstall verb))
1012
1013 ;;; ----------------------------------------------------------------------
1014 ;;;
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)))
1020
1021 ;;}}}
1022
1023 ;;; ########################################################### &macro ###
1024
1025 ;;{{{ macro
1026
1027 ;;; ----------------------------------------------------------------------
1028 ;;;
1029 (defmacro tinyprocmail-o (&rest body)
1030   "Move overlay to point and protect BODY. Overlay is hiddedn after body."
1031   (`
1032    (unwind-protect
1033        (progn
1034          (tinyprocmail-overlay (point))
1035          (,@ body))
1036      (tinyprocmail-overlay-hide))))
1037
1038 ;;; ----------------------------------------------------------------------
1039 ;;;
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."
1043   (`
1044    (let ((buffer (get-buffer tinyprocmail-:lint-output-buffer)))
1045      (when buffer
1046        (with-current-buffer buffer
1047          (,@ body))))))
1048
1049 ;;; ----------------------------------------------------------------------
1050 ;;;
1051 (put 'tinyprocmail-fix-macro 'lisp-indent-function 1)
1052 (defmacro tinyprocmail-fix-macro (message &rest body)
1053   "Fix. Display MESSAGE and do BODY."
1054   (`
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)))))
1058      (,@ body))))
1059
1060 ;;; ----------------------------------------------------------------------
1061 ;; fmacro = function create macro
1062 ;;;
1063 (defmacro tinyprocmail-fmacro-move (back method)
1064   "Make move function using BACK and METHOD."
1065   (let* ((sym (intern
1066                (format "tinyprocmail-%s-%s"
1067                        (if back
1068                            "backward"
1069                          "forward")
1070                        (symbol-name   (` (, method)))))))
1071     (`
1072      (defun (, sym) ()
1073        (interactive)
1074        (tinyprocmail-forward (quote (, back)) (quote (, method)))))))
1075
1076 ;;}}}
1077 ;;{{{ misc
1078
1079 ;;; ............................................................. misc ...
1080
1081 ;;; ----------------------------------------------------------------------
1082 ;;;
1083 (defsubst tinyprocmail-comment-line-p ()
1084   "Check if this ine is full comment line. Use `save-excursion'."
1085   (save-excursion
1086     (beginning-of-line)
1087     (looking-at "^[ \t]*#")))
1088
1089 ;;; ----------------------------------------------------------------------
1090 ;;;
1091 (defsubst tinyprocmail-comment-line-pp ()
1092   "Check if this ine is full comment line at current point forward."
1093   (looking-at "^[ \t]*#"))
1094
1095 ;;; ----------------------------------------------------------------------
1096 ;;;
1097 (defsubst tinyprocmail-string-valid-p (string &optional type)
1098   "Check is STRING is valid variable. Find any supicious character.
1099 Input:
1100   STRING    variable or read filename.
1101   TYPE      if 'path; then check as path."
1102   (cond
1103    ((eq type 'path)
1104     (string-match "^[-_a-zA-Z0-9.$\\/@]+$" string))
1105    (t
1106     (string-match "^[_a-zA-Z0-9]+$" string))))
1107
1108 ;;; ----------------------------------------------------------------------
1109 ;;;
1110 (defun tinyprocmail-supported-p (feature)
1111   "Check if FEATURE is supported by `tinyprocmail-:procmail-version'."
1112   (let* ((v (or tinyprocmail-:procmail-version "")))
1113     (cond
1114      ((eq feature 'condition-middle-comment)
1115       (string-match "3.11pre7" v))
1116      ((error "Invalid feature %s" feature)))))
1117
1118 ;;; ----------------------------------------------------------------------
1119 ;;;
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
1123 # Lint: -w
1124 :0 fh
1125 | doSomething
1126
1127 Return:
1128
1129   str    directive flags."
1130   (save-excursion
1131     (if start-point (goto-char start-point))
1132     (end-of-line)
1133     (when (and (re-search-backward "^[ \t]*:0" nil t)
1134                (forward-line -1)
1135                (looking-at "^[ \t]*#[ \t]*Lint:\\(.*\\)"))
1136       (match-string 1))))
1137
1138 ;;; ----------------------------------------------------------------------
1139 ;;;
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)))
1144
1145 ;;; ----------------------------------------------------------------------
1146 ;;;
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)
1157                    (priority       1)
1158                    (face           highlight)
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 " <<"))))
1165     (save-excursion
1166       (goto-char point)
1167       (move-overlay tinyprocmail-:overlay
1168                     (line-beginning-position)
1169                     (line-end-position)
1170                     (current-buffer))
1171       (goto-char (line-end-position))
1172       (move-overlay tinyprocmail-:overlay
1173                     (line-beginning-position)
1174                     (line-end-position)
1175                     (current-buffer)))))
1176
1177 ;;; ----------------------------------------------------------------------
1178 ;;;
1179 (defun tinyprocmail-overlay-hide ()
1180   "Move overlay out of sight."
1181   (interactive)
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  "")))))
1189
1190 ;;; ----------------------------------------------------------------------
1191 ;;;
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))
1195     (when (boundp sym)
1196       (if restore
1197           (set sym (get 'tinyprocmail-mode property))
1198         (put 'tinyprocmail-mode property (symbol-value sym))
1199         (set sym keywords)))))
1200
1201 ;;; ----------------------------------------------------------------------
1202 ;;;
1203 (defun tinyprocmail-log (point string &optional point-min)
1204   "Log POINT and STRING to lint buffer if `tinyprocmail-:lint' is non-nil.
1205
1206 Input:
1207
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))
1221            (LINE   (if point
1222                        (save-excursion
1223                          (goto-char point)
1224                          (count-lines
1225                           (or point-min (point-min))
1226                           (if (bolp)
1227                               (1+ (point))
1228                             (point)))))))
1229 ;;;      (if (eq point 4266) (ti::d! LINE point point-min (bolp) ))
1230       (with-current-buffer buffer
1231         (ti::pmax)
1232         (if (and point string)
1233             (insert
1234              (format
1235               "%s:%03d: %s\n"
1236               name
1237               LINE
1238               string))
1239           (insert string))))))
1240
1241 ;;; ----------------------------------------------------------------------
1242 ;;;
1243 (defun tinyprocmail-log-start ()
1244   "Start log by adding header into list log buffer."
1245   (tinyprocmail-log
1246    nil
1247    (format
1248     (concat
1249      "\n\n"
1250      "*** %s (%s) %s tinyprocmail.el %s\n%s")
1251     (ti::date-standard-date 'minutes)
1252     (buffer-name)
1253     (or tinyprocmail-:procmail-version "")
1254     (ti::string-match "[0-9][0-9.]+" 0 tinyprocmail-:version-id)
1255
1256     (if (buffer-file-name)
1257         (concat "cd " (file-name-directory (buffer-file-name)) "\n")
1258       ""))))
1259
1260 ;;; ----------------------------------------------------------------------
1261 ;;;
1262 (defsubst tinyprocmail-recipe-start-p (&optional line)
1263   "Check if current LINE is recipe start line."
1264   (if line
1265       (string-match "^[ \t]*:0" line)
1266     (save-excursion
1267       (beginning-of-line)
1268       (looking-at "^[ \t]*:0"))))
1269
1270 ;;; ----------------------------------------------------------------------
1271 ;;;
1272 (defun tinyprocmail-condition-line-p (&optional line)
1273   "Check if current LINE is condition line."
1274   (if line
1275       (string-match "^[ \t]*\\*" line)
1276     (save-excursion
1277       (beginning-of-line)
1278       (or (looking-at "^[ \t]*\\*")
1279           (progn
1280             ;;  Peek previous line
1281             ;;  * condition \
1282             ;;    line \
1283             ;;    line-end    << suppose point is here
1284             (forward-line -1)
1285             (looking-at "^[ \t*]+.*[\\]"))))))
1286
1287 ;;; ----------------------------------------------------------------------
1288 ;;;
1289 (defun tinyprocmail-assignment-line-p (&optional line)
1290   "Check if current LINE has assignment."
1291   (if line
1292       (string-match "^[^=]+=" line)
1293     (save-excursion
1294       (beginning-of-line)
1295       (or (looking-at "^[^=]+=")
1296           (progn
1297             ;;  Peek previous line
1298             ;;  * condition \
1299             ;;    line \
1300             ;;    line-end    << suppose point is here
1301             (forward-line -1)
1302             (or (looking-at "^[^=]+=.*[\\][ \t]*$")
1303                 (progn
1304                   (tinyprocmail-skip-continuation-backward)
1305                   (forward-line 1)
1306                   (looking-at "^[^=]+="))))))))
1307
1308 ;;; ----------------------------------------------------------------------
1309 ;;;
1310 (defmacro tinyprocmail-flag-string ()
1311   "Return base flag string."
1312   (` "aAeEHBDfhbwWirc:"))
1313
1314 ;;; ----------------------------------------------------------------------
1315 ;;;
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)))
1319
1320 ;;; ----------------------------------------------------------------------
1321 ;;;
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")))
1326
1327 ;;; ----------------------------------------------------------------------
1328 ;;;
1329 (defun tinyprocmail-brace-p (&optional line)
1330   "Check if cursor is under brace or in brace LINE. Return 'beg, 'end or nil."
1331   (interactive)
1332   (cond
1333    (line
1334     (save-excursion
1335       (cond
1336        ((looking-at "[ \t]*{")
1337         'beg)
1338        ((looking-at "[ \t]*}")
1339         'beg))))
1340    (t
1341     (or (if (char= (following-char) ?{) 'beg)
1342         (if (char= (following-char) ?}) 'end)))))
1343
1344 ;;; ----------------------------------------------------------------------
1345 ;;;
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."
1349   (interactive)
1350   (let* ((opoint (point))
1351          (point  opoint))
1352     ;;  Suppose there is no action line at all
1353     ;;
1354     ;;  :0
1355     ;;  * test
1356     ;;
1357     ;;  # comment
1358     ;;  :0[*] point is somewhere here, previous condition end.
1359     (beginning-of-line)
1360     ;;  - Eat all newlines and comments backward
1361     (setq point (point))
1362     (if (tinyprocmail-recipe-start-p)
1363         (backward-line 1))
1364     (when (tinyprocmail-skip-comments-backward)
1365       (if (tinyprocmail-condition-line-p)
1366           (forward-line 1))
1367       (setq point (point)))
1368     (goto-char point)
1369     (skip-chars-forward " \t")
1370     ;;
1371     ;;  :0  :0   :0   :0    :0        :0
1372     ;;  !   |    {    mbox  /dev/null $mbox
1373
1374     (when (not (looking-at "[|!/][^#\n]+\\|[$]?[A-Z]\\|{"))
1375       (point))))
1376
1377 ;;}}}
1378 ;;{{{ move: primitives
1379
1380 ;;; ----------------------------------------------------------------------
1381 ;;;
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)))
1387     (if list
1388         (apply 'min list))))
1389
1390 ;;; ----------------------------------------------------------------------
1391 ;;;
1392 (defun tinyprocmail-skip-regexp (regexp &optional backward)
1393   "Skip all lines that match `looking-at' REGEXP. Optionally BACKWARD."
1394   (let* (done)
1395     (while (and (not (eobp))
1396                 (looking-at regexp))
1397       (setq done t)
1398       (if backward
1399           (backward-line 1)
1400         (forward-line 1)))
1401     (when done
1402       (if backward
1403           (skip-chars-backward " \t")
1404         ;;  Go to first char in the line
1405         (skip-chars-forward " \t")))))
1406
1407 ;;; ----------------------------------------------------------------------
1408 ;;;
1409 (defun tinyprocmail-skip-comments-forward ()
1410   "Skip all comments and whitespace lines."
1411   (tinyprocmail-skip-regexp "[ \t]*#\\|[ \t]*$"))
1412
1413 ;;; ----------------------------------------------------------------------
1414 ;;;
1415 (defun tinyprocmail-skip-comments-backward ()
1416   "Skip all comments and whitespace lines."
1417   (tinyprocmail-skip-regexp "[ \t]*#\\|[ \t]*$" 'back))
1418
1419 ;;; ----------------------------------------------------------------------
1420 ;;;
1421 (defun tinyprocmail-skip-continuation-forward ()
1422   "Skip lines that end to backslash."
1423   (tinyprocmail-skip-regexp ".*[\\][ \t]*$"))
1424
1425 ;;; ----------------------------------------------------------------------
1426 ;;;
1427 (defun tinyprocmail-skip-continuation-backward ()
1428   "Skip lines that end to backslash."
1429   (tinyprocmail-skip-regexp ".*[\\][ \t]*$" 'back))
1430
1431 ;;; ----------------------------------------------------------------------
1432 ;;;
1433 (defun tinyprocmail-move-to-next-condition-line ()
1434   "Move to next line in condition.
1435 Return:
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")
1440             (save-excursion
1441               (beginning-of-line)
1442               ;; :0
1443               (looking-at "^[ \t]*:")))
1444         (forward-line 1)
1445       (forward-line 1)
1446       (when (and (not (looking-at "[ \t]*\\*")) ;; Beginning of recipe
1447                  cont-p)
1448         (forward-line -1)               ;Start from original line
1449         (inline (tinyprocmail-skip-continuation-forward))
1450         (forward-line 1))
1451       ;; * condition
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]*\\*")))
1458
1459 ;;; ----------------------------------------------------------------------
1460 ;;;
1461 (defun tinyprocmail-move-to-condition-end ()
1462   "Go to condition end. Point must inside condition or recipe start."
1463   (let* ()
1464     (cond
1465      ((tinyprocmail-brace-p)
1466       nil)
1467      ((and (tinyprocmail-recipe-start-p)
1468            (save-excursion
1469              (forward-line 1)
1470              (not (looking-at "^[ \t]*\\*"))))
1471       ;;   Handle special case
1472       ;;
1473       ;;   :0 c:
1474       ;;   mailbox
1475       (forward-line 1)
1476       (skip-chars-forward " \t"))
1477      (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.
1485                       ;;
1486                       ;;  :0
1487                       ;;  * condition
1488                       ;;
1489                       ;;  *condition
1490                       ;;  mbox
1491
1492                       (and (looking-at "^[ \t]*$")
1493                            (save-excursion
1494                              (forward-line 1)
1495                              ;;  Must be condition line
1496                              (looking-at "[ \t]*\\*")))))
1497         (skip-chars-forward " \t"))))))
1498
1499 ;;; ----------------------------------------------------------------------
1500 ;;;
1501 (defun tinyprocmail-move-to-macthing-brace (&optional no-adjust)
1502   "Go to { or } brace when sitting on } or {.
1503 Input:
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))
1508            (table       otable))
1509       (modify-syntax-entry ?{ "(" table)
1510       (modify-syntax-entry ?} ")" table)
1511       (put 'tinyprocmail-:mode-name 'syntax-table table)))
1512
1513   (let* ((otable (syntax-table)))
1514     (set-syntax-table (get 'tinyprocmail-:mode-name 'syntax-table))
1515     (prog1
1516         (cond
1517          ((char= (following-char) ?{)
1518           (forward-list 1)
1519           (if no-adjust
1520               (forward-line 1)
1521             (backward-char 1)))
1522          (t
1523           (forward-char 1)
1524           (backward-list 1)))
1525       (set-syntax-table otable))))
1526
1527 ;;; ----------------------------------------------------------------------
1528 ;;;
1529 (defun tinyprocmail-move-to-recipe-end ()
1530   "Go to recipe block end. Return recipe bound: (beg . end) ."
1531   (let* (beg)
1532     (if (not (tinyprocmail-recipe-start-p))
1533         (tinyprocmail-backward-strict))
1534     (setq beg (point))
1535
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))))
1542
1543 ;;}}}
1544 ;;{{{ move: interactive
1545
1546 ;;; ............................................................ &move ...
1547
1548 (tinyprocmail-fmacro-move nil  strict)
1549 (tinyprocmail-fmacro-move back strict)
1550
1551 ;;; ----------------------------------------------------------------------
1552 ;;;
1553 (defun tinyprocmail-fix-backslashes-paragraph ()
1554   "Fix backslashes at point"
1555   (interactive)
1556   (ti::buffer-backslash-fix-paragraph))
1557
1558 ;; ----------------------------------------------------------------------
1559 ;;;
1560 (defun tinyprocmail-backward (&optional method verb)
1561   "Find previous procmail recipe. See METHOD in `tinyprocmail-forward'. VERB."
1562   (interactive)
1563   (ti::verb)
1564   (tinyprocmail-forward 'back method verb))
1565
1566 ;;; ----------------------------------------------------------------------
1567 ;;;
1568 (defun tinyprocmail-forward (&optional back method verb)
1569   "Find next procmail recipe.
1570
1571 Input:
1572
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:
1577   VERB      Be verbose
1578
1579 Return:
1580
1581    nil or non-nil if moved."
1582   (interactive)
1583   (let* ((opoint (point))
1584          (re  (cond
1585                ((eq method 'strict)
1586                 "^:")
1587                ((eq method 'any)
1588                 "^[# \t]*\\(:\\)")
1589                (t
1590                 "^[ \t]*\\(:\\)"))))
1591     (ti::verb)
1592     (cond
1593      (back
1594       (beginning-of-line)
1595       (if (re-search-backward re nil t)
1596           (goto-char (match-beginning 1))
1597         (if verb
1598             (message "TinyProcmail: No more recipes backward."))
1599         (goto-char opoint)
1600         nil))
1601      (t
1602       (end-of-line)
1603       (if (re-search-forward re nil t)
1604           (goto-char (match-beginning 1))
1605         (if verb
1606             (message "TinyProcmail: No more recipes forward."))
1607         (goto-char opoint)
1608         nil)))))
1609
1610 ;;}}}
1611 ;;{{{ hide
1612
1613 ;;; ............................................................ &hide ...
1614
1615 ;;; ----------------------------------------------------------------------
1616 ;;;
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))))))
1628
1629 ;;; ----------------------------------------------------------------------
1630 ;;;
1631 (defun tinyprocmail-show-comment-text-recipe ()
1632   "See `tinyprocmail-hide-comment-text-recipe'."
1633   (interactive)
1634   (tinyprocmail-hide-comment-text-recipe 'show))
1635
1636 ;;; ----------------------------------------------------------------------
1637 ;;;
1638 (defun tinyprocmail-hide-comment-text-recipe (&optional show)
1639   "Hide or SHOW comment text in current recipe. point must be in recipe."
1640   (interactive "P")
1641   (let* ((region (save-excursion (tinyprocmail-move-to-recipe-end))))
1642     (tinyprocmail-hide-comment-text-region
1643      (car region) (cdr region) show)))
1644
1645 ;;; ----------------------------------------------------------------------
1646 ;;;
1647 (defun tinyprocmail-show-comment-text-region (beg end)
1648   "See `tinyprocmail-hide-comment-text-region'. Use Region BEG END."
1649   (interactive "r")
1650   (tinyprocmail-hide-comment-text-region beg end 'show))
1651
1652 ;;}}}
1653 ;;{{{ output mode:
1654
1655 ;;; .......................................................... &output ...
1656
1657 ;;; ----------------------------------------------------------------------
1658 ;;;
1659 (defun tinyprocmail-output-end ()
1660   "Go to endline of output block."
1661   (interactive)
1662   (if (looking-at "^[ \t]*$")
1663       (skip-chars-forward " \t\n"))
1664   (re-search-forward "^[ \t]*$"))
1665
1666 ;;; ----------------------------------------------------------------------
1667 ;;;
1668 (defun tinyprocmail-output-start ()
1669   "Go to start line of output block."
1670   (interactive)
1671   (unless (re-search-backward "^\\*\\*")
1672     (error "Invalid buffer format, No ** found.")))
1673
1674 ;;; ----------------------------------------------------------------------
1675 ;;;
1676 (defun tinyprocmail-output-line-start ()
1677   "Go to start of first code line."
1678   (tinyprocmail-output-start)
1679   (forward-line 1)
1680   (if (looking-at "cd \\([^ \t\n]+\\)")
1681       (forward-line 1))
1682   (point))
1683
1684 ;;; ----------------------------------------------------------------------
1685 ;;;
1686 (defun tinyprocmail-output-region ()
1687   "Return output region block '(beg . end)."
1688   (save-excursion
1689     (let* ((beg (tinyprocmail-output-line-start)))
1690       (tinyprocmail-output-end)
1691       (cons beg (point)))))
1692
1693 ;;; ----------------------------------------------------------------------
1694 ;;;
1695 (defun tinyprocmail-output-sort-by-error (&optional reverse)
1696   "Sort block by error. Optionally REVERSE."
1697   (interactive "P")
1698   (let* ((region (tinyprocmail-output-region)))
1699     (ti::save-line-column-macro nil nil
1700       (sort-regexp-fields
1701        (if reverse -1)
1702        "^[^:]+:[^:]+:\\([^,]+\\).*$" "\\1"
1703        (car region)
1704        (cdr region)))))
1705
1706 ;;; ----------------------------------------------------------------------
1707 ;;;
1708 (defun tinyprocmail-output-sort-by-line (&optional reverse)
1709   "Sort block by line number. Optionally REVERSE."
1710   (interactive "P")
1711   (let* ((region (tinyprocmail-output-region)))
1712     (ti::save-line-column-macro nil nil
1713       (sort-lines reverse (car region) (cdr region)))))
1714
1715 ;;; ----------------------------------------------------------------------
1716 ;;;
1717 (defun tinyprocmail-output-file-save (&optional file)
1718   "Write `tinyprocmail-:lint-output-buffer' to `tinyprocmail-:lint-file' using FILE."
1719   (interactive)
1720   (save-excursion
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)))))
1725
1726 ;;; ----------------------------------------------------------------------
1727 ;;;
1728 (defun tinyprocmail-output-file-kill ()
1729   "Kill `tinyprocmail-:lint-file' if it exists."
1730   (interactive)
1731   (if (file-exists-p tinyprocmail-:lint-output-file)
1732       (delete-file tinyprocmail-:lint-output-file)))
1733
1734 ;;; ----------------------------------------------------------------------
1735 ;;;
1736 (defun tinyprocmail-output-clear  (&optional verb)
1737   "Clear `tinyprocmail-:lint-output-buffer' buffer if it exists. VERB."
1738   (interactive)
1739   (ti::verb)
1740   (save-excursion
1741     (cond
1742      ((ti::set-buffer-safe tinyprocmail-:lint-output-buffer)
1743       (erase-buffer)
1744       (if verb
1745           (message "TinyProcmail: %s cleared"
1746                    tinyprocmail-:lint-output-buffer))))))
1747
1748 ;;}}}
1749
1750 ;;{{{ Lint: Flag functions
1751
1752 ;;; ............................................................ &flag ...
1753
1754 ;;; ----------------------------------------------------------------------
1755 ;;;
1756 (defun tinyprocmail-flag-read (string)
1757   "Read flags; including lock char, from STRING. If no flags read, return nil."
1758   ;;
1759   ;;  We have to count space, beacuse
1760   ;;
1761   ;;  :0 E fh c:   is valid recipe.
1762   ;;
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)))
1767
1768 ;;; ----------------------------------------------------------------------
1769 ;;;
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.
1774
1775 Return:
1776
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))
1785          (ret        "")
1786          (ret-len    0)
1787          (conflict1  0)
1788          (conflict2  0)
1789          case-fold-search
1790          ch
1791          pos)
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
1796         ;;
1797         ;;  aAeEf
1798         ;;    |
1799         ;;    pos = 2 if the ch was 'f'
1800         (setq pos (match-beginning 0))
1801         ;;  Increment the logical length: The character was valid
1802         (incf  ret-len)                 ;OK
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
1807           (aset hash pos t)
1808           (if (string-match ch "aAeE")
1809               (incf  conflict1))
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))))))
1816     (cond
1817      ((or (> conflict1 1)
1818           (> conflict2 1))
1819       ;; There can be only one: aAeE
1820       2)
1821      ((eq len (length ret))
1822       ;; All flags checked and they were valid
1823       ret)
1824      ((eq len ret-len)
1825       ;; All flags ok, but there were duplicates, which were
1826       ;; removed.
1827       (make-symbol ret))
1828      (t
1829       ;; Error: invalid flags
1830       1))))
1831
1832 ;;; ----------------------------------------------------------------------
1833 ;;;
1834 (defun tinyprocmail-flag-format-default (flags)
1835   "Default FLAGS format: ' STANDARDIZED-ORDER'. FLAGS must not have spaces."
1836   (let* (new)
1837     (setq new (tinyprocmail-flag-standardize flags))
1838     (if (or (stringp new)
1839             (and (symbolp new)
1840                  (setq new (symbol-name new))))
1841         (if (not (eq tinyprocmail-:flag-and-recipe-start-style
1842                      'flags-together))
1843             (concat " " new)
1844           new)
1845       (error "Invalid flags %s --> %s " flags new))))
1846
1847 ;;; ----------------------------------------------------------------------
1848 ;;;
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))
1852          beg
1853          end
1854          list)
1855     (when (and replace tinyprocmail-:flag-format-function)
1856       (setq replace (funcall tinyprocmail-:flag-format-function replace)))
1857     (beginning-of-line)
1858     (when (re-search-forward ":0" eol t)
1859       (setq beg (point))
1860       (setq list (inline (ti::re-search-point-list '(":" "#") nil eol))
1861             end  (if list (apply 'min list) eol))
1862       (delete-region beg end)
1863       (goto-char beg)
1864       (if replace
1865           (insert replace)))))
1866
1867 ;;; ----------------------------------------------------------------------
1868 ;;;
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))
1873          flags1
1874          flags2
1875          str)
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
1880       ;;  character is $
1881       ;;
1882       ;;  :0 $FLAGS
1883       ;;
1884       (if (string-match "^[$]" flags1)
1885           (progn
1886             (tinyprocmail-log
1887              (point)
1888              (format
1889               "info, flag variable `%s' was not checked." flags1))
1890             nil)                        ;RET VAL
1891         (setq flags2 (tinyprocmail-flag-standardize flags1))
1892         ;;  How would the standardization go?
1893         (cond
1894          ((symbolp flags2)
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)))
1900          ((eq 1 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)"))
1904           (setq flags2 nil))
1905          ((eq 2 flags2)
1906           (setq str "Error, flag conflict; some simultaneous 'aAeEwW'")
1907           (tinyprocmail-log (point) str)
1908           (tinyprocmail-fix-macro (concat str "(C-g to quit)"))
1909           (setq flags2 nil))
1910          ((and  (not (string= flags1 flags2)) pedantic)
1911           (setq
1912            str
1913            (format
1914             "Pedantic, flag order style is not standard `%s', was `%s'"
1915             flags2 flags1))
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))))
1921
1922 ;;; ----------------------------------------------------------------------
1923 ;;;
1924 (defun tinyprocmail-conition-comment-move-up ()
1925   "Move comment upward and kill it."
1926   (let* (str
1927          col)
1928     (save-excursion
1929       (beginning-of-line)
1930       (cond
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))))))
1941       (when str
1942         (if (null (re-search-backward "^[ \t#]*$" nil t))
1943             (ti::pmin)
1944           (end-of-line))
1945         ;;   COL is the indentation of the code.
1946         (insert (if col (make-string col ?\ ) "") str "\n")
1947         t))))
1948
1949 ;;; ----------------------------------------------------------------------
1950 ;;;
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))
1956          (opoint  (point))
1957          (tab     "    ")
1958          no-move)
1959     (or point (setq point opoint))
1960     (or point-min (setq point-min (point-min)))
1961     (cond
1962      ((save-excursion
1963         (beginning-of-line)
1964         (looking-at "^[ \t]*\\*.*#"))
1965       (tinyprocmail-log
1966        point
1967        "Error, comments not allowed in condition line."
1968        point-min)
1969       (tinyprocmail-fix-macro
1970        "Comments not allowed in condition, Move upwards "
1971        (setq no-move
1972              (tinyprocmail-conition-comment-move-up))))
1973      ((null supp)
1974       (tinyprocmail-log
1975        point
1976        "Error, embedded comment. Not supported by your procmail."
1977        point-min)
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)
1983       (cond
1984        ((looking-at "\\*")
1985         (tinyprocmail-log
1986          point
1987          "\
1988 info, embedded comment, please indent it by 4 spaces (readability)."
1989          point-min)
1990         (save-excursion
1991           (forward-line 1) (move-to-column col t)
1992           (tinyprocmail-fix-macro "[recommendation] Indent comment "
1993                                   (insert tab))))
1994        ((looking-at "\\([ \t]+\\)#")
1995         ;;
1996         ;;     # comment
1997         ;;   # comment   << here
1998         (tinyprocmail-log point "info, embedded comment does not line up."
1999                           point-min)
2000         (save-excursion
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
2005       (forward-line 1)))
2006     (if (null no-move)                  ;The comment line was killed.
2007         (end-of-line))))
2008
2009 ;;; --------------------------------------------------------- &comment ---
2010 ;;;
2011 (defun tinyprocmail-lint-condition-comments (&optional point-min)
2012   "Check condition area and comments.
2013 Buffer must be narrowed to condition lines.
2014
2015 :0              --> :0
2016 * condition         * condition
2017 # comment             # comment
2018 * condition         * condition
2019
2020 Input:
2021
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.
2026
2027 Return:
2028
2029  nil
2030  non-nil   there are embedded  comments"
2031   (ti::pmin)
2032   (while (re-search-forward "#" nil t)
2033     (backward-char 1)
2034     (tinyprocmail-condition-comment-embedded (point) point-min)))
2035
2036 ;;; ----------------------------------------------------------------------
2037 ;;;
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)
2043          (opoint    (point))
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))
2050          str
2051          str2
2052          point
2053          lock-file
2054          file
2055          cond-end
2056          fix-line fix list
2057          dev-null-p
2058          formail-p
2059          formail-d-p
2060          paren-p
2061          sendmail-p
2062          condition-p
2063          brace-p
2064          pipe-p
2065 ;;;      var-or-literal-p
2066          mbox-many-p
2067          mbox-p
2068          assignment-p
2069          forward-p
2070          redirection-p
2071          invalid-action-p
2072          fwd-invalid-p
2073          size-test-p)
2074     (catch 'end
2075       (tinyprocmail-move-to-condition-end)
2076       (setq cond-end  (point)
2077             flags     (or flags "")
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
2084             ;; :0
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
2090             ;;
2091             ;;  ! forward
2092             ;;  | pipe
2093             ;;  $MBOX
2094             ;;  MBOX
2095             ;;
2096             ;; This Moves point
2097             invalid-action-p  (if (tinyprocmail-action-line-ok-p) cond-end)
2098             ;;  VAR=| formail -zX'Subject:'
2099             assignment-p      (if (looking-at ".*=|")
2100                                   (point)))
2101 ;;;    (ti::d! mbox-p dev-null-p (ti::read-current-line))
2102       ;; ....................................................... mailbox ...
2103       (when (eobp)
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
2113         (save-excursion
2114           (goto-char invalid-action-p)
2115           (skip-chars-forward " \t\n")
2116           (cond
2117            ((looking-at "{")
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 ")
2121                                     (beginning-of-line)
2122                                     (delete-region
2123                                      (point)
2124                                      (progn
2125                                        (skip-chars-backward " \t\n")
2126                                        (forward-line 1)
2127                                        (point)))))
2128            (t
2129             (setq str "Error, Invalid action line; no |!$A-Z or brace found.")
2130             (tinyprocmail-log invalid-action-p str)
2131             (save-excursion
2132               (goto-char invalid-action-p)
2133               (tinyprocmail-fix-macro (concat "[cannot-fix] " str)))))))
2134       ;; ....................................................... forward ...
2135       (when fwd-invalid-p
2136         (setq str "Error, invalid forward line. Maybe extra colons.")
2137         (tinyprocmail-log fwd-invalid-p str)
2138         (tinyprocmail-fix-macro (concat str " Remove ")
2139                                 (beginning-of-line)
2140                                 (while (re-search-forward "," (line-end-position) t)
2141                                   (ti::replace-match 0))))
2142       (when forward-p
2143         (save-excursion
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\\> *\\)")
2150             (setq str "\
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
2156       ;;
2157       ;;      :0
2158       ;;      * condition         * condition
2159       ;;      # comment           | pipe actions. \\
2160       ;;      *                     more actions \\
2161       ;;      {                     end
2162       ;;
2163       (goto-char cond-end)
2164 ;;;;   (ti::d! (point) brace-p  (ti::read-current-line))
2165       (cond
2166        ((tinyprocmail-recipe-start-p)
2167         (backward-line 1))
2168        ((null brace-p)
2169         ;;   | recipe \\
2170         ;;     continues
2171         (tinyprocmail-skip-continuation-forward)
2172         (forward-line 1)))
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" \
2180         ;;     .. rest
2181         ;;    ) | $SENDMAIL
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:
2192                 ;;
2193                 ;;  | foo \
2194                 ;;    this > mbox
2195                 ;;
2196                 ;;  Accept "out >>FILE"  and "OUT >>$FILE", but single
2197                 ;;  token must have leading space
2198                 ;;
2199                 ;;  If the > token is directly in the cond-end line
2200                 ;;
2201                 ;;  | cat > junk
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)))
2207           nil)
2208         ;; ................................................. empty-lines ...
2209         (ti::pmin)
2210         (while (and (not (eobp))
2211                     (re-search-forward "^[ \t]*$" nil t))
2212           (setq point nil)
2213           (tinyprocmail-log
2214            (point) "Error, no empty lines allowed inside condition block"
2215            point-min)
2216           (tinyprocmail-fix-macro
2217            "Error, empty line not allowed inside condition. Remove "
2218            (setq point t)
2219            (ti::buffer-kill-line))
2220           (if (null point)
2221               (forward-line 1)))
2222         ;; .................................................... comments ...
2223         (ti::pmin)
2224         (when (and pedantic
2225                    (setq point
2226                          (tinyprocmail-lint-condition-comments point-min)))
2227           (tinyprocmail-log
2228            point "Pedantic, Condition lines have embedded comments."
2229            point-min)))
2230       ;; #################################################### Narrow-end ###
2231       (goto-char opoint)
2232       ;; ............................................. no-condition-line ...
2233       (unless condition-p
2234         (setq fix nil  str ""  str2 flags)
2235         (dolist (ch '("H" "B" "D"))
2236           (when (ti::string-match-case ch str2)
2237             (setq fix t
2238                   str  (concat str ch)
2239                   str2 (ti::replace-match 0 nil str2))))
2240         (when fix
2241           (setq str
2242                 (format "Warning, no condition line, but flags `%s' found."
2243                         str))
2244           (tinyprocmail-log opoint str)
2245           (tinyprocmail-fix-macro (concat str " Remove ")
2246                                   (setq fix-line t
2247                                         flags    str2))))
2248       ;; ......................................... nested {} block start ...
2249       ;; Most of the flags don't make sense in the outer block level
2250       ;;
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.
2255       (when brace-p
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.
2261         (setq
2262          list
2263          (cond
2264           ((string-match "c" flags)
2265            '("f" "h" "b" "i" "r"))
2266           (t
2267            '("f" "h" "b" "i" "r" "w" "W"))))
2268         (setq str2 flags)
2269         (dolist (ch list)
2270           (when (ti::string-match-case ch str2)
2271             (setq fix t
2272                   str  (concat str ch)
2273                   str2 (ti::replace-match 0 nil str2))))
2274         (when fix
2275           (setq str
2276                 (format "Warning, start of {} block has unnecessary flags `%s'"
2277                         str))
2278           (tinyprocmail-log opoint str)
2279           (tinyprocmail-fix-macro (concat str " Remove ")
2280                                   (setq fix-line t
2281                                         flags    str2)))
2282         (when (and lock-p
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[<>] ...
2288       (when size-test-p
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 ")
2294                                     (setq fix-line  t
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 ")
2300                                     (setq fix-line t
2301                                           flags (replace-regexp-in-string "B" "" flags))))))
2302       ;; .................................................... assignment ...
2303       ;; VAR=| cat something
2304       (when assignment-p
2305         (when (and (string-match "c" flags)
2306                    (null dc))
2307           (setq str "Warning, flag `c' is useless in assignment =|")
2308           (tinyprocmail-log opoint str)
2309           (tinyprocmail-fix-macro (concat str " Remove ")
2310                                   (setq fix-line t
2311                                         flags (replace-regexp-in-string "c" "" flags))))
2312         (when (and (string-match "i" flags)
2313                    (null di))
2314           (setq str "Warning, flag `i' is not recommended in assignment =|")
2315           (tinyprocmail-log opoint str)
2316           (tinyprocmail-fix-macro (concat str " Remove ")
2317                                   (setq fix-line t
2318                                         flags (replace-regexp-in-string "i" "" flags))))
2319         (when lock-p
2320           (setq str "Warning, lockfile \":\" is useless in assignment =|")
2321           (tinyprocmail-log opoint str)
2322           (tinyprocmail-fix-macro (concat str " Remove ")
2323                                   (setq fix-line t
2324                                         flags (replace-regexp-in-string ":" "" flags))))
2325         (save-excursion
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
2333                  formail-d-p
2334                  (null assignment-p)
2335                  (null brace-p))
2336         (when (string-match "f" flags)
2337           (tinyprocmail-log
2338            opoint "Error, formail -D used. Flag `f' is a mistake.")
2339           (tinyprocmail-fix-macro "Formail -D used, remove `f' flag? "
2340                                   (setq fix-line t
2341                                         flags (replace-regexp-in-string "f" "" flags))))
2342         (when (not (string-match "W" flags))
2343           (tinyprocmail-log
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)
2352           (tinyprocmail-log
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))))
2356         (when  (not lock-p)
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 ...
2362       (setq fix nil)
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))))
2366         (tinyprocmail-log
2367          (point)
2368          "Warning, `f', but no h;b;hb found. What's up here? (readability) ")
2369         (when (eq tinyprocmail-:lint-fix-mode 'semi)
2370           (if (tinyprocmail-o
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)))
2374           (setq fix t)))
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 ")
2381                                 (setq fix t)
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
2386       (when (and pipe-p
2387                  (null brace-p)
2388                  (null assignment-p)
2389                  (null (string-match "w" flags))
2390                  (null dw)
2391                  (or (null pipe-w-re)
2392                      (save-excursion
2393                        (goto-char cond-end)
2394                        (not (looking-at pipe-w-re)))))
2395         (setq str "\
2396 Warning, recipe with \"|\" may need `w' flag. (recommended) ")
2397         (tinyprocmail-log opoint str)
2398         (tinyprocmail-fix-macro (concat str " Add ")
2399                                 (setq fix-line t
2400                                       flags    (concat "w" flags))))
2401       ;; ................................................. check formail ...
2402       (when (and formail-p
2403                  (null formail-d-p)
2404                  (null paren-p) ;; If this, then "f" is not needed
2405                  (null assignment-p)
2406                  (null brace-p)
2407                  ;; Don't require "f" flag in this case
2408                  ;;
2409                  ;;  | $FORMAIL -A "header" >> mbox
2410                  ;;
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)
2420                  pipe-p
2421                  (null formail-p)
2422                  (null sendmail-p)
2423                  (not (ti::string-match-case "f" flags))
2424                  (not (ti::string-match-case "i" flags))
2425                  (null di))
2426         (setq str "Warning, recipe with \"|\", but no \">\" may need `i' flag.")
2427         (tinyprocmail-log opoint str)
2428         (tinyprocmail-fix-macro (concat str " Add ")
2429                                 (setq fix-line t
2430                                       flags    (concat "i" flags))))
2431       ;;  i is meaningless if nested condition follows
2432       ;;  :0 i
2433       ;;  { }
2434       (when (and brace-p
2435                  (ti::string-match-case "i" flags)
2436                  (null di))
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 ")
2440                                 (setq fix-line t
2441                                       flags (replace-regexp-in-string "i" "" flags))))
2442       (when (and (null redirection-p)
2443                  pipe-p
2444                  (not (ti::string-match-case "f" flags))
2445                  (not (ti::string-match-case "c" flags))
2446                  (ti::string-match-case "i" flags)
2447                  (null dc))
2448         (setq str
2449               "\
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))))
2454       (goto-char opoint)
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 ")
2462                                   (setq fix-line t
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
2466       ;;  block
2467       (when (and flags
2468                  brace-p lock-p
2469                  (ti::string-match-case "W" flags)
2470                  (ti::string-match-case "c" flags))
2471         (setq str
2472               "info, redundant `Wc:', `c:' already implies W in {} block.")
2473         (tinyprocmail-log opoint str)
2474         (tinyprocmail-fix-macro (concat str " Correct")
2475                                 (setq fix-line t
2476                                       flags (replace-regexp-in-string "W" "" flags))))
2477       ;; ................................................. need lockfile ...
2478       (when (and redirection-p
2479                  (not brace-p)
2480                  (not formail-p)
2481                  (not forward-p)
2482                  (not lock-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 ")
2486                                 (setq fix-line  t
2487                                       flags    (concat flags ":"))))
2488       ;;  Missing lockfile, but not if /dev/null
2489       ;;  :0      :0
2490       ;;  mbox    /dev/null
2491       (when (and mbox-p
2492                  (not dev-null-p)
2493                  (not redirection-p)
2494                  (not brace-p)
2495                  (not formail-p)
2496                  (not forward-p)
2497                  (not lock-p))
2498         (setq str "Warning, message dropped to folder, it may need a lock.")
2499         (tinyprocmail-log mbox-p str)
2500         (save-excursion
2501           (goto-char mbox-p)
2502           (tinyprocmail-fix-macro (concat str " Add ")
2503                                   (setq fix-line t
2504                                         flags    (concat flags ":")))))
2505       ;; .......................................................... MBOX ...
2506       (when mbox-p
2507         ;; ... ... ... ... ... ... ... ... ... ... ... ... ...  dev/null . .
2508         (when dev-null-p
2509           (when lock-p
2510             (setq str "Warning, /dev/null doesn't need lock")
2511             (tinyprocmail-log mbox-p str)
2512             (save-excursion
2513               (goto-char mbox-p)
2514               (tinyprocmail-fix-macro (concat str " Remove ")
2515                                       (setq fix-line t
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)
2520             (save-excursion
2521               (goto-char mbox-p)
2522               (tinyprocmail-fix-macro (concat str " Add ")
2523                                       (setq fix-line t
2524                                             flags    (concat flags "h"))))))
2525         ;; ... ... ... ... ... ... ... ... ... ... ... ... ... mbox name . .
2526         ;;  Check MBOX name
2527         (save-excursion
2528           (goto-char mbox-p)
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 ")
2538                                   (setq fix-line t
2539                                         flags (replace-regexp-in-string "i" "" flags))))
2540         (when (and flags (ti::string-match-case "hb\\|bh" flags))
2541           (setq str "\
2542 Warning, flag combo `hb' is useless when dropping to folder.")
2543           (tinyprocmail-log opoint str)
2544           (tinyprocmail-fix-macro (concat str " Remove ")
2545                                   (setq fix-line t
2546                                         flags (replace-regexp-in-string "hb\\|bh" "" flags)))))
2547       ;; .................................................... check lock ...
2548       (goto-char opoint)
2549       (beginning-of-line)
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
2553                                     (match-string 2))))
2554         ;; change :0 HB: c  to :0 HBc:
2555         (tinyprocmail-log
2556          (point)
2557          (format "Possibly Flag used as lock `%s'" (ti::read-current-line)))
2558         (cond
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)
2564                  fix-line t)))
2565          (t
2566           (tinyprocmail-fix-macro
2567            (format "[cannot-fix] Odd one char `%s' lockfile, C-g to quit."
2568                    str)))))
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
2576
2577                 (and
2578                  ;; If lock file has variable expansion, then we won't
2579                  ;; check it.
2580                  (not (string-match "[$]" lock-file))
2581                  (not (tinyprocmail-string-valid-p lock-file 'path))))
2582           (tinyprocmail-log
2583            (point)
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
2588         (cond
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)))
2592           (tinyprocmail-log
2593            (point)
2594            (format
2595             "info, could't check extention .lock in lockfile `%s'"
2596             lock-file)))
2597          ((save-match-data
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")))
2605          ((save-match-data
2606             (and
2607              (when (string-match "\\(.*\\)\\." lock-file)
2608                (setq file (match-string 1 lock-file)))
2609              (not (string-match "\\.lock\\|\\.lck\\|[$]LOCKEXT" lock-file))))
2610           (tinyprocmail-log
2611            (point)
2612            (format
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
2618       (goto-char opoint)
2619       (when fix-line (tinyprocmail-flag-kill flags)))))
2620
2621 ;;}}}
2622 ;;{{{ lint: other
2623
2624 ;;; ........................................................... &other ...
2625
2626 ;;; ----------------------------------------------------------------------
2627 ;;;
2628 (defun tinyprocmail-lint-malformed-brace ()
2629   "Check braces."
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?"
2635                               (insert " ")
2636                               (backward-char 1)))
2637     (when (and (looking-at ".*\\(}\\)")
2638                (save-match-data
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 " }")))))
2643
2644 ;;; ----------------------------------------------------------------------
2645 ;;;
2646 (defun tinyprocmail-lint-malformed-misc  ()
2647   "Check varaious other things."
2648   (let* (;;; (pedantic (eq tinyprocmail-:lint-log-verbose 'pedantic))
2649          (opoint  (point))
2650          (re "echo\\|cat\\|tail\\|head\\|sed\\|perl\\|awk\\|perl\\|[-]")
2651          str)
2652     ;;  Detect "dummy `echo`", missing "=" or ";"
2653     ;;
2654     ;;  But following is valid.
2655     ;;
2656     ;;          LOG = "text start
2657     ;;          and-newline "
2658     (while (re-search-forward
2659             "^[ \t]*\\([^!|#\n=;]+\\)\\([ \t]+\\)[\"`]" nil t)
2660       (setq str (match-string 0))
2661       (when (save-match-data
2662               (and
2663                (not (string-match re (match-string 1)))
2664                (not (string-match "\"[ \t]*$\\|\"[ \t]*#"  str))
2665                (not (tinyprocmail-condition-line-p))))
2666         (setq
2667          str
2668          (format "Warning, After `%s', there is no \"=\" or \";\""
2669                  (match-string 1)))
2670         (tinyprocmail-log (point) str)
2671         (tinyprocmail-fix-macro (concat str " Add = ")
2672                                 (ti::replace-match 2 " = "))))
2673     (goto-char opoint)))
2674
2675 ;;; ----------------------------------------------------------------------
2676 ;;;
2677 (defun tinyprocmail-lint-malformed-var-defs ()
2678   "Check variable definitions and assignments."
2679   (let* (
2680 ;;;      (pedantic (eq tinyprocmail-:lint-log-verbose 'pedantic))
2681 ;;;      cont-p
2682          var1
2683          var2
2684          str
2685          op)
2686     (while (re-search-forward "^[ \t]*[^#\n].*=" nil t)
2687       (beginning-of-line)
2688       (unless (tinyprocmail-comment-line-pp)
2689         ;;  Backslash at the end (continuation)
2690         ;;
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
2695         ;;
2696         ;; MONTHNAME = `date +%y-%m`
2697         (when (looking-at ".*=`.*date")
2698           (setq str
2699                 (concat
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 . .
2711         ;; VAR-1 = 1
2712         (when (and (looking-at "^[ \t]*\\([^ \t\n]+\\)[ \t]*=")
2713                    (setq str (match-string 1))
2714
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
2721           ;;
2722           ;;     SPAM_REGEXP_FILE   ="\
2723           ;;     filename.*=.*\.(pif|rar|zoo|arj|exe|bat)"
2724           (setq str
2725                 (format
2726                  "Warning, odd characters in variable `%s'." str))
2727           (tinyprocmail-log (point) str)
2728           (tinyprocmail-fix-macro str))
2729         ;; ... ... ... ... ... ... ... ... ... ... ... ... ... . literal . .
2730         ;;   VAR = abc
2731         ;;   VER = 1.4a
2732         (when (looking-at "^[^=\n]+=[ \t]*\\([a-z]\\|[0-9]+[-.a-z]\\)")
2733           (tinyprocmail-log
2734            (point)
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
2740         ;; var = var-var                ok
2741         ;; var = $var-$var      ok
2742         (when (looking-at
2743                "^[ \t]*[a-z].*=[ \t]*[$]\\([a-z]+[^-/_a-z0-9$ \t\n#]+\\)")
2744           (setq str
2745                 (format
2746                  "Error, Odd variable name to the right `%s'."
2747                  (match-string 1)))
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
2758                   ;; no comment
2759                   (looking-at "^.*=[ \t]*`.*\\('\\)[ \t]*$"))
2760           (setq
2761            str
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 ...
2767         ;; D = ${D: ...
2768         ;;
2769         ;; Note that the colon in this case is ok.
2770         ;; D = {D}:/directory/file.txt
2771         (when (looking-at
2772                (concat
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)))
2789              (insert var1)
2790              (setq var2 var1)))
2791           ;;  Writing VAR = ${$VAR is a mistake. Notice 2nd $
2792           (when (save-match-data (and var2 (string-match "^[$]" var2)))
2793             (tinyprocmail-log
2794              (point)
2795              (format
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
2800                                var2
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)))))
2806             (setq
2807              str
2808              (format
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*
2815           (when nil
2816             (when (and (save-match-data (looking-at ".*[-+][ \t]*`"))
2817                        pedantic)
2818               (tinyprocmail-log
2819                (point)
2820                (concat
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)))
2829             (tinyprocmail-log
2830              (point)
2831              (format "Error, invalid init operator `%s'. Not [-+] " op))
2832             (tinyprocmail-fix-macro
2833              (format "[cannot-fix] Invalid init operator `%s' "
2834                      op  )))))
2835       (forward-line 1))))
2836
2837 ;;; ----------------------------------------------------------------------
2838 ;;;
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)
2843                      "" " "))
2844          list
2845          str)
2846     (while (setq list (ti::re-search-point-list '("^[ \t]*0:" "^[ \t]*:[^0]")
2847                                                 'beginning-of-line))
2848       (goto-char (apply 'min list))
2849       (cond
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")))
2855        (t
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
2860         (cond
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")))
2866          (t                             ; ':'
2867           (setq str "Warning, Suspicious recipe start, use standard `:0'.")
2868           (tinyprocmail-log (point) str)
2869           (tinyprocmail-fix-macro str
2870                                   (delete-horizontal-space)
2871                                   (insert "0" space)
2872                                   (if (not (looking-at " "))
2873                                       (insert " ")))))))
2874       (end-of-line))))
2875
2876 ;;; ----------------------------------------------------------------------
2877 ;;;
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]+[?]"))
2883          tmp
2884          str
2885          match-p
2886          end)
2887     (catch 'done
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(^) ...
2895       ;; * ! Headr-field:
2896       ;; * ! FROM_DAEMON
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
2900                   (looking-at
2901                    (concat
2902                     "\\*[ \t!]*\\(FROM_DAEMON\\|FROM_MAILER\\|TO_?\\)"
2903                     "[ \t]*$"))))
2904         (setq
2905          str
2906          (format
2907           "Warning, `%s' does not have (^) in condition line."
2908           (match-string 1)))
2909         (tinyprocmail-log (point) str)
2910         (tinyprocmail-fix-macro (concat  str " Add ")
2911                                 (goto-char (match-beginning 1))
2912                                 (insert "^")))
2913       ;; .......................................................... TO ...
2914       ;; * ^TOregexp  is a mistake, should use ^TOregexp\> or something
2915       ;; Skip ^TO$REGEXP
2916       (when (let (case-fold-search)
2917               (and (looking-at ".*\\(TO[^([\n]+\\)")
2918                    (save-match-data
2919                      (and (not (looking-at ".*[\\]>[ \t]*$"))
2920                           (not (looking-at ".*[$]"))
2921
2922                           ;;  Next line must not a condition
2923                           ;;  line
2924                           ;;
2925                           ;;  * ^TOadmin
2926                           ;;  * more-restrictive-condition
2927                           ;;
2928                           (not (save-excursion
2929                                  (forward-line 1)
2930                                  (looking-at "[ \t]*\\*")))))
2931                    ;;  Try to find similar contruct from buffer.
2932                    ;;  If found then user needs swap the order of
2933                    ;;  these two.
2934                    ;;
2935                    ;;  1) TOaddr  and later) TOadd-another
2936                    ;;
2937                    (save-excursion
2938                      (end-of-line)
2939                      (re-search-forward
2940                       (regexp-quote (match-string 1)) nil t))))
2941         (setq
2942          str
2943          (format
2944           "Warning, `%s' is not unique, another similar TO found."
2945           (match-string 1)))
2946         (tinyprocmail-log (point) str)
2947         (tinyprocmail-fix-macro (concat "[cannot-fix]" str)))
2948       ;; ....................................................... extra ...
2949       ;;   * regexp|regexp\
2950       ;;   * |regexp|regexp  << Ooops, extra (*)
2951       (when (and (looking-at "\\*[ \t]*|")
2952                  (save-excursion        ;Peek previous
2953                    (forward-line -1)
2954                    (looking-at "[\\][ \t]*$")))
2955         (setq str "Warning, extra \"*\" before fist regexp \"|\".")
2956         (tinyprocmail-log (point) str)
2957         (tinyprocmail-fix-macro (concat str " Remove `*' ")
2958                                 (delete-char 1)
2959                                 (throw 'done 'extra))) ;Nothing more to check
2960       ;; ........................................... expansion missing ...
2961       ;;  Very common mistake, you forgot the beginning $
2962       ;;
2963       ;;  * ! $REGEXP
2964       ;;
2965       ;;  * $$REGEXP  is ok
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]*$")
2971                  (not var-test-p))
2972         (setq
2973          str
2974          (format "Error, No eval($) in condition found. (%s)"
2975                  (match-string 1)))
2976         (tinyprocmail-log (point) str)
2977         (tinyprocmail-fix-macro (concat str " Add `$' ")
2978                                 (save-excursion
2979                                   (forward-char 1)
2980                                   (insert "$" ))))
2981       ;;  A bit more complex
2982       ;;  * !^(To|Cc|Bcc):.*$LOGNAME
2983       ;;
2984       ;;  But this is ok
2985       ;;
2986       ;;  * ? echo "$ARG"
2987       ;;
2988       ;;  And $HOME variable is accepted without eval
2989       (when (and (not var-test-p)
2990                  (not shell-test-p)
2991                  (looking-at
2992                   "\\*[^$\n]+\\(.\\)[$]\\([A-Z][^ \t\n]+\\)+[^$\n]$")
2993                  (save-match-data
2994                    (not (string-match "[\"]" (match-string 1))))
2995                  (save-match-data
2996                    (not (string-match "[$]HOME" (match-string 2)))))
2997         (setq
2998          str
2999          (format "Error, No eval($) in complex condition found. (%s)"
3000                  (match-string 2)))
3001         (tinyprocmail-log point str)
3002         (tinyprocmail-fix-macro (concat str " Add `$' ")
3003                                 (save-excursion
3004                                   (forward-char 1)
3005                                   (insert "$" ))))
3006       ;; ............................................. missing eval($) ...
3007       ;;  User forgot the interpolation in the line
3008       ;;
3009       ;;  * ! VAR ?? $eval-this-var
3010       ;;
3011       ;;  --> * ! VAR ?? $ $eval-this-var
3012       ;;
3013       ;;  It's impossible to catch this however
3014       ;;
3015       ;;  * ! VAR ?? $eval-this $eval-second
3016       ;;
3017       ;;
3018       ;;  But these are ok
3019       ;;
3020       ;;  * FROM??^foo@bar$
3021       ;;  * TO??^$
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))
3034                                 (insert " $ ")
3035                                 (goto-char point)))
3036       ;; Warn about:      *$  $VAR ?? test
3037       ;; Not Warn about:  *$  $VAR ?? $test
3038       ;;
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
3046       ;;  *$ VAR ?? test
3047       ;;
3048 ;;;       (when (and (looking-at "[^$\n]+[?][?]")           ;; * VAR ?? test
3049 ;;;               (not (looking-at ".*[HB][! \t]+[?]"))  ;; * HB  ?? regexp
3050 ;;;               )
3051 ;;;      (tinyprocmail-log
3052 ;;;       (point)
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)
3057 ;;;        (insert "$")
3058 ;;;        ))
3059       ;; .................................................... empty ?? ...
3060       (when (looking-at ".*[?][?][ \t\n]*$")
3061         (tinyprocmail-log (point) "Error, Nothing follows ?? ")
3062         ;; Can't autofix this one.
3063         ;;
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]*$")
3069         (tinyprocmail-log
3070          (point)
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.
3077       (save-excursion
3078         ;;   Skip continuation
3079         (while (and (not (eobp))
3080                     (looking-at  ".*\\[ \t]*"))
3081           (forward-line 1))
3082         (save-excursion
3083           (setq end (line-end-position))
3084           (goto-char point)
3085           (if (re-search-forward "\/" end t)
3086               (setq match-p t)))
3087         (when (and (null match-p) (looking-at "^[^ \t\]+\\*[ \t]*$"))
3088           (tinyprocmail-log
3089            (point)
3090            "info, maybe useless regexp `*' at the end condition.")
3091           (tinyprocmail-fix-macro "[cannot-fix] Useless `*' at the end."))))))
3092
3093 ;;; ----------------------------------------------------------------------
3094 ;;;
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)))
3099
3100 ;;; ----------------------------------------------------------------------
3101 ;;;
3102 (defun tinyprocmail-standardize-recipe-start ()
3103   "Check whole buffer and change recipe start to ':0 FLAGS'.
3104 Refrences:
3105  `tinyprocmail-:flag-and-recipe-start-style'"
3106   (interactive)
3107   (let* ((style tinyprocmail-:flag-and-recipe-start-style)
3108          (i     0)
3109          (found 0))
3110     (save-excursion
3111       (ti::pmin)
3112       (while (tinyprocmail-forward)
3113         (incf  found)
3114         ;;   We're sitting on :  go to 2 char forward
3115         (forward-char 2)
3116         (cond
3117          ((eq style 'flags-together)
3118           (when (looking-at "\\([ \t]\\)[^#]")
3119             (ti::replace-match 1)
3120             (incf  i)))
3121          (t
3122           (unless (looking-at "[ \t\n]")
3123             (insert " ")
3124             (incf  i))))
3125         (end-of-line)))
3126
3127     (unless (zerop i)
3128       (message
3129        "TinyProcmail: standardized %d recipe start line(s). Style: %s"
3130        i
3131        (if style
3132            (symbol-name style)
3133          "Flags separated from :0")))
3134
3135     (when (zerop found)
3136       (message "TinyProcmail: Can't find any recipes from buffer."))))
3137
3138 ;;; ----------------------------------------------------------------------
3139 ;;;
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."
3144   (let* (options
3145          flags
3146          point)
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))
3150         (forward-line 1)
3151         (setq flags (tinyprocmail-flag-read (ti::read-current-line)))
3152         (tinyprocmail-log
3153          point
3154          (format "info, Lint options `%s'. recipe flags `%s'."
3155                  options flags))))))
3156
3157 ;;; ----------------------------------------------------------------------
3158 ;;;
3159 (defun tinyprocmail-lint-find-wrong-escape-codes ()
3160   "Find misused \\t and \\n characters."
3161   (let* (str
3162          val)
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))
3167         (setq
3168          str
3169          (format "Error, escape code `%s' is not known to procmail." val))
3170         (tinyprocmail-log (point) str)
3171         (cond
3172          ((save-match-data (string-match "t" val))
3173           (tinyprocmail-fix-macro (concat str " Correct ")
3174                                   (ti::replace-match 1 "\t")))
3175          (t
3176           (tinyprocmail-fix-macro (concat "[cannot-fix] " str))))))))
3177
3178 ;;; ----------------------------------------------------------------------
3179 ;;;
3180 (defun tinyprocmail-lint-find-2spaces ()
3181   "Find misused [  ] contructs. User meant space and TAB."
3182   (let* (str
3183          set
3184          done
3185          not-comment
3186          not-backtics)
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
3192                            (beginning-of-line)
3193                            (save-match-data (not (looking-at ".*`")))))
3194       ;;  Skip lines like:  dummy = `echo "[this value here]" > file`
3195       (when (and not-comment not-backtics)
3196         (save-match-data
3197           (when (string-match " \\( +\\)" set)
3198             (setq
3199              str
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 "
3203                                     (setq done t)
3204                                     (setq set (ti::replace-match 1 "\t" set)))))
3205         (when done
3206           (ti::replace-match 1 set))
3207         (when (and (or (tinyprocmail-condition-line-p)
3208                        (tinyprocmail-assignment-line-p))
3209                    ;; like " asd "
3210                    (string-match " [^ ]+ " set))
3211           (setq str "Warning, two spaces inside regexp [].")
3212           (tinyprocmail-fix-macro (concat "[cannot-fix] " str)))))))
3213
3214 ;;}}}
3215 ;;{{{ Lint: main
3216
3217 ;;; ----------------------------------------------------------------------
3218 ;;;
3219 (defun tinyprocmail-output-display ()
3220   "Show `tinyprocmail-:lint-output-buffer' buffer."
3221   (interactive)
3222   (cond
3223    ((null (get-buffer tinyprocmail-:lint-output-buffer))
3224     (error "No `tinyprocmail-:lint-output-buffer'"))
3225    (t
3226     (ti::save-excursion-macro
3227       (display-buffer tinyprocmail-:lint-output-buffer)
3228       (select-window (get-buffer-window tinyprocmail-:lint-output-buffer))
3229       (ti::pmax)
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)))))
3235
3236 ;;; ----------------------------------------------------------------------
3237 ;;;
3238 (defun tinyprocmail-lint-forward (&optional mode verb)
3239   "Lint the code forward.
3240 Input:
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."
3245   (interactive "P")
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)
3250          (opoint                (point))
3251          (time                  (current-time))
3252          (count                 0)
3253          point
3254          secs
3255          ret)
3256     (ti::verb)
3257     (when mode
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)
3264     (beginning-of-line)
3265     (run-hooks 'tinyprocmail-:lint-before-hook)
3266     (goto-char opoint)
3267     (while (tinyprocmail-forward)
3268       (setq point (point))
3269       (incf  count)
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))
3276       (when ret
3277         (dolist (func check-list)
3278           (goto-char point)
3279           (funcall func (car ret) (cdr ret) )))
3280       (goto-char point)
3281       (forward-line 1))
3282     (dolist (func tinyprocmail-:lint-after-hook)
3283       (goto-char opoint)
3284       (funcall func))
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)
3291           time (/ secs 60))
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)))
3296
3297 ;;; ----------------------------------------------------------------------
3298 ;;;
3299 (defun tinyprocmail-lint-buffer (&optional mode verb)
3300   "Lint whole buffer. See MODE and VERB from `tinyprocmail-lint-forward'."
3301   (interactive "P")
3302   (ti::verb)
3303   (save-excursion
3304     (ti::pmin)
3305     (tinyprocmail-lint-forward mode verb)))
3306
3307 ;;; ----------------------------------------------------------------------
3308 ;;;
3309 (defun tinyprocmail-lint-buffer-batch (&optional clear file)
3310   "Batch Lint buffer and write results to `tinyprocmail-:lint-output-file'.
3311 Input:
3312
3313   CLEAR  If non-nil, clear output buffer
3314   FILE   Save results to this file"
3315   (interactive "P")
3316   (if clear
3317       (tinyprocmail-output-clear))
3318   ;;  Delete empty lines from the beginning to get line numbers
3319   ;;  sensible
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))
3325
3326 ;;}}}
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.
3331
3332 ;; file pkg.tar:
3333 ;; r--r--r-- 240/222  14764 Jun 22 15:05 1998 pm-lint.rc 1.26
3334 ;;
3335 ;; -----BEGIN PGP MESSAGE-----
3336 ;; Version: 2.6.3ia
3337 ;; Comment: Base64 signed. File: pkg.tar uncompresses to approx. 20K
3338 ;;
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
3446 ;; =D/Dv
3447 ;; -----END PGP MESSAGE-----
3448
3449 ;;}}}
3450
3451 (add-hook 'tinyprocmail-:mode-define-keys-hook
3452           'tinyprocmail-mode-define-keys)
3453
3454 (add-hook 'tinyprocmail-output-:mode-define-keys-hook
3455           'tinyprocmail-output-mode-define-keys)
3456
3457 (provide 'tinyprocmail)
3458 (run-hooks 'tinyprocmail-:load-hook)
3459
3460 ;;; tinyprocmail.el ends here