home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume34 / mailagent / patch15 < prev    next >
Text File  |  1993-01-17  |  46KB  |  1,280 lines

  1. Newsgroups: comp.sources.misc
  2. From: ram@eiffel.com (Raphael Manfredi)
  3. Subject: v34i115:  mailagent - Rule Based Mail Filtering, Patch15
  4. Message-ID: <1993Jan17.205549.1935@sparky.imd.sterling.com>
  5. X-Md4-Signature: f7bc2d707c6e5bec66eabacf6f95f6e7
  6. Date: Sun, 17 Jan 1993 20:55:49 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: ram@eiffel.com (Raphael Manfredi)
  10. Posting-number: Volume 34, Issue 115
  11. Archive-name: mailagent/patch15
  12. Environment: Perl, Sendmail, UNIX
  13. Patch-To: mailagent: Volume 33, Issue 93-109
  14.  
  15. [The latest patch for mailagent version 2.9 is #16.]
  16.  
  17. System: mailagent version 2.9
  18. Patch #: 15
  19. Priority: MEDIUM
  20. Subject: military timezones did not parse correctly
  21. Subject: (fix by Paul Marquess <pmarquess@rosebud.bfsec.bt.co.uk>)
  22. Subject: Configure now asks if #! is to be used to start perl
  23. Subject: minor tr argument problem fixed within Configure
  24. Subject: new standard format for vacation message
  25. Subject: new parameters: nfslock, mmdf, mmdfbox and compress
  26. Subject: can now deal with compression
  27. Subject: knows about MMDF-style mailboxes
  28. Subject: leading perl start up is now configured
  29. Subject: documents new features: compression and MMDF mailboxes
  30. Subject: can now perform NFS-safe lockings
  31. Subject: locking operation automatically checks for outdated locks
  32. Subject: saving operation now knows about compression
  33. Subject: sanity checks performed on saved mail for NFS failure
  34. Subject: outdated locks checking now performed by &acs_rqst
  35. Subject: typo fix
  36. Subject: now checks for error on file closing (buffer flushing)
  37. Subject: undocumented feature commented (WRITE may allow hooks)
  38. Subject: now knows about NFS-safe locks
  39. Subject: lock outdating now performed by &acs_rqst
  40. Subject: make sure tests are not run as super-user
  41. Subject: perload now knows about leading ':' for shell startup
  42. Subject: two new (empty) test files in agent/test/misc
  43. Subject: new library files for folder compression and MMDF support
  44. Date: Tue Jan 12 13:41:57 PST 1993
  45. From: Raphael Manfredi <ram@eiffel.com>
  46.  
  47. Description:
  48.     Military timezones did not parse correctly.
  49.     (fix by Paul Marquess <pmarquess@rosebud.bfsec.bt.co.uk> posted
  50.     on comp.lang.perl and integrated)
  51.  
  52.     Configure now asks if #! is to be used to start perl. This should take
  53.     care of the "Illegal variable name" error message emitted when csh
  54.     attempts to start a perl script!
  55.  
  56.     New standard format for vacation message.
  57.  
  58.     New configuration parameters: nfslock, mmdf, mmdfbox and compress.
  59.     It is now possible to get NFS-safe locks. Moreover, the mailagent
  60.     can now deal with compression and knows about MMDF-style mailboxes.
  61.  
  62.     Documents new features: compression and MMDF mailboxes.
  63.  
  64.     Sanity checks are now performed on saved mail for NFS failure. The
  65.     mail file is stat()'ed to make sure all the NFS write() have been
  66.     correctly performed. Otherwise, some soft-mounted partitions could
  67.     end-up with an empty mail message without any error report!
  68.     Thank you NFS.
  69.  
  70.     Make sure tests are not run as super-user. Some of the tests involve
  71.     writing permissions checks, which does not concern the super-user.
  72.     The test suite expects some failure which cannot happen when root is
  73.     involved.
  74.  
  75.     Perload now knows about leading ':' for shell startup. Putting a
  76.     leading colon should tell the kernel that the file is a "shell"
  77.     script to be run using the Bourne shell and not by the current
  78.     shell held in the SHELL environment variable. We want to avoid all
  79.     the csh-like shells.
  80.  
  81.     Two new (empty) test files in agent/test/misc. You will have to create
  82.     those files by hand at the end of the patching process, or further
  83.     patches will not apply. Those files are empty because I did not find
  84.     the time to write them, but this set of patches had to get out.
  85.  
  86.     New library files for folder compression and MMDF support.
  87.  
  88.  
  89. Fix:    From rn, say "| patch -p -N -d DIR", where DIR is your mailagent source
  90.     directory.  Outside of rn, say "cd DIR; patch -p -N <thisarticle".
  91.     If you don't have the patch program, apply the following by hand,
  92.     or get patch (version 2.0, latest patchlevel).
  93.  
  94.     After patching:
  95.         *** DO NOTHING--INSTALL ALL PATCHES UP THROUGH #16 FIRST ***
  96.  
  97.     If patch indicates that patchlevel is the wrong version, you may need
  98.     to apply one or more previous patches, or the patch may already
  99.     have been applied.  See the patchlevel.h file to find out what has or
  100.     has not been applied.  In any event, don't continue with the patch.
  101.  
  102.     If you are missing previous patches they can be obtained from me:
  103.  
  104.         Raphael Manfredi <ram@eiffel.com>
  105.  
  106.     If you send a mail message of the following form it will greatly speed
  107.     processing:
  108.  
  109.         Subject: Command
  110.         @SH mailpatch PATH mailagent 2.9 LIST
  111.                ^ note the c
  112.  
  113.     where PATH is a return path FROM ME TO YOU either in Internet notation,
  114.     or in bang notation from some well-known host, and LIST is the number
  115.     of one or more patches you need, separated by spaces, commas, and/or
  116.     hyphens.  Saying 35- says everything from 35 to the end.
  117.  
  118.     To get some more detailed instructions, send me the following mail:
  119.  
  120.         Subject: Command
  121.         @SH mailhelp PATH
  122.  
  123.  
  124. Index: patchlevel.h
  125. Prereq: 14
  126. 4c4
  127. < #define PATCHLEVEL 14
  128. ---
  129. > #define PATCHLEVEL 15
  130.  
  131. Index: agent/pl/compress.pl
  132. *** agent/pl/compress.pl.old    Tue Jan 12 13:41:15 1993
  133. --- agent/pl/compress.pl    Tue Jan 12 13:41:15 1993
  134. ***************
  135. *** 0 ****
  136. --- 1,172 ----
  137. + ;# $Id: compress.pl,v 2.9.1.1 93/01/12 12:12:08 ram Exp $
  138. + ;#
  139. + ;#  Copyright (c) 1992, Raphael Manfredi
  140. + ;#
  141. + ;#  You may redistribute only under the terms of the GNU General Public
  142. + ;#  Licence as specified in the README file that comes with dist.
  143. + ;#
  144. + ;# $Log:    compress.pl,v $
  145. + ;# Revision 2.9.1.1  93/01/12  12:12:08  ram
  146. + ;# patch15: created
  147. + ;# 
  148. + ;# 
  149. + ;# This module handles compressed folders. Each folder specified in the file
  150. + ;# 'compress' from the configuration file is candidate for compression checks.
  151. + ;# The file specifies folders using shell patterns. If the pattern does not
  152. + ;# start with a /, the match is only attempted to the basename of the folder.
  153. + ;# 
  154. + ;# Folder uncompressed are recompressed only before the mailagent is about
  155. + ;# to exit, so that the burden of successive decompressions is avoided should
  156. + ;# two or more mails be delivered to the same compressed folder. However, if
  157. + ;# there is not enough disk space to hold all the uncompressed folder, the
  158. + ;# mailagent will try to recompress them to try to make some room.
  159. + ;#
  160. + ;# The initial patterns are held in the @compress array, while the compression
  161. + ;# status is stored within %compress. The key is the file name, and the value
  162. + ;# is 0 if uncompression was attempted but failed somehow so recompression must
  163. + ;# not be done, or 1 if uncompression was successful and the folder is flagged
  164. + ;# for delayed recompression.
  165. + #
  166. + # Folder compression
  167. + #
  168. + package compress;
  169. + # Read in the compression file into the @compress array. As usual, shell
  170. + # comments are ignored.
  171. + sub init {
  172. +     unless (open(COMPRESS, "$cf'compress")) {
  173. +         &'add_log("WARNING cannot open compress file $cf'compress: $!")
  174. +             if $'loglvl > 5;
  175. +         return;
  176. +     }
  177. +     while (<COMPRESS>) {
  178. +         chop;
  179. +         next if /^\s*#/;            # Skip comments
  180. +         next if /^\s*$/;            # And blank lines
  181. +         $_ = &'perl_pattern($_);    # Shell pattern to perl one
  182. +         s/^~/$cf'home/;                # ~ substitution
  183. +         $_ = '.*/'.$_ unless m|^/|;    # Focus on basename unless absolute path
  184. +         push(@compress, $_);        # Record pattern
  185. +     }
  186. +     close COMPRESS;
  187. + }
  188. + # Uncompress a folder, and record it in the %compress array for further
  189. + # recompression at the end of the mailagent processing. Return 1 for success.
  190. + # If the $retry parameter is set, other folders will be recompressed should
  191. + # this particular uncompression fail.
  192. + sub uncompress {
  193. +     local($folder, $retry) = @_;    # Folder to be decompressed
  194. +     return if defined $compress{$folder};    # We already dealt with that folder
  195. +     # Make sure there is a .Z file, and that the corresponding folder is not
  196. +     # already present. If there is no .Z file but the folder already exists,
  197. +     # mark it uncompressed.
  198. +     if (-f "$folder.Z") {        # A compressed form exists
  199. +         if (-f $folder) {        # As well as an uncompressed form
  200. +             &'add_log("WARNING both folders $folder and $folder.Z exist")
  201. +                 if $'loglvl > 5;
  202. +             &'add_log("NOTICE ignoring compressed file") if $'loglvl > 6;
  203. +             $compress{$folder} = 0;        # Do not recompress it
  204. +             return 1;
  205. +         }
  206. +         # Normal case: there is a compressed file and no uncompressed version
  207. +         local($status) = system("uncompress $folder.Z");
  208. +         if ($status) {            # Uncompression failed
  209. +             local($retrying);
  210. +             $retrying = " (retrying)" if $retry;
  211. +             &'add_log("ERROR cannot uncompress $folder$retrying") if $'loglvl;
  212. +             # Maybe there is not enough disk space, and maybe we can get some
  213. +             # by recompressing the folders we have decompressed so far.
  214. +             if ($retry) {            # Attempt is to be retried
  215. +                 &recompress;        # Recompress other folders, if any
  216. +                 return 0;            # And report failure
  217. +             }
  218. +             &'add_log("WARNING $folder present before delivery")
  219. +                 if -f $folder && $'loglvl > 5;
  220. +             &'add_log("ERROR original $folder.Z lost")
  221. +                 if ! -f "$folder.Z" && $'loglvl;
  222. +             $compress{$folder} = 0;        # Do not recompress it
  223. +         } else {                # Folder should be decompressed
  224. +             if (-f "$folder.Z") {
  225. +                 &'add_log("WARNING compressed $folder still present")
  226. +                     if $'loglvl > 5;
  227. +                 $compress{$folder} = 0;    # Do not recompress it
  228. +             } else {
  229. +                 $compress{$folder} = 1;    # Will be recompressed after delivery
  230. +             }
  231. +             &'add_log("uncompressed $folder") if $'loglvl > 8;
  232. +         }
  233. +     } else {
  234. +         $compress{$folder} = 1;        # Folder will be compressed after creation
  235. +     }
  236. +     1;            # Success
  237. + }
  238. + # Compress a folder
  239. + sub compress {
  240. +     local($folder) = @_;        # Folder to be compressed
  241. +     return unless $compress{$folder};    # Folder not to be recompressed
  242. +     delete $compress{$folder};            # Mark it compressed anyway
  243. +     if (-f "$folder.Z") {        # A compressed form exists
  244. +         &'add_log("ERROR compressed $folder already present") if $'loglvl;
  245. +         return;
  246. +     }
  247. +     if (0 != &'acs_rqst($folder)) {        # Cannot compress if not locked
  248. +         &'add_log("NOTICE $folder locked, skiping compression") if $'loglvl > 6;
  249. +         return;
  250. +     }
  251. +     local($status) = system("compress $folder");
  252. +     if ($status) {
  253. +         &'add_log("ERROR cannot compress $folder") if $'loglvl;
  254. +         if (-f $folder) {
  255. +             unless (unlink "$folder.Z") {
  256. +                 &'add_log("ERROR cannot remove $folder.Z: $!") if $'loglvl;
  257. +             } else {
  258. +                 &'add_log("NOTICE removing $folder.Z") if $'loglvl > 6;
  259. +             }
  260. +         } else {
  261. +             &'add_log("ERROR original $folder lost") if $'loglvl;
  262. +         }
  263. +     } else {
  264. +         &'add_log("WARNING uncompressed $folder still present")
  265. +             if -f $folder && $'loglvl > 5;
  266. +         &'add_log("compressed $folder") if $'loglvl > 8;
  267. +     }
  268. +     &'free_file($folder);
  269. + }
  270. + # Recompress all folders which have been delivered to
  271. + sub recompress {
  272. +     foreach $file (keys %compress) {
  273. +         &compress($file);
  274. +     }
  275. + }
  276. + # Restore uncompressed folder if listed in the compression list
  277. + sub restore {
  278. +     return unless $cf'compress;        # Do nothing if no compress parameter
  279. +     return unless -s $cf'compress;    # No compress list file, or empty
  280. +     &init unless defined @compress;    # Initialize array only once
  281. +     local($folder) = @_;            # Folder candidate for uncompression
  282. +     &'add_log("candidate folder is $folder") if $'loglvl > 18;
  283. +     # Loop over each pattern in the compression file and see if the folder
  284. +     # matches one of them. As soon as one matches, the folder is uncompressed
  285. +     # if necessary and the processing is over.
  286. +     foreach $pattern (@compress) {
  287. +         &'add_log("matching against '$pattern'") if $'loglvl > 19;
  288. +         if ($folder =~ /^$pattern$/) {
  289. +             &'add_log("matched '$pattern'") if $'loglvl > 18;
  290. +             # Give it two shots. The second parameter is a retrying flag.
  291. +             # The difference between the two is that recompression of other
  292. +             # uncompressed folders is attempted the first time if the folder
  293. +             # cannot be uncompressed (assuming low disk space).
  294. +             &uncompress($folder, 0) unless &uncompress($folder, 1);
  295. +             last;
  296. +         }
  297. +     }
  298. + }
  299. + package main;
  300.  
  301. Index: agent/man/mailagent.SH
  302. Prereq: 2.9.1.7
  303. *** agent/man/mailagent.SH.old    Tue Jan 12 13:41:03 1993
  304. --- agent/man/mailagent.SH    Tue Jan 12 13:41:05 1993
  305. ***************
  306. *** 18,24 ****
  307.   .TH MAILAGENT $manext "Version $VERSION PL$PATCHLEVEL"
  308.   ''' @(#) Manual page for mailagent's filter -- (c) ram February 1991
  309.   '''
  310. ! ''' $Id: mailagent.SH,v 2.9.1.7 92/12/01 09:16:23 ram Exp $
  311.   '''
  312.   '''  Copyright (c) 1991, 1992, Raphael Manfredi
  313.   '''
  314. --- 18,24 ----
  315.   .TH MAILAGENT $manext "Version $VERSION PL$PATCHLEVEL"
  316.   ''' @(#) Manual page for mailagent's filter -- (c) ram February 1991
  317.   '''
  318. ! ''' $Id: mailagent.SH,v 2.9.1.8 93/01/12 12:09:46 ram Exp $
  319.   '''
  320.   '''  Copyright (c) 1991, 1992, Raphael Manfredi
  321.   '''
  322. ***************
  323. *** 26,31 ****
  324. --- 26,34 ----
  325.   '''  License as specified in the README file that comes with dist.
  326.   '''
  327.   ''' $Log:    mailagent.SH,v $
  328. + ''' Revision 2.9.1.8  93/01/12  12:09:46  ram
  329. + ''' patch15: documents new features: compression and MMDF mailboxes
  330. + ''' 
  331.   ''' Revision 2.9.1.7  92/12/01  09:16:23  ram
  332.   ''' patch13: fixed various typos on the word "Precedence"
  333.   ''' patch13: new paragraph about file inclusion
  334. ***************
  335. *** 186,191 ****
  336. --- 189,198 ----
  337.   Name of the file containing authorized commands. Needed when PROCESS is used.
  338.   (suggested: \$spool/commands).
  339.   .TP
  340. + .I compress
  341. + Name of the file containing the list of compressed folders. See section about
  342. + folder compression. This is an optional parameter. (suggested: ~/.compress).
  343. + .TP
  344.   .I context
  345.   File holding the mailagent context. The context saves some variables which
  346.   need to be kept over the life of the process. Needed if auto cleaning is
  347. ***************
  348. *** 230,239 ****
  349. --- 237,264 ----
  350.   Maximum size in bytes of files before using \fIkit\fR for sending files. This
  351.   is used by PROCESS. (suggested: 150000).
  352.   .TP
  353. + .I mmdf
  354. + Set this to ON if you wish to be able to save mail in MMDF-style mailboxes.
  355. + (suggested: OFF, unless you use MMDF or MH).
  356. + .TP
  357. + .I mmdfbox
  358. + The value of this variable only matters when \fImmdf\fR is on. If set to ON,
  359. + then new folders will be created as MMDF ones. This variable is not used when
  360. + saving to an existing folder, since in that case the \fImailagent\fR will
  361. + automatically determine the type and save the message accordingly.
  362. + (suggested: OFF, unless you use MMDF or wish to use MH's \fImshf\fR).
  363. + .TP
  364.   .I name
  365.   First name of the user, used by the mailagent when referring to you. This sets
  366.   the value of the %U macro.
  367.   .TP
  368. + .I nfslock
  369. + Set it to ON to ensure NFS-secure locks. The difference is that the hostname
  370. + is used in conjunction with the PID to obtain a lock. However, the mailagent
  371. + has to fork/exec to obtain that information. This is an optional parameter
  372. + which is set to OFF by default. (suggested: OFF if you deliver
  373. + mail from only one machine, even though it's via NFS).
  374. + .TP
  375.   .I path
  376.   Minimum path to be used by C filter program. To set a specific path
  377.   for a machine \fIhost\fR, set up a \fIp_host\fR variable. This will
  378. ***************
  379. *** 2083,2088 ****
  380. --- 2108,2170 ----
  381.   For those hooks which are finally ran by perl, the special @INC array has
  382.   the mailagent's own private library path prepended to it, so that \fIrequire\fR
  383.   first looks in this place.
  384. + .SH "FOLDERS"
  385. + A folder is a file which can be the target of a delivery by the mailagent,
  386. + that is to say the argument of SAVE-like commands.
  387. + '''
  388. + .SS "Folder Format"
  389. + .PP
  390. + By default, mails are written into folders according to the standard UNIX-style
  391. + mailbox format: each mail starts with a leading \fIFrom\fR line bearing the
  392. + sender's address and the date. However, by setting the \fImmdf\fR parameter
  393. + from the \fI~/.mailagent\fR to ON, the \fImailagent\fR will be able to save
  394. + messages in MMDF format: each message is sandwiched between two lines of four
  395. + ctrl-A characters (ASCII code 1) and the leading \fIFrom\fR line is removed.
  396. + .PP
  397. + When MMDF mode is activated, each folder will be scanned to see if it is a
  398. + UNIX-style or MMDF-style mailbox and the message will be saved accordingly.
  399. + When saving to a new folder, the default is to create a UNIX-style mailbox,
  400. + unless the \fImmdfbox\fR configuration variable was set to ON, in which case
  401. + the MMDF format prevails.
  402. + .PP
  403. + Note that the MMDF format is also the standard for MH packed folders, so by
  404. + enabling the MMDF mode, you can actually deliver directly to those packed
  405. + folders. The MH command \fIinc\fR is able to incorporate mail from either
  406. + form anyway, i.e. it does not matter whether the folder is in UNIX format
  407. + (also called UUCP-style) or in MMDF format.
  408. + '''
  409. + .SS "Folder Compression"
  410. + .PP
  411. + If you have \fIcompress\fR in your PATH (as set up by \fI~/.mailagent\fR), then
  412. + you may wish to use folder compression to save some disk space, especially when
  413. + you are away for some time and do not want to see your mail fill-up the
  414. + filesystem.
  415. + .PP
  416. + To achieve folder compression, you have to set up a file, referred to by the
  417. + \fIcompress\fR configuration variable. This file must list folder names, one
  418. + per line, with blank lines ignored and shell-style (#) comments allowed. You
  419. + may use shell-style patterns to specify the folders, and the match will be
  420. + attempted on the full pathname of the folder (~ subsitution occurs). If you
  421. + do not specify a pattern starting with a leading '/' character, then the match
  422. + will be attempted on the basename of the folder (i.e. the last componenent of
  423. + the folder path). If you want to compress all your folders, then simply put
  424. + a single '*' inside this file.
  425. + .PP
  426. + When attempting delivery, the mailagent will check the folder name against
  427. + the list of patterns in the compress file. If there is a match, the folder is
  428. + flagged as compressed. Then the mailagent attempts decompression if there
  429. + is already a compressed form (a .Z file) and if no uncompressed form is present.
  430. + Delivery is then made to the uncompressed folder. However, recompression is not
  431. + done immediately, since it is still possible to get messages to that folder in
  432. + a single batch delivery. Should disk space become so tight that decompression
  433. + of other folders is impossible, the mailagent will recompress the folders
  434. + it has already uncompressed. Otherwise, it waits until the last moment.
  435. + .PP
  436. + If for some reason there is a .Z compresed folder which cannot be decompressed,
  437. + the mailagent will deliver the mail to the plain folder. Further delivery
  438. + to that folder will be faced with both a compressed and a plain version of the
  439. + folder, and that will get you a warning in the log file, but delivery will be
  440. + made automatically to the plain file.
  441.   .SH EXAMPLES
  442.   Here are some examples of rule files. First, if you do not specify a rule
  443.   file or if it is empty, the following built-in rule applies:
  444.  
  445. Index: agent/pl/actions.pl
  446. Prereq: 2.9.1.4
  447. *** agent/pl/actions.pl.old    Tue Jan 12 13:41:12 1993
  448. --- agent/pl/actions.pl    Tue Jan 12 13:41:13 1993
  449. ***************
  450. *** 1,4 ****
  451. ! ;# $Id: actions.pl,v 2.9.1.4 92/12/01 09:18:05 ram Exp $
  452.   ;#
  453.   ;#  Copyright (c) 1992, Raphael Manfredi
  454.   ;#
  455. --- 1,4 ----
  456. ! ;# $Id: actions.pl,v 2.9.1.5 93/01/12 12:11:44 ram Exp $
  457.   ;#
  458.   ;#  Copyright (c) 1992, Raphael Manfredi
  459.   ;#
  460. ***************
  461. *** 6,11 ****
  462. --- 6,15 ----
  463.   ;#  Licence as specified in the README file that comes with dist.
  464.   ;#
  465.   ;# $Log:    actions.pl,v $
  466. + ;# Revision 2.9.1.5  93/01/12  12:11:44  ram
  467. + ;# patch15: saving operation now knows about compression
  468. + ;# patch15: sanity checks performed on saved mail for NFS failure
  469. + ;# 
  470.   ;# Revision 2.9.1.4  92/12/01  09:18:05  ram
  471.   ;# patch13: allowed file inclusion for KEEP and STRIP
  472.   ;# patch13: file inclusion processing now handled by &include_file
  473. ***************
  474. *** 55,83 ****
  475.       local($failed) = 0;                # Printing status
  476.       &add_log("starting SAVE $mailbox") if $loglvl > 15;
  477.       if (-x $mailbox) {                # Folder hook
  478. !         &save_hook;
  479. !     } else {
  480. !         &save_folder;
  481.       }
  482.       &emergency_save if $failed;
  483.       ($mailbox, $failed);            # Where save was made and failure status
  484.   }
  485.   
  486. ! # Called by &save when folder is a regular one (i.e. not a hook). Manipulates
  487. ! # variables in the context of &save.
  488.   sub save_folder {
  489.       if (open(MBOX, ">>$mailbox")) {
  490. !         do mbox_lock($mailbox);            # Lock mailbox
  491. !         # First print the Header, and add the X-Filter: line.
  492. !         (print MBOX $Header{'Head'}) || ($failed = 1);
  493. !         (print MBOX $FILTER, "\n\n") || ($failed = 1);
  494. !         (print MBOX $Header{'Body'}) || ($failed = 1);
  495. !         print MBOX "\n";                # Allow parsing by other tools
  496. !         do mbox_unlock($mailbox);        # Will close file
  497. !         # Logging only in case of error
  498. !         if ($failed) {
  499. !             do add_log("ERROR could not save mail in $mailbox") if $loglvl > 0;
  500.           }
  501.       } else {
  502.           if (-f "$mailbox") {
  503.               do add_log("ERROR cannot append to $mailbox") if $loglvl;
  504. --- 59,116 ----
  505.       local($failed) = 0;                # Printing status
  506.       &add_log("starting SAVE $mailbox") if $loglvl > 15;
  507.       if (-x $mailbox) {                # Folder hook
  508. !         $failed = &save_hook;        # Deliver to program
  509. !     } else {                        # Saving to a normal folder
  510. !         # Uncompress folders if necessary. The restore routine will perform
  511. !         # the necessary checks and return immediately if no compression is
  512. !         # wanted for that particular folder. However, we can avoid the overhead
  513. !         # of calling this routine (and loading it when using dataloading) if
  514. !         # the 'compress' configuration parameter is missing.
  515. !         &compress'restore($mailbox) if $cf'compress;
  516. !         $failed = &save_folder($mailbox);
  517.       }
  518. +     &add_log("ERROR could not save mail in $mailbox") if $failed && $loglvl;
  519.       &emergency_save if $failed;
  520.       ($mailbox, $failed);            # Where save was made and failure status
  521.   }
  522.   
  523. ! # Called by &save when folder is a regular one (i.e. not a hook).
  524.   sub save_folder {
  525. +     local($mailbox) = @_;            # Where mail should be saved
  526. +     local($amount);                    # Amount of bytes written
  527. +     local($failed);
  528.       if (open(MBOX, ">>$mailbox")) {
  529. !         &mbox_lock($mailbox);        # Lock mailbox, now have exclusive access
  530. !         local($size) = -s $mailbox;    # Initial mailbox size
  531. !         # If MMDF-style mailboxes are allowed, then the saving routine will
  532. !         # try to determine what kind of folder it is delivering to and choose
  533. !         # the right format. Otherwise, standard Unix format is assumed.
  534. !         if ($cf'mmdf =~ /on/i) {    # MMDF-style allowed
  535. !             # Save to mailbox, selecting the right format (UNIX vs MMDF)
  536. !             ($failed, $amount) = &mmdf'save(*MBOX, $mailbox);
  537. !         } else {
  538. !             # Save to UNIX folder
  539. !             ($failed, $amount) = &mmdf'save_unix(*MBOX);
  540.           }
  541. +         # Because we might write over NFS, and because we might have had to
  542. +         # force fate to get a lock, it is wise to make sure the folder has the
  543. +         # right size, which would tend to indicate the mail made it to the
  544. +         # buffer cache, if not to the disk itself.
  545. +         local($should) = $size + $amount;    # Computed new size for mailbox
  546. +         local($new_size) = -s $mailbox;        # Last write was flushed to disk
  547. +         &add_log("ERROR $mailbox has $new_size bytes (should have $should)")
  548. +             if $new_size != $should && $loglvl;
  549. +         $failed = 1 if $new_size != $should;
  550. +         # Finally, release the lock on the mailbox and close the file. If the
  551. +         # closing operation fails for whatever reason, the routine will return
  552. +         # a 1, so $failed will be set. Of course, "normally" it should not
  553. +         # fail at that point, since the mail was previously flushed.
  554. +         $failed |= &mbox_unlock($mailbox);    # Will close file
  555.       } else {
  556.           if (-f "$mailbox") {
  557.               do add_log("ERROR cannot append to $mailbox") if $loglvl;
  558. ***************
  559. *** 86,91 ****
  560. --- 119,125 ----
  561.           }
  562.           $failed = 1;
  563.       }
  564. +     $failed;        # Propagate failure status
  565.   }
  566.   
  567.   # Called by &save when folder is a hook. This simply calls the mailhook
  568. ***************
  569. *** 92,99 ****
  570.   # program, which will analyze the hook and perform the necessary actions.
  571.   sub save_hook {
  572.       &add_log("hooking mail on folder") if $loglvl > 15;
  573. !     $failed =
  574. !         &shell_command("$privlib/mailhook $mailbox", $MAIL_INPUT, $NO_FEEDBACK);
  575.   }
  576.   
  577.   # The "PROCESS" command
  578. --- 126,133 ----
  579.   # program, which will analyze the hook and perform the necessary actions.
  580.   sub save_hook {
  581.       &add_log("hooking mail on folder") if $loglvl > 15;
  582. !     # Return command failure status (0 means ok)
  583. !     &shell_command("$privlib/mailhook $mailbox", $MAIL_INPUT, $NO_FEEDBACK);
  584.   }
  585.   
  586.   # The "PROCESS" command
  587.  
  588. Index: agent/pl/acs_rqst.pl
  589. Prereq: 2.9
  590. *** agent/pl/acs_rqst.pl.old    Tue Jan 12 13:41:08 1993
  591. --- agent/pl/acs_rqst.pl    Tue Jan 12 13:41:08 1993
  592. ***************
  593. *** 1,4 ****
  594. ! ;# $Id: acs_rqst.pl,v 2.9 92/07/14 16:49:28 ram Exp $
  595.   ;#
  596.   ;#  Copyright (c) 1991, Raphael Manfredi
  597.   ;#
  598. --- 1,4 ----
  599. ! ;# $Id: acs_rqst.pl,v 2.9.1.1 93/01/12 12:10:37 ram Exp $
  600.   ;#
  601.   ;#  Copyright (c) 1991, Raphael Manfredi
  602.   ;#
  603. ***************
  604. *** 6,46 ****
  605.   ;#  Licence as specified in the README file that comes with dist.
  606.   ;#
  607.   ;# $Log:    acs_rqst.pl,v $
  608.   ;# Revision 2.9  92/07/14  16:49:28  ram
  609.   ;# 3.0 beta baseline.
  610.   ;# 
  611.   ;#
  612. ! # Asks for the exclusive access of a file
  613. ! # The given parameter (let's say F) is the absolute path
  614. ! # of the file we want to access. The routine checks for the
  615. ! # presence of F.lock. If it exists, it sleeps 1 second and tries
  616. ! # again. After 10 trys, it reports failure by returning -1.
  617. ! # Otherwise, file F.lock is created and the pid of the current
  618.   # process is written. It is checked afterwards.
  619.   sub acs_rqst {
  620.       local($file) = @_;    # file to be locked
  621.       local($max) = 10;    # max number of attempts
  622.       local($mask);        # to save old umask
  623.       while ($max) {
  624.           $max--;
  625.           if (-f "$file.lock") {
  626. !             sleep(2);    # busy: wait
  627.               next;
  628.           }
  629.           # Attempt to create lock
  630.           $mask = umask(0333);            # no write permission
  631.           if (open(FILE, ">$file.lock")) {
  632. !             print FILE "$$\n";            # write pid
  633.               close FILE;
  634.               umask($mask);                # restore old umask
  635.               # Check lock
  636.               open(FILE, "$file.lock");
  637. !             $_ = <FILE>;                # read contents
  638.               close FILE;
  639. !             last if int($_) == $$;        # lock is ok
  640.           } else {
  641.               umask($mask);                # restore old umask
  642. !             sleep(2);                    # busy: wait
  643.           }
  644.       }
  645.       if ($max) {
  646. --- 6,77 ----
  647.   ;#  Licence as specified in the README file that comes with dist.
  648.   ;#
  649.   ;# $Log:    acs_rqst.pl,v $
  650. + ;# Revision 2.9.1.1  93/01/12  12:10:37  ram
  651. + ;# patch15: can now perform NFS-safe lockings
  652. + ;# patch15: locking operation automatically checks for outdated locks
  653. + ;# 
  654.   ;# Revision 2.9  92/07/14  16:49:28  ram
  655.   ;# 3.0 beta baseline.
  656.   ;# 
  657.   ;#
  658. ! ;# The basic file locking scheme implemented here by acs_rqst is not completely
  659. ! ;# suitable with NFS if multiple mailagent can run, since they could have the
  660. ! ;# same PID on different machine and both think they got a lock. To make this
  661. ! ;# work with NFS, the ~/.mailagent config file must have the 'nfslock' variable
  662. ! ;# set to 'YES', which will cause the mailagent to include hostname informations
  663. ! ;# in the lock file.
  664. ! ;#
  665. ! ;# The traditional NFS scheme of having a `hostname`.pid file linked to .lock
  666. ! ;# (since the linking operation remains atomic even with NFS) does not seem
  667. ! ;# suitable here, since I want to be able to recover from crashes, and detect
  668. ! ;# out-of-date locks. Therefore, I must be able to know what is the name of the
  669. ! ;# lock file. The link/unlink trick could leave some temporary files around.
  670. ! ;# Since write on disks are atomic anyway, only one process can conceivably
  671. ! ;# obtain a lock with my scheme.
  672. ! ;#
  673. ! ;# The NFS-secure lock is made optional because, in order to get the hostname,
  674. ! ;# perl must fork to exec an appropriate program. This added overhead might not
  675. ! ;# be necessary in all the situations.
  676. ! ;#
  677. ! # Asks for the exclusive access of a file. The config variable 'nfslock'
  678. ! # determines whether the locking scheme has to be NFS-secure or not.
  679. ! # The given parameter (let's say F) is the absolute path of the file we want
  680. ! # to access. The routine checks for the presence of F.lock. If it exists, it
  681. ! # sleeps 2 seconds and tries again. After 10 trys, it reports failure by
  682. ! # returning -1. Otherwise, file F.lock is created and the pid of the current
  683.   # process is written. It is checked afterwards.
  684.   sub acs_rqst {
  685.       local($file) = @_;    # file to be locked
  686.       local($max) = 10;    # max number of attempts
  687. +     local($delay) = 2;    # seconds to wait between attempts
  688.       local($mask);        # to save old umask
  689. +     local($stamp);        # string written in lock file
  690. +     &checklock($file);    # avoid long-lasting locks
  691. +     if ($cf'nfslock =~ /on/i) {            # NFS-secure lock wanted
  692. +         $stamp = "$$" . &hostname;        # use PID and hostname
  693. +     } else {
  694. +         $stamp = "$$";                    # use PID only (may spare a fork)
  695. +     }
  696.       while ($max) {
  697.           $max--;
  698.           if (-f "$file.lock") {
  699. !             sleep($delay);                # busy: wait
  700.               next;
  701.           }
  702.           # Attempt to create lock
  703.           $mask = umask(0333);            # no write permission
  704.           if (open(FILE, ">$file.lock")) {
  705. !             print FILE "$stamp\n";        # write locking stamp
  706.               close FILE;
  707.               umask($mask);                # restore old umask
  708.               # Check lock
  709.               open(FILE, "$file.lock");
  710. !             chop($_ = <FILE>);            # read contents
  711.               close FILE;
  712. !             last if $_ eq $stamp;        # lock is ok
  713.           } else {
  714.               umask($mask);                # restore old umask
  715. !             sleep($delay);                # busy: wait
  716.           }
  717.       }
  718.       if ($max) {
  719.  
  720. Index: agent/pl/mmdf.pl
  721. *** agent/pl/mmdf.pl.old    Tue Jan 12 13:41:40 1993
  722. --- agent/pl/mmdf.pl    Tue Jan 12 13:41:40 1993
  723. ***************
  724. *** 0 ****
  725. --- 1,110 ----
  726. + ;# $Id: mmdf.pl,v 2.9.1.1 93/01/12 13:34:34 ram Exp $
  727. + ;#
  728. + ;#  Copyright (c) 1992, Raphael Manfredi
  729. + ;#
  730. + ;#  You may redistribute only under the terms of the GNU General Public
  731. + ;#  Licence as specified in the README file that comes with dist.
  732. + ;#
  733. + ;# $Log:    mmdf.pl,v $
  734. + ;# Revision 2.9.1.1  93/01/12  13:34:34  ram
  735. + ;# patch15: created
  736. + ;# 
  737. + ;# 
  738. + ;# This set of routine handles MMDF-style mailboxes, which differ from the
  739. + ;# traditional Unix-style boxes by encapsulating each message between 2 lines
  740. + ;# of 4 ^A, one at the begining and one at the end. The leading From_ line is
  741. + ;# consequently not needed and is removed.
  742. + ;#
  743. + ;# Note: this MMDF-style mailbox is also used by MH packed folders.
  744. + ;#
  745. + #
  746. + # MMDF-style saving routines
  747. + #
  748. + package mmdf;
  749. + # Attempt to save in a possible MMDF mailbox. The routine opens the mailbox
  750. + # and tries to determine what kind of mailbox it is, then selects the
  751. + # appropriate saving routine.
  752. + sub save {
  753. +     local(*FD, $mailbox) = @_;    # File descriptor and mailbox name
  754. +     if (&is_mmdf($mailbox)) {    # Folder looks like an MMDF mailbox
  755. +         &save_mmdf(*FD);        # Use MMDF format then
  756. +     } else {
  757. +         &save_unix(*FD);        # Be conservative and use standard format
  758. +     }
  759. + }
  760. +     
  761. + # Save to a MMDF-style mailbox and return failure status with message length
  762. + sub save_mmdf {
  763. +     local(*FD) = @_;            # File descriptor
  764. +     local($amount) = 0;            # Amount of bytes saved
  765. +     local($failed);
  766. +     local($from);
  767. +     local(@head) = split(/\n/, $'Header{'Head'});
  768. +     $from = shift(@head);        # The first From_ line has to be skipped
  769. +     unless ($from =~ /^From\s/) {
  770. +         &'add_log("WARNING leading From line absent") if $'loglvl > 5;
  771. +         unshift(@head, $from);    # Put it back if not a From_ line
  772. +     }
  773. +     (print FD "\01\01\01\01\n") || ($failed = 1);
  774. +     foreach $line (@head) {
  775. +         (print FD $line, "\n") || ($failed = 1);
  776. +         $amount += length($line) + 1;
  777. +     }
  778. +     (print FD $'FILTER, "\n\n") || ($failed = 1);
  779. +     (print FD $'Header{'Body'}) || ($failed = 1);
  780. +     &force_flushing(*FD);
  781. +     (print FD "\01\01\01\01\n") || ($failed = 1);
  782. +     $amount +=
  783. +         length($'Header{'Body'}) +    # Message body
  784. +         length($'FILTER) + 2 +        # X-Filter line plus two newlines
  785. +         5 + 5;                        # MMDF message delimiter lines
  786. +     ($failed, $amount);
  787. + }
  788. + # Save to a Unix-style mailbox and return failure status with message length
  789. + sub save_unix {
  790. +     local(*FD) = @_;            # File descriptor
  791. +     local($amount) = 0;            # Amount of bytes saved
  792. +     local($failed);
  793. +     # First print the Header, then add the X-Filter: line, followed by body.
  794. +     (print FD $'Header{'Head'}) || ($failed = 1);
  795. +     (print FD $'FILTER, "\n\n") || ($failed = 1);
  796. +     (print FD $'Header{'Body'}) || ($failed = 1);
  797. +     &force_flushing(*FD);
  798. +     (print FD "\n") || ($failed = 1);        # Allow parsing by other tools
  799. +     $amount +=
  800. +         length($'Header{'Head'}) +    # Message header
  801. +         length($'Header{'Body'}) +    # Message body
  802. +         length($'FILTER) + 2 +        # X-Filter line plus two newlines
  803. +         1;                            # Trailing new-line
  804. +     ($failed, $amount);
  805. + }
  806. + # Force flushing on file descriptor, so that after next print, we may rest
  807. + # assured everything as been written on disk. That way, we may stat the file
  808. + # without closing it (since that would release any flock-style lock).
  809. + sub force_flushing {
  810. +     local(*FD) = @_;            # File descriptor we want to flush
  811. +     select((select(FD), $| = 1)[0]);
  812. + }
  813. + # Guess whether the folder we are writing to is MMDF-style or not.
  814. + sub is_mmdf {
  815. +     local($folder) = @_;        # The folder to be scanned
  816. +     open(FOLDER, "$folder") || return 0;    # Can't open -> not MMDF, say.
  817. +     local($_);                    # First line from folder
  818. +     $_ = <FOLDER>;                # Can be empty
  819. +     close FOLDER;
  820. +     return 0 if /^From\s/;            # Looks like an Unix-style mailbox
  821. +     return 1 if /^\01\01\01\01\n/;    # This must be an MMDF-style mailbox
  822. +     # If we can't decide (most probably because $_ is empty), then choose
  823. +     # according to the 'mmdfbox' parameter.
  824. +     &'add_log("WARNING folder $folder may be corrupted")
  825. +         if $_ ne '' && $'loglvl > 5;
  826. +     $cf'mmdfbox =~ /on/i ? 1 : 0;    # Force MMDF if mmdfbox is ON
  827. + }
  828. + package main;
  829.  
  830. Index: agent/magent.SH
  831. Prereq: 2.9.1.3
  832. *** agent/magent.SH.old    Tue Jan 12 13:40:43 1993
  833. --- agent/magent.SH    Tue Jan 12 13:40:44 1993
  834. ***************
  835. *** 14,20 ****
  836.   esac
  837.   echo "Extracting agent/magent (with variable substitutions)"
  838.   $spitshell >magent <<!GROK!THIS!
  839. ! # feed this into perl
  840.       eval 'exec perl -S \$0 "\$@"'
  841.           if \$running_under_some_shell;
  842.   
  843. --- 14,20 ----
  844.   esac
  845.   echo "Extracting agent/magent (with variable substitutions)"
  846.   $spitshell >magent <<!GROK!THIS!
  847. ! $startperl
  848.       eval 'exec perl -S \$0 "\$@"'
  849.           if \$running_under_some_shell;
  850.   
  851. ***************
  852. *** 22,28 ****
  853.   # via the filter. Mine looks like this:
  854.   #   "|exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"
  855.   
  856. ! # $Id: magent.SH,v 2.9.1.3 92/12/01 09:14:07 ram Exp $
  857.   #
  858.   #  Copyright (c) 1991, 1992, Raphael Manfredi
  859.   #
  860. --- 22,28 ----
  861.   # via the filter. Mine looks like this:
  862.   #   "|exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"
  863.   
  864. ! # $Id: magent.SH,v 2.9.1.4 93/01/12 12:08:31 ram Exp $
  865.   #
  866.   #  Copyright (c) 1991, 1992, Raphael Manfredi
  867.   #
  868. ***************
  869. *** 30,35 ****
  870. --- 30,40 ----
  871.   #  Licence as specified in the README file that comes with dist.
  872.   #
  873.   # $Log:    magent.SH,v $
  874. + # Revision 2.9.1.4  93/01/12  12:08:31  ram
  875. + # patch15: can now deal with compression
  876. + # patch15: knows about MMDF-style mailboxes
  877. + # patch15: leading perl start up is now configured
  878. + # 
  879.   # Revision 2.9.1.3  92/12/01  09:14:07  ram
  880.   # patch13: hostname is now computed once and cached
  881.   # patch13: three new .pl files are now appended
  882. ***************
  883. *** 190,196 ****
  884.   $jobnum = &jobnum;                # Compute a job number
  885.   
  886.   # Allow only ONE mailagent at a time (resource consumming)
  887. ! do checklock($baselock);        # Make sure old locks do not remain
  888.   unless (-f $lockfile) {
  889.       # Try to get the lock file (acting as a token). We do not need locking if
  890.       # we have been invoked with an option and that option is not -q.
  891. --- 195,201 ----
  892.   $jobnum = &jobnum;                # Compute a job number
  893.   
  894.   # Allow only ONE mailagent at a time (resource consumming)
  895. ! &checklock($baselock);            # Make sure old locks do not remain
  896.   unless (-f $lockfile) {
  897.       # Try to get the lock file (acting as a token). We do not need locking if
  898.       # we have been invoked with an option and that option is not -q.
  899. ***************
  900. *** 287,292 ****
  901. --- 292,298 ----
  902.   
  903.   # End of mailagent processing
  904.   &write_stats;                    # Resynchronizes the statistics file
  905. + &compress'recompress;            # Compress some of the folders we delivered to
  906.   &contextual_operations;            # Perform all the contextual operations
  907.   &add_log("mailagent exits") if $loglvl > 17;
  908.   unlink $lockfile if $locked;
  909. ***************
  910. *** 446,456 ****
  911.       seek(MBOX, 0, 2);                # Someone may have appended something
  912.   }
  913.   
  914. ! # Remove lock on mailbox
  915.   sub mbox_unlock {
  916.       local($file) = @_;                # File name
  917. !     close MBOX;                        # Closing will remove flock lock
  918.       &free_file($file) unless $flock_only;        # Remove the .lock
  919.   }
  920.   
  921.   # Computes the e-mail address of the user
  922. --- 452,464 ----
  923.       seek(MBOX, 0, 2);                # Someone may have appended something
  924.   }
  925.   
  926. ! # Remove lock on mailbox and return a failure status if closing failed
  927.   sub mbox_unlock {
  928.       local($file) = @_;                # File name
  929. !     local($status);                    # Error status from close
  930. !     $status = close(MBOX);            # Closing will remove flock lock
  931.       &free_file($file) unless $flock_only;        # Remove the .lock
  932. +     $status ? 0 : 1;                # Return 0 for ok, 1 if close failed
  933.   }
  934.   
  935.   # Computes the e-mail address of the user
  936. ***************
  937. *** 575,579 ****
  938. --- 583,589 ----
  939.   $grep -v '^;#' pl/include.pl >>magent
  940.   $grep -v '^;#' pl/plural.pl >>magent
  941.   $grep -v '^;#' pl/hostname.pl >>magent
  942. + $grep -v '^;#' pl/mmdf.pl >>magent
  943. + $grep -v '^;#' pl/compress.pl >>magent
  944.   chmod 755 magent
  945.   $eunicefix magent
  946.  
  947. Index: Configure
  948. Prereq: 2.9.1.1
  949. *** Configure.old    Tue Jan 12 13:40:34 1993
  950. --- Configure    Tue Jan 12 13:40:35 1993
  951. ***************
  952. *** 16,22 ****
  953.   # Write to ram@eiffel.com (Raphael Manfredi) and I will send you the
  954.   # latest revision of the dist package, which includes metaconfig.)
  955.   
  956. ! # $Id: Configure,v 2.9.1.1 92/12/01 09:09:08 ram Exp $
  957.   #
  958.   # Generated on Tue Jul 14 19:38:33 PDT 1992 [metaconfig 2.8 PL13]
  959.   
  960. --- 16,22 ----
  961.   # Write to ram@eiffel.com (Raphael Manfredi) and I will send you the
  962.   # latest revision of the dist package, which includes metaconfig.)
  963.   
  964. ! # $Id: Configure,v 2.9.1.2 93/01/12 12:06:33 ram Exp $
  965.   #
  966.   # Generated on Tue Jul 14 19:38:33 PDT 1992 [metaconfig 2.8 PL13]
  967.   
  968. ***************
  969. *** 205,210 ****
  970. --- 205,211 ----
  971.   orgname=''
  972.   package=''
  973.   perlpath=''
  974. + startperl=''
  975.   pidtype=''
  976.   privlib=''
  977.   reg10=''
  978. ***************
  979. *** 2548,2553 ****
  980. --- 2549,2583 ----
  981.       esac
  982.   done
  983.   
  984. + : figure out how to guarantee perl startup
  985. + case "$sharpbang" in
  986. + *!)
  987. +     $cat <<EOH
  988. + I can use the #! construct to start perl on your system. This will make
  989. + startup of perl scripts faster, but may cause problems if you want to share
  990. + those scripts and perl is not in a standard place (/usr/bin/perl) on all your
  991. + platforms. The alternative is to force a shell by starting the script with a
  992. + single ':' character.
  993. + EOH
  994. +     dflt=n
  995. +     case "$startperl" in
  996. +     *!*) dflt=y;;
  997. +     '') case "$d_portable" in
  998. +         "$define") ;;
  999. +         *) dflt=y;;
  1000. +         esac;;
  1001. +     esac
  1002. +     rp='Shall I use #! to start up perl?'
  1003. +     . ./myread
  1004. +     case "$ans" in
  1005. +     y*|Y*) startperl="#!$perlpath";;
  1006. +     *) startperl=": # use perl";;
  1007. +     esac;;
  1008. + *) startperl=": # use perl";;
  1009. + esac
  1010.   : see what type pids are declared as in the kernel
  1011.   case "$pidtype" in
  1012.   '')
  1013. ***************
  1014. *** 2672,2678 ****
  1015.   
  1016.   : get C preprocessor symbols handy
  1017.   echo " "
  1018. ! echo $attrlist | $tr '[ - ]' '[\012-\012]' >Cppsym.know
  1019.   $cat <<EOSS >Cppsym
  1020.   $startsh
  1021.   case "\$1" in
  1022. --- 2702,2708 ----
  1023.   
  1024.   : get C preprocessor symbols handy
  1025.   echo " "
  1026. ! echo $attrlist | $tr ' ' '\012' >Cppsym.know
  1027.   $cat <<EOSS >Cppsym
  1028.   $startsh
  1029.   case "\$1" in
  1030. ***************
  1031. *** 2699,2705 ****
  1032.   case \$# in
  1033.   0) exit 1;;
  1034.   esac
  1035. ! echo \$* | $tr '[ - ]' '[\012-\012]' | $sed -e 's/\(.*\)/\\
  1036.   #ifdef \1\\
  1037.   exit 0; _ _ _ _\1\\     \1\\
  1038.   #endif\\
  1039. --- 2729,2735 ----
  1040.   case \$# in
  1041.   0) exit 1;;
  1042.   esac
  1043. ! echo \$* | $tr ' ' '\012' | $sed -e 's/\(.*\)/\\
  1044.   #ifdef \1\\
  1045.   exit 0; _ _ _ _\1\\     \1\\
  1046.   #endif\\
  1047. ***************
  1048. *** 3044,3049 ****
  1049. --- 3074,3080 ----
  1050.   orgname='$orgname'
  1051.   package='$package'
  1052.   perlpath='$perlpath'
  1053. + startperl='$startperl'
  1054.   pidtype='$pidtype'
  1055.   privlib='$privlib'
  1056.   reg10='$reg10'
  1057.  
  1058. Index: agent/pl/free_file.pl
  1059. Prereq: 2.9
  1060. *** agent/pl/free_file.pl.old    Tue Jan 12 13:41:29 1993
  1061. --- agent/pl/free_file.pl    Tue Jan 12 13:41:30 1993
  1062. ***************
  1063. *** 1,4 ****
  1064. ! ;# $Id: free_file.pl,v 2.9 92/07/14 16:50:00 ram Exp $
  1065.   ;#
  1066.   ;#  Copyright (c) 1991, Raphael Manfredi
  1067.   ;#
  1068. --- 1,4 ----
  1069. ! ;# $Id: free_file.pl,v 2.9.1.1 93/01/12 13:28:16 ram Exp $
  1070.   ;#
  1071.   ;#  Copyright (c) 1991, Raphael Manfredi
  1072.   ;#
  1073. ***************
  1074. *** 6,11 ****
  1075. --- 6,14 ----
  1076.   ;#  Licence as specified in the README file that comes with dist.
  1077.   ;#
  1078.   ;# $Log:    free_file.pl,v $
  1079. + ;# Revision 2.9.1.1  93/01/12  13:28:16  ram
  1080. + ;# patch15: now knows about NFS-safe locks
  1081. + ;# 
  1082.   ;# Revision 2.9  92/07/14  16:50:00  ram
  1083.   ;# 3.0 beta baseline.
  1084.   ;# 
  1085. ***************
  1086. *** 13,30 ****
  1087.   # Remove the lock on a file. Returns 0 if ok, -1 otherwise
  1088.   sub free_file {
  1089.       local($file) = @_;
  1090.   
  1091.       if ( -f "$file.lock") {
  1092.           # if lock exists, check for pid
  1093.           open(FILE, "$file.lock");
  1094. !         $_ = <FILE>;
  1095.           close FILE;
  1096. !         if (int($_) == $$) {
  1097. !             # pid is correct
  1098.               $result = 0;
  1099.               unlink "$file.lock";
  1100.           } else {
  1101. !             # pid is not correct
  1102.               $result = -1;
  1103.           }
  1104.       } else {
  1105. --- 16,40 ----
  1106.   # Remove the lock on a file. Returns 0 if ok, -1 otherwise
  1107.   sub free_file {
  1108.       local($file) = @_;
  1109. +     local($stamp);        # string written in lock file
  1110. +     if ($cf'nfslock =~ /on/i) {            # NFS-secure lock wanted
  1111. +         $stamp = "$$" . &hostname;        # use PID and hostname
  1112. +     } else {
  1113. +         $stamp = "$$";                    # use PID only (may spare a fork)
  1114. +     }
  1115.   
  1116.       if ( -f "$file.lock") {
  1117.           # if lock exists, check for pid
  1118.           open(FILE, "$file.lock");
  1119. !         chop($_ = <FILE>);
  1120.           close FILE;
  1121. !         if ($_ eq $stamp) {
  1122. !             # pid (plus hostname eventually) is correct
  1123.               $result = 0;
  1124.               unlink "$file.lock";
  1125.           } else {
  1126. !             # pid is not correct (we did not get that lock)
  1127.               $result = -1;
  1128.           }
  1129.       } else {
  1130.  
  1131. Index: agent/pl/matching.pl
  1132. Prereq: 2.9.1.2
  1133. *** agent/pl/matching.pl.old    Tue Jan 12 13:41:38 1993
  1134. --- agent/pl/matching.pl    Tue Jan 12 13:41:38 1993
  1135. ***************
  1136. *** 1,4 ****
  1137. ! ;# $Id: matching.pl,v 2.9.1.2 92/12/01 09:25:48 ram Exp $
  1138.   ;#
  1139.   ;#  Copyright (c) 1992, Raphael Manfredi
  1140.   ;#
  1141. --- 1,4 ----
  1142. ! ;# $Id: matching.pl,v 2.9.1.3 93/01/12 13:34:10 ram Exp $
  1143.   ;#
  1144.   ;#  Copyright (c) 1992, Raphael Manfredi
  1145.   ;#
  1146. ***************
  1147. *** 6,11 ****
  1148. --- 6,14 ----
  1149.   ;#  Licence as specified in the README file that comes with dist.
  1150.   ;#
  1151.   ;# $Log:    matching.pl,v $
  1152. + ;# Revision 2.9.1.3  93/01/12  13:34:10  ram
  1153. + ;# patch15: typo fix
  1154. + ;# 
  1155.   ;# Revision 2.9.1.2  92/12/01  09:25:48  ram
  1156.   ;# patch13: new perl_pattern function to transform shell-style patterns
  1157.   ;# patch13: file inclusion now handled by include_file
  1158. ***************
  1159. *** 115,121 ****
  1160.           # one of them matches, we stop and return true. A selector may contain
  1161.           # metacharacters, in which case a regular pattern matching is attempted
  1162.           # on the true *header* fields (i.e. we skip the pseudo keys like Body,
  1163. !         # Head, etc..). For instance, Return* would attempt a match on the
  1164.           # field Return-Receipt-To:, if present. The special macro %& is set
  1165.           # to the list of all the fields on which the match succeeded
  1166.           # (alphabetically sorted).
  1167. --- 118,124 ----
  1168.           # one of them matches, we stop and return true. A selector may contain
  1169.           # metacharacters, in which case a regular pattern matching is attempted
  1170.           # on the true *header* fields (i.e. we skip the pseudo keys like Body,
  1171. !         # Head, etc..). For instance, Return.* would attempt a match on the
  1172.           # field Return-Receipt-To:, if present. The special macro %& is set
  1173.           # to the list of all the fields on which the match succeeded
  1174.           # (alphabetically sorted).
  1175.  
  1176. Index: agent/pl/emergency.pl
  1177. Prereq: 2.9.1.1
  1178. *** agent/pl/emergency.pl.old    Tue Jan 12 13:41:23 1993
  1179. --- agent/pl/emergency.pl    Tue Jan 12 13:41:23 1993
  1180. ***************
  1181. *** 1,4 ****
  1182. ! ;# $Id: emergency.pl,v 2.9.1.1 92/08/12 21:33:04 ram Exp $
  1183.   ;#
  1184.   ;#  Copyright (c) 1992, Raphael Manfredi
  1185.   ;#
  1186. --- 1,4 ----
  1187. ! ;# $Id: emergency.pl,v 2.9.1.2 93/01/12 12:13:41 ram Exp $
  1188.   ;#
  1189.   ;#  Copyright (c) 1992, Raphael Manfredi
  1190.   ;#
  1191. ***************
  1192. *** 6,11 ****
  1193. --- 6,14 ----
  1194.   ;#  Licence as specified in the README file that comes with dist.
  1195.   ;#
  1196.   ;# $Log:    emergency.pl,v $
  1197. + ;# Revision 2.9.1.2  93/01/12  12:13:41  ram
  1198. + ;# patch15: now checks for error on file closing (buffer flushing)
  1199. + ;# 
  1200.   ;# Revision 2.9.1.1  92/08/12  21:33:04  ram
  1201.   ;# patch6: do not read mail if stdin is connected to a tty
  1202.   ;# 
  1203. ***************
  1204. *** 95,101 ****
  1205.       if (open(MBOX, ">>$mbox")) {
  1206.           (print MBOX $Header{'All'}) && ($ok = 1);
  1207.           print MBOX "\n";                # allow parsing by other mail tools
  1208. !         close MBOX;
  1209.           if ($ok) {
  1210.               do add_log("DUMPED in $mbox") if $loglvl > 5;
  1211.               return 1;
  1212. --- 98,104 ----
  1213.       if (open(MBOX, ">>$mbox")) {
  1214.           (print MBOX $Header{'All'}) && ($ok = 1);
  1215.           print MBOX "\n";                # allow parsing by other mail tools
  1216. !         close(MBOX) || ($ok = 0);
  1217.           if ($ok) {
  1218.               do add_log("DUMPED in $mbox") if $loglvl > 5;
  1219.               return 1;
  1220. ***************
  1221. *** 126,132 ****
  1222.                   $printed = 1;
  1223.               }
  1224.           }
  1225. !         close WAITING;
  1226.           if ($printed) {
  1227.               if (!$ok) {
  1228.                   do add_log("ERROR could not update waiting file") if $loglvl;
  1229. --- 129,135 ----
  1230.                   $printed = 1;
  1231.               }
  1232.           }
  1233. !         close(WAITING) || ($ok = 0);
  1234.           if ($printed) {
  1235.               if (!$ok) {
  1236.                   do add_log("ERROR could not update waiting file") if $loglvl;
  1237.  
  1238. Index: agent/test/TEST
  1239. *** agent/test/TEST.old    Tue Jan 12 13:41:45 1993
  1240. --- agent/test/TEST    Tue Jan 12 13:41:46 1993
  1241. ***************
  1242. *** 26,31 ****
  1243. --- 26,32 ----
  1244.   -f "../$mailagent" && -x _ || die "No $mailagent.\n";
  1245.   -f "../mailhook" && -x _ || die "No mailhook.\n";
  1246.   -f '../filter/filter' && -x _ || die "No filter.\n";
  1247. + $> || die "Cannot run tests as super-user.\n";
  1248.   
  1249.   &load_ok;        # Don't rerun successful tests if up to date
  1250.   
  1251.  
  1252. *** End of Patch 15 ***
  1253.  
  1254. exit 0 # Just in case...
  1255.