home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume41 / mailagnt / part17 < prev    next >
Encoding:
Text File  |  1993-12-03  |  54.6 KB  |  1,645 lines

  1. Newsgroups: comp.sources.misc
  2. From: Raphael Manfredi <ram@acri.fr>
  3. Subject: v41i017:  mailagent - Flexible mail filtering and processing package, v3.0, Part17/26
  4. Message-ID: <1993Dec3.213310.22471@sparky.sterling.com>
  5. X-Md4-Signature: a52515bcb2908f86200f38505242c4da
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Advanced Computer Research Institute, Lyon, France.
  8. Date: Fri, 3 Dec 1993 21:33:10 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: Raphael Manfredi <ram@acri.fr>
  12. Posting-number: Volume 41, Issue 17
  13. Archive-name: mailagent/part17
  14. Environment: UNIX, Perl
  15. Supersedes: mailagent: Volume 33, Issue 93-109
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then feed it
  19. # into a shell via "sh file" or similar.  To overwrite existing files,
  20. # type "sh file -c".
  21. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  22. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  23. # Contents:  Credits agent/files/agenthelp agent/filter/environ.c
  24. #   agent/pl/header.pl agent/pl/lexical.pl agent/pl/newcmd.pl
  25. #   agent/test/Makefile.SH agent/test/actions
  26. # Wrapped by ram@soft208 on Mon Nov 29 16:49:57 1993
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. echo If this archive is complete, you will see the following message:
  29. echo '          "shar: End of archive 17 (of 26)."'
  30. if test -f 'Credits' -a "${1}" != "-c" ; then 
  31.   echo shar: Will not clobber existing file \"'Credits'\"
  32. else
  33.   echo shar: Extracting \"'Credits'\" \(6672 characters\)
  34.   sed "s/^X//" >'Credits' <<'END_OF_FILE'
  35. XINTRO
  36. X
  37. XThis version of mailagent has been written by Raphael Manfredi based on
  38. Xprevious work from Larry Wall, published in dist-2.0.
  39. X
  40. XHISTORY
  41. X
  42. XThe mailagent history is somewhat linked to the dist package history. When
  43. XI discovered dist-2.0 in August 1990 and began extending metaconfig and the
  44. Xpat tools, I also began extending Larry Wall's mailagent, which was included
  45. Xin dist-2.0 along with the mailpatch command.
  46. X
  47. XI started adding mailhelp, maildist and maillist because I needed them, and
  48. Xthen left France in March 1991 to go write an Eiffel compiler until March 1993.
  49. XSince I was getting a lot of mail at ISE, I felt the need for a real mail
  50. Xfilter and not just an @SH command processor. Moreover, I was getting tired of
  51. Xhaving "Command mails" showing in my mailbox. The only mail filter I knew at
  52. Xthat time was ELM's, and it was not meeting my requirements.
  53. X
  54. XTherefore, I started extending mailagent late 1991 up to the point where it
  55. Xbecame a real and flexible mail filter (according to my standards anyway)
  56. Xand since I thought others would like to play with it too, I released it
  57. Xas a beta 3.0 (known as version 2.9) on comp.sources.misc in July 1992 after
  58. X7 months of hard work.
  59. X
  60. XI got a lot of positive feedback on this program, and therefore I continued
  61. Xto extend it. I also began to see some limitations in the 2.9 version and
  62. Xstarted adding new features (like perl escapes, user-defined commands,
  63. Xetc...). I originally intended to make version 3.0 part of dist-3.0, but
  64. Xmailagent was starting to get a life of its own and not really a part of dist.
  65. XHowever, @SH commands like ``package'' are meant to be used in conjunction
  66. Xwith metaconfig, one of dist's components.
  67. X
  68. XIn March 1993, I left ISE and returned to France to join A.C.R.I.. Although
  69. Xmailagent-3.0 was virtually finished by that time, I needed to release dist-3.0
  70. Xfirst due to some intensive user-pressure ;-). It's only late October 1993 that
  71. XI found the time to finalize the manual page and add all the small features
  72. Xstacked in my TODO list...
  73. X
  74. XCREDITS
  75. X
  76. XMy first thanks will go to Larry Wall <lwall@netlabs.com> who wrote perl
  77. Xin the first place. I would never have dreamed of implementing mailagent
  78. Xfrom scratch if perl had not been invented.
  79. X
  80. XThen I'd like to thank all the early mailagent users for their useful input,
  81. Xsuggestions, encouragement, contributions, constructive criticism or bug
  82. Xreports, and especially (by alphabetical order on the first name):
  83. X
  84. X    Akihiro Hashimoto <cik1a03@dominic.ipc.chiba-u.ac.jp>
  85. X    Andrew Hardie <ash@cellar.demon.co.uk>
  86. X    Andy Glew <glew@ichips.intel.com>
  87. X    Arthur Siffert <siffert@spot.Colorado.EDU>
  88. X    Bertrand Meyer <bertrand@eiffel.com>
  89. X    Bill Campbell <billc@sierra.com>
  90. X    Bill Wohler <wohler@hw1175.sap-ag.de>
  91. X    Bjoern Stabell <bjoerns@stud.cs.uit.no>
  92. X    Brent Chapman <brent@GreatCircle.COM>
  93. X    CJ Canon <Clement.J.Canon@arp.anu.edu.au>
  94. X    Charles A. Lind <lind@eng.umd.edu>
  95. X    Chris Lewis <clewis@ferret.ocunix.on.ca>
  96. X    Christian Bertin <bertin@acri.fr>
  97. X    Christoph von Stuckrad <stucki@math.fu-berlin.de>
  98. X    Christopher Davis <ckd@eff.org>
  99. X    Dan O'Neill <dano@Cadence.COM>
  100. X    Dan Pierson <pierson@kukri.enet.dec.com>
  101. X    Darrell Schiebel <dschieb@muse.cv.nrao.edu>
  102. X    David Giddy <d.giddy@trl.oz.au>
  103. X    David Vincenzetti <vince@dsi.unimi.it>
  104. X    David W. Tamkin <dattier@ddsw1.mcs.com>
  105. X    Edward Feustel <efeustel@ida.org>
  106. X    Eric M. Carroll <eric@enfm.utcc.utoronto.ca>
  107. X    Erland Unruh <Erland.Unruh@malmo.trab.se>
  108. X    Forrest Aldrich <visgraph!forrie>
  109. X    Geoffrey Hart <ghart@ems.cdc.com>
  110. X    George Entenman <ge@mcnc.org>
  111. X    George Smith <gbs@stratus.swdc.stratus.com>
  112. X    George W. Wood <wood@ibmoto.com>
  113. X    Graham Stoney <greyham@research.canon.oz.au>
  114. X    Henry Kautz <kautz@research.att.com>
  115. X    J. Mark Noworolski <jmn@crown.Berkeley.EDU>
  116. X    JJ Lay <csjjlay@knuth.mtsu.edu>
  117. X    James Ault <aultj@rpi.edu>
  118. X    Jan Polcher <polcher@lmd.ens.fr>
  119. X    Jan-Piet Mens <jpm@Logix.DE>
  120. X    Janna Wemekamp <jw@cxcbr.cx.oz.au>
  121. X    Jean-Marc Calvez <calvez@acri.fr>
  122. X    Jean-Marc Eurin <eurin@acri.fr>
  123. X    Jim McCoy <mccoy@ccwf.cc.utexas.edu>
  124. X    John Plate <plate@infotek.dk>
  125. X    Jost Krieger <x920031@rubb.rz.ruhr-uni-bochum.de>
  126. X    Keith Pyle <keith@ibmoto.com>
  127. X    Keith Rose <rose@ultryx.com>
  128. X    Kent Landfield <kent@imd.sterling.com>
  129. X    Kresten Krab Thorup <krab@iesd.auc.dk>
  130. X    Larry W. Virden <lvirden@cas.org>
  131. X    Mark Allman <mallman@oucsace.cs.ohiou.edu>
  132. X    Mark Kristie <clueless@camelot.bradley.edu>
  133. X    Mats Lidell <Mats.Lidell@eua.ericsson.se>
  134. X    Matthew Simmons <zarthac@camelot.bradley.edu>
  135. X    Michael Elkins <michael@jarthur.Claremont.EDU>
  136. X    Michael Peppler <mpeppler@itf0.itf.ch>
  137. X    NW Thomson <nige@kauri.vuw.ac.nz>
  138. X    Nigel Metheringham <nigelm@ohm.york.ac.uk>
  139. X    Ofer Inbar <cos@chaos.cs.brandeis.edu>
  140. X    Pascal Poissonnier <ppoisson@acri.fr>
  141. X    Patrick Labbaye <labbaye@acri.fr >
  142. X    Paul Raines <raines@bohr.physics.upenn.edu>
  143. X    Peter J Diaz de Leon <peter@miller.cs.uwm.edu>
  144. X    Peter N. Wan <peter@cc.gatech.edu>
  145. X    Ralf E. Stranzenbach <ralf@reswi.en.open.de>
  146. X    Richard Schneider <rschneid@erc.epson.com>
  147. X    Robert Brown <robertb@Barra.COM>
  148. X    Scott A. McIntyre <scott@shrug.dur.ac.uk>
  149. X    Scott Blachowicz <scott@statsci.com>
  150. X    Scott Robinson <shr@ichips.intel.com>
  151. X    Scott Schwartz <schwartz@groucho.cs.psu.edu>
  152. X    Sean Casey <sean@ms.uky.edu>
  153. X    Shigeya Suzuki <shigeya@foretune.co.jp>
  154. X    Soumendra Daas <dos@remus.ee.byu.edu>
  155. X    Sterling IMD <root@imd.sterling.com>
  156. X    Steve Hill <steveh@okam.corp.sgi.com>
  157. X    Syd Weinstein <syd@dsinc.dsi.com>
  158. X    Terry Hull <terry@eece.ksu.edu>
  159. X    Thomas Richter <richter@chemie.fu-berlin.de>
  160. X    Thunder-Thumbs <siffert@rintintin.Colorado.EDU>
  161. X    Tom Christiansen <tchrist@wraeththu.cs.colorado.edu>
  162. X    Tom Limoncelli <tom_limoncelli@Warren.MENTORG.COM>
  163. X    Tom Zheng <tzheng@cs.purdue.edu>
  164. X    Valery Alexeev <alexeev@math.utah.edu>
  165. X    Wes Price <ww2@bullwinkle.ssc.gov>
  166. X    Wolfram Schmidt <wschmidt@iao.fhg.de>
  167. X    Yeung Chee Wai <cheewai@uxmail.ust.hk>
  168. X    alexeev@math.utah.edu
  169. X    drw@bourbaki.mit.edu
  170. X    jim@morse.ilo.dec.com
  171. X    rschneid@callisto.erc.epson.com
  172. X    rsmith@proteus.arc.nasa.gov
  173. X    s090@brems.ii.uib.no
  174. X    sondeen@ISI.EDU
  175. X    wschmidt@iao.fhg.de
  176. X
  177. XIf I forgot your name, please let me know, and accept my apologies -- not
  178. Xnecessarily in that order ;-).
  179. X
  180. XDeepest thanks go to:
  181. X
  182. X    Christian Bertin <bertin@acri.fr>
  183. X    Eric M. Carroll <eric@enfm.utcc.utoronto.ca>
  184. X    Graham Stoney <greyham@research.canon.oz.au>
  185. X    Jean-Marc Eurin <eurin@acri.fr>
  186. X
  187. Xfor their active contributions, with a special mention to Jean-Marc Eurin
  188. Xfor carefully reviewing the release, installing it and proofreading the (long)
  189. Xmanual page before my official posting to comp.sources.misc.
  190. X
  191. XFinally, thank you Shigeya Suzuki <shigeya@foretune.co.jp> for hosting the
  192. Xagent-users mailing list. Send mail to majordomo@foretune.co.jp to subscribe.
  193. X
  194. X    Raphael Manfredi <ram@acri.fr>
  195. X    Lyon, France, December 1993
  196. END_OF_FILE
  197.   if test 6672 -ne `wc -c <'Credits'`; then
  198.     echo shar: \"'Credits'\" unpacked with wrong size!
  199.   fi
  200.   # end of 'Credits'
  201. fi
  202. if test -f 'agent/files/agenthelp' -a "${1}" != "-c" ; then 
  203.   echo shar: Will not clobber existing file \"'agent/files/agenthelp'\"
  204. else
  205.   echo shar: Extracting \"'agent/files/agenthelp'\" \(6402 characters\)
  206.   sed "s/^X//" >'agent/files/agenthelp' <<'END_OF_FILE'
  207. XThe purpose of the mail agent is to enable me answer some of your
  208. Xrequests, even if it's early in the morning and I've just gone to bed ! :-)
  209. X
  210. XFor instance, you need Larry Wall's patch program or Rich Salz's cshar.
  211. XI have them and I use them in my own kits.  So you may ask me to send them
  212. Xto you.  Of course, you could send me a mail saying "Please, could you
  213. Xsend me the cshar distribution kit ?", but I certainly won't be able to do
  214. Xit at once , either because I am not there when the mail arrives, or
  215. Xbecause someone else asked before you...
  216. X
  217. XWith the mail agent, there are no problems. You simply (!) send me a mail
  218. Xof the following form:
  219. X
  220. X    Subject: Command
  221. X    @SH maildist =DEST= cshar 3.0
  222. X
  223. Xand you will get version 3.0 of cshar.
  224. X
  225. X
  226. XHere are the possible commands:
  227. X
  228. X    - mailhelp PATH
  229. X        # sends some help
  230. X
  231. X    - maillist PATH
  232. X        # sends a list of what is available
  233. X
  234. X    - mailpatch PATH SYSTEM VERSION LIST
  235. X        # sends patches for a system
  236. X
  237. X    - maildist PATH SYSTEM VERSION
  238. X        # sends a whole distribution kit (latest patchlevel)
  239. X
  240. Xwhere PATH is a return path FROM ME TO YOU either in Internet notation
  241. Xor in bang notation from some well-known host.  As far as you are
  242. Xconcerned, it appears to be =DEST=.
  243. X
  244. XPATH may be omitted for mailhelp and maillist, in which case the return
  245. Xaddress found in the mail header will be used.
  246. X
  247. XSYSTEM is the system's name and VERSION is the version number.  For
  248. Xsystems that are not maintained, the version number has no sense and
  249. Xthus may be omitted (for maildist). A '-' stands for the latest version.
  250. X
  251. XThe LIST for mailpatch is the number of one or more patches you need,
  252. Xseparated by spaces, commas, and/or hyphens. For instance:
  253. X
  254. X    2,3 4-7,10-
  255. X
  256. Xasks for patches 2 and 3, then 4 to 7, and from 10 to the end, while
  257. X
  258. X    -5 10 11
  259. X
  260. Xrequests patches up to 5, then 10 and 11.
  261. X
  262. X
  263. XCommands must be preceded by the token "@SH" at the beginning of a line.
  264. XDo not put spaces/tabs in front of "@SH".  In the mail examples I give,
  265. XI do put one, but it is only for clarity purpose.
  266. X
  267. XIn the same way, the line "Subject: Command" must be left-justified.
  268. XNote that the subject of the mail does not need to be "Command", as long
  269. Xas you put the "Subject: Command" line in the body of your message,
  270. Xbefore your commands. You may use either "Command" or "command".
  271. X
  272. XOnce the "Subject: Command" line appears in your mail, either in the
  273. Xheader or in the body, you may put as many commands as necessary.
  274. XFor example:
  275. X
  276. X    Subject: Command
  277. X
  278. X    @SH maillist =DEST=
  279. X    @SH maildist =DEST= cshar 3.0
  280. X
  281. X
  282. XIf you are in doubt of what is the return path, you may put "PATH" or a
  283. Xsingle '-' instead of your address, and the mail agent will replace it
  284. Xwith the return path it finds in the mail header.  In case you do not
  285. Xtrust your mail headers, you may force the return path with the "@PATH"
  286. Xcommand.  The mail agent reads the whole message before actually
  287. Xprocessing it, so the location of this command does not really matters.
  288. XHere is an example:
  289. X
  290. X    Subject: Command
  291. X
  292. X    @SH mailhelp
  293. X    @SH mailpatch - kit 2.0 4,5
  294. X    @PATH =DEST=
  295. X
  296. X
  297. XWhen you ask for files to be sent, the mail agent makes shell archives or
  298. Xkit archives, depending on the amount of bytes that are to be returned.
  299. XIf it exceeds an arbitrary-fixed limit of =MAXSIZE= bytes, files are sent
  300. Xas kit archives.  Otherwise, they will be sent as shell archives provided
  301. Xthat no file is greater than the maximum allowed for a single shell
  302. Xarchive.  This is called the "auto" packing mode.
  303. X
  304. XThe "@PACK" command forces the distribution mode, which is "auto" by
  305. Xdefault. The specified packing mode is used, until another "@PACK"
  306. Xcommand is found. Valid parameters are "auto", "kit" and "shar".
  307. XNote that forcing mode to "shar" may well result in a failure if one
  308. Xof the files to be sent is bigger than the maximum size allowed for a
  309. Xshell-archive (around 50000 bytes). However, the mail agent does its
  310. Xbest: it will split large files and uuencode non-ASCII ones.
  311. X
  312. XWhen you use maildist, please do not request for "shar" mode, as "kit" will
  313. Xbe more efficient and safer. Note that when the packing mode is "auto" and
  314. Xthe mailagent has to kit the files, a minikit is included. Hence you may
  315. Xunkit the distribution even if you do not have kit. But it will always be
  316. Xsimpler with kit anyway.
  317. X
  318. X"Kit" is a binary tar-mailer that you must own in order to unkit
  319. Xthe kit archives which do not include a 'minikit'. If you do not have it,
  320. Xsend me the following mail:
  321. X
  322. X    Subject: Command
  323. X    @SH maildist =DEST= kit -
  324. X
  325. Xand you will get the latest release of "kit".
  326. X
  327. XHere is another example that uses the "@PACK" request (the following
  328. Xpackage names, revision numbers and patchlevels are here for the purpose
  329. Xof demonstration only. Reality may -- and often will -- be completely
  330. Xdifferent):
  331. X
  332. X    Subject: Command
  333. X
  334. X    -- Set the return path, so that we can use '-' without fear.
  335. X    @PATH =DEST=
  336. X    -- Request patches for kit 2.0, sent in "auto" packing mode.
  337. X    -- Note that the '-' actually stands for the return path.
  338. X    -- We could also have said:
  339. X    --     @SH mailpatch =DEST= kit 2.0 3-
  340. X    -- but as long as we have more than one command in the file,
  341. X    -- it would be cumbersome to repeat the address each time.
  342. X    @SH mailpatch - kit 2.0 3-
  343. X    -- Force packing mode to "shar", as we don't want to kit 'kit'.
  344. X    -- We don't know what the latest version is, so we put a '-'.
  345. X    -- Maildist will send the version at its highest patchlevel.
  346. X    @PACK shar
  347. X    @SH maildist - kit -
  348. X    -- Kit is more reliable and will greatly reduce the amount of
  349. X    -- transmitted data (typical gain is 50% for sources).
  350. X    @PACK kit
  351. X    -- We want version 2.0 for dist and nothing else.
  352. X    @SH maildist - dist 2.0
  353. X    -- Request all patches for the latest version of matrix
  354. X    @SH mailpatch - matrix - 1-
  355. X
  356. X
  357. XA nice thing with the mail agent is that you can ask for a receipt, in
  358. Xorder to be sure that I received your mail.  You may do so by placing
  359. Xthe "@RR" command at the beginning of any line in the body of your
  360. Xmessage.  A receipt will then be sent to the return path extracted from
  361. Xthe header.  You may force the receipt to be sent to a given address by
  362. Xgiving it after the @RR token.  Saying "@RR PATH" or "@RR -" is possible
  363. Xbut not very different from a single "@RR" !!
  364. X
  365. XHere are valid requests:
  366. X
  367. X    @RR
  368. X    @RR =DEST=
  369. X    @RR login@cpu.domain.top
  370. X
  371. XNote that no "Subject: Command" line is necessary for that, so you may
  372. Xask for receipts in every mail.
  373. X
  374. X
  375. XIf this help file is not clear enough, or if you have suggestions/questions,
  376. Xfeel free to ask me.
  377. END_OF_FILE
  378.   if test 6402 -ne `wc -c <'agent/files/agenthelp'`; then
  379.     echo shar: \"'agent/files/agenthelp'\" unpacked with wrong size!
  380.   fi
  381.   # end of 'agent/files/agenthelp'
  382. fi
  383. if test -f 'agent/filter/environ.c' -a "${1}" != "-c" ; then 
  384.   echo shar: Will not clobber existing file \"'agent/filter/environ.c'\"
  385. else
  386.   echo shar: Extracting \"'agent/filter/environ.c'\" \(6445 characters\)
  387.   sed "s/^X//" >'agent/filter/environ.c' <<'END_OF_FILE'
  388. X/*
  389. X
  390. X ######  #    #  #    #     #    #####    ####   #    #           ####
  391. X #       ##   #  #    #     #    #    #  #    #  ##   #          #    #
  392. X #####   # #  #  #    #     #    #    #  #    #  # #  #          #
  393. X #       #  # #  #    #     #    #####   #    #  #  # #   ###    #
  394. X #       #   ##   #  #      #    #   #   #    #  #   ##   ###    #    #
  395. X ######  #    #    ##       #    #    #   ####   #    #   ###     ####
  396. X
  397. X    Environment setting.
  398. X*/
  399. X
  400. X/*
  401. X * $Id: environ.c,v 3.0 1993/11/29 13:48:07 ram Exp ram $
  402. X *
  403. X *  Copyright (c) 1990-1993, Raphael Manfredi
  404. X *  
  405. X *  You may redistribute only under the terms of the Artistic License,
  406. X *  as specified in the README file that comes with the distribution.
  407. X *  You may reuse parts of this distribution only within the terms of
  408. X *  that same Artistic License; a copy of which may be found at the root
  409. X *  of the source tree for mailagent 3.0.
  410. X *
  411. X * $Log: environ.c,v $
  412. X * Revision 3.0  1993/11/29  13:48:07  ram
  413. X * Baseline for mailagent 3.0 netwide release.
  414. X *
  415. X */
  416. X
  417. X#include "config.h"
  418. X#include "portable.h"
  419. X#include "hash.h"
  420. X#include <stdio.h>
  421. X
  422. X#ifdef I_STRING
  423. X#include <string.h>
  424. X#else
  425. X#include <strings.h>
  426. X#endif
  427. X#include "confmagic.h"
  428. X
  429. X#define ENV_VARS    200                /* An average number of environment vars */
  430. X#define MAX_STRING    4096            /* Maximum size for an environment value */
  431. X
  432. X/* The environment is stored as an associative array: the key is the variable's
  433. X * name, and we store the value as the associated value, of course. This is
  434. X * not suitable for direct passing to a child, but it eases the environment
  435. X * modifications.
  436. X */
  437. Xprivate struct htable henv;            /* The associative array for env */
  438. X
  439. Xextern char *malloc();                /* Memory allocation */
  440. Xextern char *strsave();                /* String saving */
  441. X
  442. Xpublic void print_env(fd, envp)
  443. XFILE *fd;
  444. Xchar **envp;
  445. X{
  446. X    /* Print the environment held in 'envp' on file 'fd'. This is mainly
  447. X     * intended for debug purposes.
  448. X     */
  449. X
  450. X    while (*envp)
  451. X        fprintf(fd, "%s\n", *envp++);
  452. X}
  453. X
  454. Xpublic int init_env(envp)
  455. Xchar **envp;
  456. X{
  457. X    /* Initializes the associative array with the current environment. Returns
  458. X     * 0 if ok, -1 if failed due to a lack of memory.
  459. X     */
  460. X
  461. X    char env_line[MAX_STRING + 1];    /* The environment line */
  462. X    char *ptr;                        /* Pointer inside env_line */
  463. X    char *env;                        /* The current environment line */
  464. X
  465. X    if (-1 == ht_create(&henv, ENV_VARS))
  466. X        return -1;                    /* Cannot create H table */
  467. X
  468. X    while (env = *envp++) {
  469. X        strncpy(env_line, env, MAX_STRING);
  470. X        ptr = index(env_line, '=');
  471. X        if (ptr == (char *) 0) {
  472. X            add_log(6, "WARNING bad environment line");
  473. X            continue;
  474. X        }
  475. X        *ptr = '\0';                /* Before '=' lies the key */
  476. X        if ((char *) 0 == ht_put(&henv, env_line, ptr + 1)) {
  477. X            add_log(4, "ERROR cannot record environment any more");
  478. X            return -1;
  479. X        }
  480. X    }
  481. X
  482. X    return 0;    /* Ok */
  483. X}
  484. X
  485. Xpublic int append_env(key, value)
  486. Xchar *key;
  487. Xchar *value;
  488. X{
  489. X    /* Appends 'value' at the end of the environment variable 'key', if it
  490. X     * already exits, otherwise create it with that value.
  491. X     * Returns 0 for success, -1 for failure.
  492. X     */
  493. X    
  494. X    char env_line[MAX_STRING + 1];    /* Then environment line */
  495. X    char *cval;                        /* Current value */
  496. X
  497. X    cval = ht_value(&henv, key);
  498. X    if (cval == (char *) 0) {
  499. X        if ((char *) 0 == ht_put(&henv, key, value)) {
  500. X            add_log(1, "ERROR cannot insert environment variable '%s'", key);
  501. X            return -1;                /* Insertion failed */
  502. X        }
  503. X        return 0;                    /* Insertion ok */
  504. X    }
  505. X
  506. X    strncpy(env_line, cval, MAX_STRING);
  507. X    if (strlen(env_line) + strlen(value) > MAX_STRING) {
  508. X        add_log(1, "ERROR cannot append to environment variable '%s'", key);
  509. X        return -1;
  510. X    }
  511. X    strcat(env_line, value);
  512. X    if ((char *) 0 == ht_force(&henv, key, env_line)) {
  513. X        add_log(1, "ERROR cannot update environment variable '%s'", key);
  514. X        return -1;
  515. X    }
  516. X
  517. X    return 0;    /* Ok */
  518. X}
  519. X
  520. Xpublic int prepend_env(key, value)
  521. Xchar *key;
  522. Xchar *value;
  523. X{
  524. X    /* Prepends 'value' at the head of the environment variable 'key', if it
  525. X     * already exits, otherwise create it with that value.
  526. X     * Returns 0 for success, -1 for failure.
  527. X     */
  528. X    
  529. X    char env_line[MAX_STRING + 1];    /* Then environment line */
  530. X    char *cval;                        /* Current value */
  531. X
  532. X    cval = ht_value(&henv, key);
  533. X    if (cval == (char *) 0) {
  534. X        if ((char *) 0 == ht_put(&henv, key, value)) {
  535. X            add_log(1, "ERROR cannot insert environment variable '%s'", key);
  536. X            return -1;                /* Insertion failed */
  537. X        }
  538. X        return 0;                    /* Insertion ok */
  539. X    }
  540. X
  541. X    strncpy(env_line, value, MAX_STRING);
  542. X    if (strlen(env_line) + strlen(cval) > MAX_STRING) {
  543. X        add_log(1, "ERROR cannot prepend to environment variable '%s'", key);
  544. X        return -1;
  545. X    }
  546. X    strcat(env_line, cval);
  547. X    if ((char *) 0 == ht_force(&henv, key, env_line)) {
  548. X        add_log(1, "ERROR cannot update environment variable '%s'", key);
  549. X        return -1;
  550. X    }
  551. X
  552. X    return 0;    /* Ok */
  553. X}
  554. X
  555. Xpublic int set_env(key, value)
  556. Xchar *key;
  557. Xchar *value;
  558. X{
  559. X    /* Set environment value 'key' and return 0 for success, -1 for failure. */
  560. X
  561. X    char env_line[MAX_STRING + 1];    /* Then environment line */
  562. X    char *cval;                        /* Current value */
  563. X
  564. X    cval = ht_value(&henv, key);
  565. X    if (cval == (char *) 0) {
  566. X        if ((char *) 0 == ht_put(&henv, key, value)) {
  567. X            add_log(1, "ERROR cannot insert environment variable '%s'", key);
  568. X            return -1;                /* Insertion failed */
  569. X        }
  570. X        return 0;                    /* Insertion ok */
  571. X    }
  572. X
  573. X    if ((char *) 0 == ht_force(&henv, key, value)) {
  574. X        add_log(1, "ERROR cannot update environment variable '%s'", key);
  575. X        return -1;
  576. X    }
  577. X
  578. X    return 0;    /* Ok */
  579. X}
  580. X
  581. Xpublic char **make_env()
  582. X{
  583. X    /* Create the environment pointer suitable for the execle() system call.
  584. X     * Return a null pointer if there is not enough memory to create the
  585. X     * environment.
  586. X     */
  587. X
  588. X    char env_line[MAX_STRING + 1];    /* The environment line */
  589. X    char **envp;                    /* The environment pointer returned */
  590. X    char **ptr;                        /* Pointer in the environment */
  591. X    int nb_line;                    /* Number of lines */
  592. X
  593. X    nb_line = ht_count(&henv) + 1;    /* Envp ends with a null pointer */
  594. X    if (nb_line == 0) {
  595. X        add_log(6, "NOTICE environment is empty");
  596. X        return (char **) 0;
  597. X    }
  598. X    envp = (char **) malloc(nb_line * sizeof(char *));
  599. X    if (envp == (char **) 0)
  600. X        fatal("out of memory");
  601. X    
  602. X    if (-1 == ht_start(&henv))
  603. X        fatal("environment H table botched");
  604. X    
  605. X    ptr = envp;
  606. X    for (ptr = envp; --nb_line > 0; (void) ht_next(&henv), ptr++) {
  607. X        sprintf(env_line, "%s=%s", ht_ckey(&henv), ht_cvalue(&henv));
  608. X        *ptr = strsave(env_line);
  609. X        if (*ptr == (char *) 0)
  610. X            fatal("no more memory for environment");
  611. X    }
  612. X
  613. X    *ptr = (char *) 0;                /* Environment is NULL terminated */
  614. X
  615. X    return envp;                    /* Pointer to new environment */
  616. X}
  617. X
  618. END_OF_FILE
  619.   if test 6445 -ne `wc -c <'agent/filter/environ.c'`; then
  620.     echo shar: \"'agent/filter/environ.c'\" unpacked with wrong size!
  621.   fi
  622.   # end of 'agent/filter/environ.c'
  623. fi
  624. if test -f 'agent/pl/header.pl' -a "${1}" != "-c" ; then 
  625.   echo shar: Will not clobber existing file \"'agent/pl/header.pl'\"
  626. else
  627.   echo shar: Extracting \"'agent/pl/header.pl'\" \(6455 characters\)
  628.   sed "s/^X//" >'agent/pl/header.pl' <<'END_OF_FILE'
  629. X;# $Id: header.pl,v 3.0 1993/11/29 13:48:49 ram Exp ram $
  630. X;#
  631. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  632. X;#  
  633. X;#  You may redistribute only under the terms of the Artistic License,
  634. X;#  as specified in the README file that comes with the distribution.
  635. X;#  You may reuse parts of this distribution only within the terms of
  636. X;#  that same Artistic License; a copy of which may be found at the root
  637. X;#  of the source tree for mailagent 3.0.
  638. X;#
  639. X;# $Log: header.pl,v $
  640. X;# Revision 3.0  1993/11/29  13:48:49  ram
  641. X;# Baseline for mailagent 3.0 netwide release.
  642. X;#
  643. X;# 
  644. Xpackage header;
  645. X
  646. X# This package implements a header checker. To initialize it, call 'reset'.
  647. X# Then, call 'valid' with a header line and the function returns 0 if the
  648. X# line is not part of a header (which means all the lines seen since 'reset'
  649. X# are not part of a mail header). If the line may still be part of a header,
  650. X# returns 1. Finally, -1 is returned at the end of the header.
  651. X
  652. Xsub init {
  653. X    # Main header fields which should be looked at when parsing a mail header
  654. X    %Mailheader = (
  655. X        'From', 1,
  656. X        'To', 1,
  657. X        'Subject', 1,
  658. X        'Date', 1,
  659. X    );
  660. X}
  661. X
  662. X# Reset header checking status
  663. Xsub reset {
  664. X    &init unless $init_done++;        # Initialize private data
  665. X    $last_was_header = 0;            # Previous line was not a header
  666. X    $maybe = 0;                        # Do we have a valid part of header?
  667. X    $line = 0;                        # Count number of lines in header
  668. X}
  669. X
  670. X# Is the current line still part of a valid header ?
  671. Xsub valid {
  672. X    local($_) = @_;
  673. X    return 1 if $last_was_header && /^\s/;    # Continuation line
  674. X    return -1 if /^$/;                        # End of header
  675. X    $last_was_header = /^([\w\-]+):/ ? 1 : 0;
  676. X    # Activate $maybe when essential parts of a valid mail header are found
  677. X    # Any client can check 'maybe' to see if what has been parsed so far would
  678. X    # be a valid RFC-822 header, even though syntactically correct.
  679. X    $maybe |= $Mailheader{$1} if $last_was_header;
  680. X    $last_was_header = /^From\s+\S+/
  681. X        unless $last_was_header || $line;    # First line may be special
  682. X    ++$line;                                # One more line
  683. X    $last_was_header;                        # Are we still inside header?
  684. X}
  685. X
  686. X# Produce a warning header field about a specific item
  687. Xsub warning {
  688. X    local($field, $added) = @_;
  689. X    local($warning);
  690. X    local(@field) = split(' ', $field);
  691. X    $warning = 'X-Filter-Note: ';
  692. X    if ($added && @field == 1) {
  693. X        $warning .= "Header $field added at ";
  694. X    } elsif ($added && @field > 1) {
  695. X        $field = join(', ', @field);
  696. X        $field =~ s/^(.*), (.*)/$1 and $2/;
  697. X        $warning .= "Headers $field added at ";
  698. X    } else {
  699. X        $warning .= "Parsing error in original previous line at ";
  700. X    }
  701. X    $warning .= &main'domain_addr;
  702. X    $warning;
  703. X}
  704. X
  705. X# Make sure header contains vital fields. The header is held in an array, on
  706. X# a line basis with final new-line chopped. The array is modified in place,
  707. X# setting defaults from the %Header array (if defined, which is the case for
  708. X# digests mails) or using local defaults.
  709. Xsub clean {
  710. X    local(*array) = @_;                    # Array holding the header
  711. X    local($added) = '';                    # Added fields
  712. X
  713. X    $added .= &check(*array, 'From', $cf'user, 1);
  714. X    $added .= &check(*array, 'To', $cf'user, 1);
  715. X    $added .= &check(*array, 'Date', &fake_date, 0);
  716. X    $added .= &check(*array, 'Subject', '<none>', 1);
  717. X
  718. X    &push(*array, &warning($added, 1)) if $added ne '';
  719. X}
  720. X
  721. X# Check presence of specific field and use value of %Header as a default if
  722. X# available and if '$use_header' is set, otherwise use the provided value.
  723. X# Return added field or a null string if nothing is done.
  724. Xsub check {
  725. X    local(*array, $field, $default, $use_header) = @_;
  726. X    local($faked);                        # Faked value to be used
  727. X    if ($use_header) {
  728. X        $faked = (defined $'Header{$field}) ? $'Header{$field} : $default;
  729. X    } else {
  730. X        $faked = $default;
  731. X    }
  732. X
  733. X    # Try to locate field in header
  734. X    local($_);
  735. X    foreach (@array) {
  736. X        return '' if /^$field:/;
  737. X    }
  738. X
  739. X    &push(*array, "$field: $faked");
  740. X    $field . ' ';
  741. X}
  742. X
  743. X# Push header line at the end of the array, without assuming any final EOH line
  744. Xsub push {
  745. X    local(*array, $line) = @_;
  746. X    local($last) = pop(@array);
  747. X    push(@array, $last) if $last ne '';    # There was no EOH
  748. X    push(@array, $line);                # Insert header line
  749. X    push(@array, '') if $last eq '';    # Restore EOH
  750. X}
  751. X
  752. X# Compute a valid date field suitable for mail header
  753. Xsub fake_date {
  754. X    require 'ctime.pl';
  755. X    local($date) = &'ctime(time);
  756. X    # Traditionally, MTAs add a ',' right after week day
  757. X    $date =~ s/^(\w+)(\s)/$1,$2/;
  758. X    chop($date);                    # Ctime adds final new-line
  759. X    $date;
  760. X}
  761. X
  762. X# Normalizes header: every first letter is uppercase, the remaining of the
  763. X# word being lowercased, as in This-Is-A-Normalized-Header. Note that RFC-822
  764. X# does not impose such a formatting.
  765. Xsub normalize {
  766. X    local($field_name) = @_;            # Header to be normalized
  767. X    $field_name =~ s/(\w+)/\u\L$1/g;
  768. X    $field_name;                        # Return header name with proper case
  769. X}
  770. X
  771. X# Format header field to fit into 78 columns, each continuation line being
  772. X# indented by 8 chars. Returns the new formatted header string.
  773. Xsub format {
  774. X    local($field) = @_;            # Field to be formatted
  775. X    local($tmp);                # Buffer for temporary formatting
  776. X    local($new) = '';            # Constructed formatted header
  777. X    local($kept);                # Length of current line
  778. X    local($len) = 78;            # Amount of characters kept
  779. X    local($cont) = ' ' x 8;        # Continuation lines starts with 8 spaces
  780. X    # Format header field, separating lines on ',' or space.
  781. X    while (length($field) > $len) {
  782. X        $tmp = substr($field, 0, $len);        # Keep first $len chars
  783. X        $tmp =~ s/^(.*)([,\s]).*/$1$2/;        # Cut at last space or ,
  784. X        $kept = length($tmp);                # Amount of chars we kept
  785. X        $tmp =~ s/\s*$//;                    # Remove trailing spaces
  786. X        $tmp =~ s/^\s*//;                    # Remove leading spaces
  787. X        $new .= $cont if $new;                # Continuation starts with 8 spaces
  788. X        $len = 70;                            # Account continuation for next line
  789. X        $new .= "$tmp\n";
  790. X        $field = substr($field, $kept, 9999);
  791. X    }
  792. X    $new .= $cont if $new;                    # Add 8 chars if continuation
  793. X    $new .= $field;                            # Remaining information on one line
  794. X}
  795. X
  796. X# Scan the head of a file and try to determine whether there is a mail
  797. X# header at the beginning or not. Return true if a header was found.
  798. Xsub main'header_found {
  799. X    local($file) = @_;
  800. X    local($correct) = 1;                # Were all the lines from top correct ?
  801. X    local($_);
  802. X    open(FILE, $file) || return 0;        # Don't care to report error
  803. X    &reset;                                # Initialize header checker
  804. X    while (<FILE>) {                    # While still in a possible header
  805. X        last if /^$/;                    # Exit if end of header reached
  806. X        $correct = &valid($_);            # Check line validity
  807. X        last unless $correct;            # No, not a valid header
  808. X    }
  809. X    close FILE;
  810. X    $correct;
  811. X}
  812. X
  813. Xpackage main;
  814. X
  815. END_OF_FILE
  816.   if test 6455 -ne `wc -c <'agent/pl/header.pl'`; then
  817.     echo shar: \"'agent/pl/header.pl'\" unpacked with wrong size!
  818.   fi
  819.   # end of 'agent/pl/header.pl'
  820. fi
  821. if test -f 'agent/pl/lexical.pl' -a "${1}" != "-c" ; then 
  822.   echo shar: Will not clobber existing file \"'agent/pl/lexical.pl'\"
  823. else
  824.   echo shar: Extracting \"'agent/pl/lexical.pl'\" \(6447 characters\)
  825.   sed "s/^X//" >'agent/pl/lexical.pl' <<'END_OF_FILE'
  826. X;# $Id: lexical.pl,v 3.0 1993/11/29 13:48:55 ram Exp ram $
  827. X;#
  828. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  829. X;#  
  830. X;#  You may redistribute only under the terms of the Artistic License,
  831. X;#  as specified in the README file that comes with the distribution.
  832. X;#  You may reuse parts of this distribution only within the terms of
  833. X;#  that same Artistic License; a copy of which may be found at the root
  834. X;#  of the source tree for mailagent 3.0.
  835. X;#
  836. X;# $Log: lexical.pl,v $
  837. X;# Revision 3.0  1993/11/29  13:48:55  ram
  838. X;# Baseline for mailagent 3.0 netwide release.
  839. X;#
  840. X;#
  841. X#
  842. X# Lexical parsing of the rules
  843. X#
  844. X
  845. X# The following subroutine is called whenever a new rule input is needed.
  846. X# It returns that new line or a null string if end of file has been reached.
  847. Xsub read_filerule {
  848. X    <RULES>;                    # Read a new line from file
  849. X}
  850. X
  851. X# The following subroutine is called in place of read_rule when rules are
  852. X# coming from the command line via @Linerules.
  853. Xsub read_linerule {
  854. X    $.++;                        # One more line
  855. X    shift(@Linerules);            # Read a new line from array
  856. X}
  857. X
  858. X# Assemble a whole rule in one line and return it. The end of a line is
  859. X# marked by a ';' at the end of an input line.
  860. Xsub get_line {
  861. X    local($result) = "";        # what will be returned
  862. X    local($in_braces) = 0;        # are we inside braces ?
  863. X    for (;;) {
  864. X        $_ = &read_rule;        # new rule line (pseudo from compile_rules)
  865. X        last if $_ eq '';        # end of file reached
  866. X        s/\n$//;                # don't use chop in case we read from array
  867. X        next if /^\s*#/;        # skip comments
  868. X        next if /^\s*$/;        # skip empty lines
  869. X        s/\s\s+/ /;                # reduce white spaces
  870. X        $result .= $_;
  871. X        # Very simple braces handling
  872. X        /.*{/ && ($in_braces = 1);
  873. X        if ($in_braces) {
  874. X            /.*}/ && ($in_braces = 0);
  875. X        }
  876. X        last if !$in_braces && /;\s*$/;
  877. X    }
  878. X    $result;
  879. X}
  880. X
  881. X# Get optional mode (e.g. <TEST>) at the beginning of the line and return
  882. X# it, or ALL if none was present. A mode can be negated by pre-pending a '!'.
  883. Xsub get_mode {
  884. X    local(*line) = shift(@_);    # edited in place
  885. X    local($_) = $line;            # make a copy of original
  886. X    local($mode) = "ALL";        # default mode
  887. X    s/^\s*<([\s\w,!]+)>// && ($mode = $1);
  888. X    $mode =~ s/\s//g;            # no spaces in returned mode
  889. X    $line = $_;                    # eventually updates the line
  890. X    $mode;
  891. X}
  892. X
  893. X# A selector is either a script or a list of header fields ending with a ':'.
  894. Xsub get_selector {
  895. X    local(*line) = shift(@_);    # edited in place
  896. X    local($_) = $line;            # make a copy of original
  897. X    local($selector) = "";
  898. X    s/^\s*,//;                    # remove rule separator
  899. X    if (/^\s*\[\[/) {            # detected a script form
  900. X        $selector = 'script:';
  901. X    } else {
  902. X        s/^\s*([^\/,{\n]*(<[\d\s,-]+>)?\s*:)// && ($selector = $1);
  903. X    }
  904. X    $line = $_;                    # eventually updates the line
  905. X    $selector;
  906. X}
  907. X
  908. X# A pattern if either a single word (with no white space) or something
  909. X# starting with a / and ending with an un-escaped / followed by some optional
  910. X# modifiers.
  911. X# Patterns may be preceded by a single '!' to negate the matching value.
  912. Xsub get_pattern {
  913. X    local(*line) = shift(@_);        # edited in place
  914. X    local($_) = $line;                # make a copy of original
  915. X    local($pattern) = "";            # the recognized pattern
  916. X    local($buffer) = "";            # the buffer used for parsing
  917. X    local($not) = '';                # shall boolean value be negated?
  918. X    s|^\s*||;                        # remove leading spaces
  919. X    s/^!// && ($not = '!');            # A leading '!' inverts matching status
  920. X    if (s|^\[\[([^{]*)\]\]||) {        # pattern is a script
  921. X        $pattern = $1;                # get the whole script
  922. X    } elsif (s|^/||) {                # pattern starts with a /
  923. X        $pattern = "/";                # record the /
  924. X        while (s|([^/]*/)||) {        # while there is something before a /
  925. X            $buffer = $1;            # save what we've been reading
  926. X            $pattern .= $1;
  927. X            last unless $buffer =~ m|\\/$|;    # finished unless / is escaped
  928. X        }
  929. X        s/^(\w+)// && ($pattern .= $1);        # add optional modifiers
  930. X    } else {                                # pattern does not start with a /
  931. X        s/([^\s,;{]*)// && ($pattern = $1);    # grab all until next delimiter
  932. X    }
  933. X    $line = $_;                    # eventually updates the line
  934. X    $pattern =~ s/\s+$//;        # remove trailing spaces
  935. X    if ($not && !$pattern) {
  936. X        &add_log("ERROR discarding '!' not followed by pattern") if $loglvl;
  937. X    } else {
  938. X        $pattern = $not . $pattern;
  939. X    }
  940. X    $pattern;
  941. X}
  942. X
  943. X# Extract the action part from the line (by editing it in place) and return
  944. X# the first action encountered. Nesting of {...} blocks may occur.
  945. Xsub get_action {
  946. X    local(*line) = shift(@_);    # edited in place
  947. X    local($_) = $line;            # make a copy of original
  948. X    return '' unless s/^\s*\{/{/;
  949. X    local($action) = &action_parse(*_, 0);
  950. X    &add_log("ERROR no action, discarding '$_'") if $loglvl && $action eq '';
  951. X    $line = $_;                    # eventually update the line
  952. X    $action =~ s/^\{\s*//;        # remove leading and trailing braces
  953. X    $action =~ s/\s*\}$//;
  954. X    $action;                    # return new action block
  955. X}
  956. X
  957. X# Recursively parse the action string and return the parsed portion of the text
  958. X# with proper nesting wherever necessary. The string given as parameter is
  959. X# edited in place and the remaining is the unparsed part.
  960. Xsub action_parse {
  961. X    local(*_) = shift(@_);        # edited in place
  962. X    local($level) = shift(@_);    # recursion level
  963. X    local($parsed) = '';        # the part we parsed so far
  964. X    local($block);                # block recognized
  965. X    local($follow);                # recursion string returned
  966. X    for (;;) {
  967. X        # Go to first un-escaped '{', if possible and save leading string
  968. X        # up-to first '{'. Note that any '}' immediately stops scanning.
  969. X        s/^(([^\\{}]|\\.)*{)// && ($parsed .= $1);
  970. X        # Go to first un-escaped '}', with any '{' stopping scan.
  971. X        $block = '';
  972. X        s/^(([^\\{}]|\\.)*\})// && ($block = $1);
  973. X        $parsed .= $block;        # block may be empty, or has trailing '}'
  974. X        if ($parsed =~ s/\{$//) {    # recursion if '{' found
  975. X            $follow = &action_parse(*_, $level + 1);
  976. X            # If a null string is returned, then no matching '}' was found
  977. X            &add_log("WARNING no closing brace (added for you)")
  978. X                if $follow eq '' && $loglvl > 5;
  979. X            $parsed .= '{' . $follow . '}';
  980. X        } elsif (s/^\}//) {            # reached end of a block
  981. X            &add_log("WARNING extra closing brace ignored")
  982. X                if $level == 0 && $loglvl > 5;
  983. X            return $parsed;
  984. X        } else {
  985. X            # Get the whole string until the next '}' and return. If a '{'
  986. X            # interposes, the first match will return an empty string. In that
  987. X            # case, we continue if we are not at level #0. Otherwise we got the
  988. X            # whole action and may return now.
  989. X            $block = '';
  990. X            s/^(([^\\{}]|\\.)*\})// && ($block = $1);
  991. X            if ($block eq '' && $level) {        # Advance until '{'
  992. X                s/^(([^\\}]|\\.)*\{)// && ($block = $1);
  993. X                $parsed .= $block;
  994. X                next;
  995. X            }
  996. X            $block =~ s/\}//;
  997. X            return $parsed . $block;
  998. X        }
  999. X    }
  1000. X}
  1001. X
  1002. END_OF_FILE
  1003.   if test 6447 -ne `wc -c <'agent/pl/lexical.pl'`; then
  1004.     echo shar: \"'agent/pl/lexical.pl'\" unpacked with wrong size!
  1005.   fi
  1006.   # end of 'agent/pl/lexical.pl'
  1007. fi
  1008. if test -f 'agent/pl/newcmd.pl' -a "${1}" != "-c" ; then 
  1009.   echo shar: Will not clobber existing file \"'agent/pl/newcmd.pl'\"
  1010. else
  1011.   echo shar: Extracting \"'agent/pl/newcmd.pl'\" \(6409 characters\)
  1012.   sed "s/^X//" >'agent/pl/newcmd.pl' <<'END_OF_FILE'
  1013. X;# $Id: newcmd.pl,v 3.0 1993/11/29 13:49:03 ram Exp ram $
  1014. X;#
  1015. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  1016. X;#  
  1017. X;#  You may redistribute only under the terms of the Artistic License,
  1018. X;#  as specified in the README file that comes with the distribution.
  1019. X;#  You may reuse parts of this distribution only within the terms of
  1020. X;#  that same Artistic License; a copy of which may be found at the root
  1021. X;#  of the source tree for mailagent 3.0.
  1022. X;#
  1023. X;# $Log: newcmd.pl,v $
  1024. X;# Revision 3.0  1993/11/29  13:49:03  ram
  1025. X;# Baseline for mailagent 3.0 netwide release.
  1026. X;#
  1027. X;# 
  1028. X;# This package handles the dynamic loading of a perl script in memory,
  1029. X;# providing a dynamic way of enhancing the command set of the mailagent.
  1030. X;#
  1031. X;# New commands are specified in the newcmd file specified in the config file.
  1032. X;# The syntax of this file is the following:
  1033. X;#
  1034. X;#   <cmd_name> <path> <function> [<status_flag> [<seen_flag>]]
  1035. X;#
  1036. X;# cmd_name: this is the command name, eg. RETURN_SENDER
  1037. X;# path: this is the path to the perl script implementing the command.
  1038. X;# function: the perl function within the script which implements the command
  1039. X;# status_flag: states whether the command modifies the execution status
  1040. X;# seen_flag: states whether the command is allowed in _SEEN_ mode
  1041. X;# 
  1042. X;# The last two booleans are optional, and may be specified as either 'yes'
  1043. X;# and 'no' or 'true' and 'false'. Their default value is respectively true
  1044. X;# and false.
  1045. X;#
  1046. X;# New commands are loaded as they are used and put in a special newcmd
  1047. X;# package, so that the names of the routines do not conflict with the
  1048. X;# mailagent's one. They are free to use whatever function the mailagent
  1049. X;# implements by prefixing the routine name with its package: normally, the
  1050. X;# execution of the command is done from within the newcmd package.
  1051. X;#
  1052. X;# Commands are given a single argument: the string forming the command name.
  1053. X;# Therefore, the command may implement the syntax it wishes. However, for
  1054. X;# the user convenience, the special array @newcmd'argv is preset with a
  1055. X;# shell-style parsed version. The mailagent also initializes the same
  1056. X;# special variables as the one set for PERL commands, only does it put them
  1057. X;# in the newcmd package instead of mailhook.
  1058. X;#
  1059. X;# Several data structures are maintained by this package:
  1060. X;#   %Usercmd, maps a command name to a file
  1061. X;#   %Loaded, records whether a file has been loaded or not
  1062. X;#   %Run, maps a command name to a perl function
  1063. X;#
  1064. X
  1065. Xpackage newcmd;
  1066. X
  1067. X#
  1068. X# User-defined commands
  1069. X#
  1070. X
  1071. X# Parse the newcmd file and record all new commands in the mailagent data
  1072. X# structures.
  1073. Xsub load {
  1074. X    return unless -s $cf'newcmd;    # Empty or non-existent file
  1075. X
  1076. X    # Security checks. We cannot extend the mailagent commands if the file
  1077. X    # describing those new commands is not owned by the user or ir world
  1078. X    # writable. Indeed, someone could redefine default commands like LEAVE
  1079. X    # and use that to break into the user account.
  1080. X    return unless &'file_secure($cf'newcmd, 'new command');
  1081. X
  1082. X    unless (open(NEWCMD, $cf'newcmd)) {
  1083. X        &'add_log("ERROR cannot open $cf'newcmd: $!") if $'loglvl;
  1084. X        &'add_log("WARNING new commands not loaded") if $'loglvl > 5;
  1085. X        return;
  1086. X    }
  1087. X
  1088. X    local($home) = $cf'home;
  1089. X    $home =~ s/(\W)/\\$1/g;            # Escape possible meta-characters like '+'
  1090. X
  1091. X    local($_);
  1092. X    local($cmd, $path, $function, $status, $seen);
  1093. X    while (<NEWCMD>) {
  1094. X        next if /^\s*#/;            # Skip comments
  1095. X        next if /^\s*$/;            # Skip blank lines
  1096. X        ($cmd, $path, $function, $status, $seen) = split(' ');
  1097. X        $cmd =~ tr/a-z/A-Z/;        # Cannonicalize to upper-case
  1098. X        $path =~ s/~/$cf'home/;        # Perform ~ substitution
  1099. X        unless (-e $path && -r _) {
  1100. X            $path =~ s/^$home/~/;
  1101. X            &'add_log("ERROR command '$cmd' bound to unreadable file $path")
  1102. X                if $'loglvl > 1;
  1103. X            next;                    # Skip invalid command
  1104. X        }
  1105. X        # Load command into data structures by setting internal tables
  1106. X        $'Filter{$cmd} = "newcmd'run";        # Main dispatcher for new commands
  1107. X        $Usercmd{$cmd} = $path;                # Record command path
  1108. X        $Loaded{$path} = 0;                    # File not loaded yet
  1109. X        $Run{$cmd} = $function;                # Perl function to call
  1110. X        $'Nostatus{$cmd} = 1 if $status =~ /^f|n/i;
  1111. X        $'Rfilter{$cmd} = 1 unless $seen =~ /^t|y/i;
  1112. X        &interface'add($cmd);                # Add interface for perl hooks
  1113. X
  1114. X        $path =~ s/^$home/~/;
  1115. X        &'add_log("new command $cmd in $path (&$function)")
  1116. X            if $'loglvl > 18;
  1117. X    }
  1118. X    close NEWCMD;
  1119. X}
  1120. X
  1121. X# This is the main dispatcher for user-defined command.
  1122. X# Our caller 'run_command' has set up some special variables, like $mfile
  1123. X# and $cmd_name, which are used here. Someday, I'll have to encapsulate that
  1124. X# in a better way--RAM.
  1125. Xsub run {
  1126. X    # Make global variables visible in this package. Variables which should
  1127. X    # not be changed are marked 'read only'.
  1128. X    local($cmd) = $'cmd;                    # Full command line (read only)
  1129. X    local($cmd_name) = $'cmd_name;            # Command name (read only)
  1130. X    local($mfile) = $'mfile;                # File name (read only)
  1131. X    local(*ever_saved) = *'ever_saved;        # Saving already occurred?
  1132. X    local(*cont) = *'cont;                    # Continuation status
  1133. X    local(*vacation) = *'vacation;            # Vacation message allowed?
  1134. X    local(*lastcmd) = *'lastcmd;            # Last failure status stored
  1135. X    local(*wmode) = *'wmode;                # Filter mode
  1136. X
  1137. X    &'add_log("user-defined command $cmd_name") if $'loglvl > 15;
  1138. X
  1139. X    # Let's see if we already have loaded the perl script which is responsible
  1140. X    # for implementing this command.
  1141. X    local($path) = $Usercmd{$cmd_name};
  1142. X    unless ($path) {
  1143. X        &'add_log("ERROR unknown user-defined command $cmd_name") if $'loglvl;
  1144. X        return 1;                    # Command failed (should not happen)
  1145. X    }
  1146. X    local($function) = $Run{$cmd_name};
  1147. X
  1148. X    unless (&dynload'load('newcmd', $path, $function)) {
  1149. X        &'add_log("ERROR cannot load code for user-defined $cmd_name")
  1150. X            if $'loglvl;
  1151. X        return 1;            # Command failed
  1152. X    }
  1153. X
  1154. X    # At this point, we know we have some code to call in order to run the
  1155. X    # user-defined command. Prepare the special array @ARGV and initialize
  1156. X    # the mailhook variable in the current package.
  1157. X    &hook'initvar('newcmd');        # Initialize convenience variables
  1158. X    local(@ARGV);                    # Argument vector for command
  1159. X    require 'shellwords.pl';
  1160. X    eval '@ARGV = &shellwords($cmd)';
  1161. X
  1162. X    # We don't need to protect the following execution within an eval, since
  1163. X    # we are currently inside one, via run_command.
  1164. X    local($failed) = &$function($cmd);        # Call user-defined function
  1165. X
  1166. X    # Log our action
  1167. X    local($msg) = $failed ? "and failed" : "successfully";
  1168. X    &'add_log("ran $cmd_name [$mfile] $msg") if $'loglvl > 6;
  1169. X
  1170. X    $failed;            # Propagate failure status
  1171. X}
  1172. X
  1173. Xpackage main;
  1174. X
  1175. END_OF_FILE
  1176.   if test 6409 -ne `wc -c <'agent/pl/newcmd.pl'`; then
  1177.     echo shar: \"'agent/pl/newcmd.pl'\" unpacked with wrong size!
  1178.   fi
  1179.   # end of 'agent/pl/newcmd.pl'
  1180. fi
  1181. if test -f 'agent/test/Makefile.SH' -a "${1}" != "-c" ; then 
  1182.   echo shar: Will not clobber existing file \"'agent/test/Makefile.SH'\"
  1183. else
  1184.   echo shar: Extracting \"'agent/test/Makefile.SH'\" \(3702 characters\)
  1185.   sed "s/^X//" >'agent/test/Makefile.SH' <<'END_OF_FILE'
  1186. X: Makefile.SH generated from Jmake.tmpl and Jmakefile [jmake 3.0 PL14]
  1187. X: $X-Id: Jmake.tmpl,v 3.0.1.1 1993/08/20 07:36:36 ram Exp ram $
  1188. X
  1189. Xcase $CONFIG in
  1190. X'')
  1191. X    if test -f config.sh; then TOP=.;
  1192. X    elif test -f ../config.sh; then TOP=..;
  1193. X    elif test -f ../../config.sh; then TOP=../..;
  1194. X    elif test -f ../../../config.sh; then TOP=../../..;
  1195. X    elif test -f ../../../../config.sh; then TOP=../../../..;
  1196. X    else
  1197. X        echo "Can't find config.sh."; exit 1
  1198. X    fi
  1199. X    . $TOP/config.sh
  1200. X    ;;
  1201. Xesac
  1202. Xcase "$0" in
  1203. X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
  1204. Xesac
  1205. XCURRENT=agent/test
  1206. XDIR=`echo $CURRENT/ | sed -e 's/\.\///g'`
  1207. Xecho "Extracting ${DIR}Makefile (with variable substitutions)"
  1208. XDATE=`date`
  1209. X$spitshell >Makefile <<!GROK!THIS!
  1210. X########################################################################
  1211. X# Makefile generated from Makefile.SH on $DATE
  1212. X
  1213. XSHELL = /bin/sh
  1214. XJMAKE = jmake
  1215. XTOP = ../..
  1216. XCURRENT = $CURRENT
  1217. XDIR = $DIR
  1218. X
  1219. X########################################################################
  1220. X# Parameters set by Configure -- edit config.sh if changes are needed
  1221. X
  1222. XCTAGS = ctags
  1223. XMAKE = make
  1224. XMV = $mv
  1225. XRM = $rm -f
  1226. X
  1227. X!GROK!THIS!
  1228. X$spitshell >>Makefile <<'!NO!SUBS!'
  1229. X########################################################################
  1230. X# Jmake rules for building libraries, programs, scripts, and data files
  1231. X# $X-Id: Jmake.rules,v 3.0 1993/08/18 12:04:14 ram Exp ram $
  1232. X
  1233. X########################################################################
  1234. X# Start of Jmakefile
  1235. X
  1236. X# $X-Id: Jmakefile,v 2.9.1.1 92/08/02 16:14:36 ram Exp $
  1237. X#
  1238. X#  Copyright (c) 1990-1993, Raphael Manfredi
  1239. X#  
  1240. X#  You may redistribute only under the terms of the Artistic License,
  1241. X#  as specified in the README file that comes with the distribution.
  1242. X#  You may reuse parts of this distribution only within the terms of
  1243. X#  that same Artistic License; a copy of which may be found at the root
  1244. X#  of the source tree for mailagent 3.0.
  1245. X#
  1246. X# $X-Log$
  1247. X
  1248. Xall::
  1249. X    @echo "The following may take a while..."
  1250. X    @echo "Don't panic if any of these tests fails and do not stop make."; \
  1251. X    ./TEST
  1252. X    @if test -f OK; then \
  1253. X        echo "Failure detected, retrying one more time, just in case..."; \
  1254. X        echo "Successful tests will not be rerun but flagged as 'done'."; \
  1255. X        sleep 2; \
  1256. X        ./TEST; \
  1257. X        if test -f OK; then \
  1258. X            echo "Hmm... Still failed... There might be a real problem."; \
  1259. X            echo "I shall be using the plain (non dataloaded) version."; \
  1260. X            sleep 2;\
  1261. X            ./TEST -n; \
  1262. X        fi \
  1263. X    fi
  1264. X
  1265. Xtest:
  1266. X    ./TEST -i
  1267. X
  1268. Xlocal_clean::
  1269. X    $(RM) -r out
  1270. X    $(RM) OK
  1271. X
  1272. X########################################################################
  1273. X# Common rules for all Makefiles -- do not edit
  1274. X
  1275. Xemptyrule::
  1276. X
  1277. Xclean: local_clean
  1278. Xrealclean: local_realclean
  1279. Xclobber: local_clobber
  1280. X
  1281. Xlocal_clean::
  1282. X    $(RM) core *~ *.o
  1283. X
  1284. Xlocal_realclean:: local_clean
  1285. X
  1286. Xlocal_clobber:: local_realclean
  1287. X    $(RM) Makefile config.sh
  1288. X
  1289. XMakefile.SH: Jmakefile
  1290. X    -@if test -f $(TOP)/.package; then \
  1291. X        if test -f Makefile.SH; then \
  1292. X            echo "    $(RM) Makefile.SH~; $(MV) Makefile.SH Makefile.SH~"; \
  1293. X            $(RM) Makefile.SH~; $(MV) Makefile.SH Makefile.SH~; \
  1294. X        fi; \
  1295. X        echo "    $(JMAKE) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT)" ; \
  1296. X        $(JMAKE) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT) ; \
  1297. X    else touch $@; exit 0; fi
  1298. X
  1299. XMakefile: Makefile.SH
  1300. X    /bin/sh Makefile.SH
  1301. X
  1302. Xtags::
  1303. X    $(CTAGS) -w *.[ch]
  1304. X    $(CTAGS) -xw *.[ch] > tags
  1305. X
  1306. Xlocal_clobber::
  1307. X    $(RM) tags
  1308. X
  1309. X########################################################################
  1310. X# Empty rules for directories with no sub-directories -- do not edit
  1311. X
  1312. Xinstall::
  1313. X    @echo "install in $(CURRENT) done."
  1314. X
  1315. Xdeinstall::
  1316. X    @echo "deinstall in $(CURRENT) done."
  1317. X
  1318. Xinstall.man::
  1319. X    @echo "install.man in $(CURRENT) done."
  1320. X
  1321. Xdeinstall.man::
  1322. X    @echo "deinstall.man in $(CURRENT) done."
  1323. X
  1324. XMakefiles::
  1325. X
  1326. XMakefiles.SH::
  1327. X
  1328. X!NO!SUBS!
  1329. Xchmod 644 Makefile
  1330. X$eunicefix Makefile
  1331. X
  1332. END_OF_FILE
  1333.   if test 3702 -ne `wc -c <'agent/test/Makefile.SH'`; then
  1334.     echo shar: \"'agent/test/Makefile.SH'\" unpacked with wrong size!
  1335.   fi
  1336.   # end of 'agent/test/Makefile.SH'
  1337. fi
  1338. if test -f 'agent/test/actions' -a "${1}" != "-c" ; then 
  1339.   echo shar: Will not clobber existing file \"'agent/test/actions'\"
  1340. else
  1341.   echo shar: Extracting \"'agent/test/actions'\" \(6572 characters\)
  1342.   sed "s/^X//" >'agent/test/actions' <<'END_OF_FILE'
  1343. X#
  1344. X# Mailagent rules for action regression tests
  1345. X#
  1346. X
  1347. X# $Id: actions,v 3.0 1993/11/29 13:49:23 ram Exp ram $
  1348. X#
  1349. X#  Copyright (c) 1990-1993, Raphael Manfredi
  1350. X#  
  1351. X#  You may redistribute only under the terms of the Artistic License,
  1352. X#  as specified in the README file that comes with the distribution.
  1353. X#  You may reuse parts of this distribution only within the terms of
  1354. X#  that same Artistic License; a copy of which may be found at the root
  1355. X#  of the source tree for mailagent 3.0.
  1356. X#
  1357. X# $Log: actions,v $
  1358. X# Revision 3.0  1993/11/29  13:49:23  ram
  1359. X# Baseline for mailagent 3.0 netwide release.
  1360. X#
  1361. X
  1362. Xmaildir = ~;
  1363. X
  1364. XX-Tag: /abort/
  1365. X{
  1366. X    ABORT -f;
  1367. X    SAVE always;
  1368. X    ABORT;
  1369. X    SAVE %u.1;
  1370. X}
  1371. X
  1372. XX-Tag: /annotate/
  1373. X{
  1374. X    ANNOTATE X-Anno-1: first;
  1375. X    ANNOTATE X-Anno-2 second;
  1376. X    ANNOTATE X-Anno-3;
  1377. X    ANNOTATE -d X-Anno-Error;
  1378. X    ANNOTATE -d X-Anno-4 fourth;
  1379. X}
  1380. X
  1381. XX-Tag: /apply/    { APPLY apply.1; REJECT -t APPLY; SAVE never };
  1382. X<IMPOSSIBLE>    { SAVE never; REJECT APPLY };
  1383. X<APPLY>            { APPLY apply.2; REJECT -f; SAVE always };
  1384. X<APPLY>            { SAVE never };
  1385. X
  1386. XX-Tag: /assign #1/,
  1387. XTo: /^(\w+)@/
  1388. X{
  1389. X    ASSIGN ram %1;
  1390. X    ASSIGN other try;
  1391. X    ASSIGN final '%#other.2';
  1392. X    ASSIGN :ram 1 + 2;
  1393. X    RUN /bin/echo '%#ram,%#other,%#final' > output';
  1394. X}
  1395. X
  1396. XX-Tag: /assign #2/
  1397. X{
  1398. X    ASSIGN :ram %#:ram + 4;
  1399. X    ASSIGN other '1+2';
  1400. X    ASSIGN final %#other + 4;
  1401. X    RUN /bin/echo '%#:ram,%#other,%#final' > output';
  1402. X}
  1403. X
  1404. XX-Tag: /back/
  1405. X{
  1406. X    BACK RUN ~/pgm;
  1407. X}
  1408. X
  1409. XX-Tag: /begin/
  1410. X{
  1411. X    BEGIN ONE;
  1412. X    BEGIN TWO;
  1413. X    REJECT;
  1414. X}
  1415. X
  1416. X<ONE> { SAVE one };
  1417. X<ONE,TWO,THREE> { SAVE two; BEGIN THREE; REJECT };
  1418. X<THREE> { SAVE three };
  1419. X
  1420. XX-Tag: /bounce 1/            { BOUNCE nobody };
  1421. XX-Tag: /bounce 2/            { BOUNCE "list" };
  1422. X
  1423. XX-Tag: /delete/                { DELETE };
  1424. X
  1425. XX-Tag: /feed/
  1426. X{
  1427. X    FEED grep -v To:;
  1428. X    SAVE ok;
  1429. X    REJECT;
  1430. X}
  1431. X
  1432. XX-Tag: /feed/, To: ram        { SAVE no_resync };
  1433. X
  1434. XX-Tag: /forward 1/            { FORWARD nobody };
  1435. XX-Tag: /forward 2/            { FORWARD "list" };
  1436. X
  1437. XX-Tag: /give/                { GIVE wc > output };
  1438. X
  1439. XX-Tag: /keep/
  1440. X{
  1441. X    KEEP From: To Subject X-None X-Long-* U*;
  1442. X    KEEP To Subject X-Long-* From X-None U*;
  1443. X    KEEP X-Long-*: Unu*-Head* X-None: To: Subject: From:;
  1444. X    KEEP "header-list" From;
  1445. X    SAVE ok;
  1446. X    REJECT;
  1447. X}
  1448. XX-Tag: /keep/, To: ram        { SAVE no_resync };
  1449. X
  1450. XX-Tag: /message/            { MESSAGE msg; DELETE };
  1451. X
  1452. XX-Tag: /macro/
  1453. X{
  1454. X    MACRO first It seems to;
  1455. X    MACRO first Another instance;
  1456. X    MACRO -p first;
  1457. X    MACRO second null;
  1458. X    MACRO -r second = ('work fine', EXPR);
  1459. X    MACRO third toto;
  1460. X    MACRO -d third;
  1461. X    RUN /bin/echo %-(first) %-(second)%-(third). > ok;
  1462. X    DELETE;
  1463. X}
  1464. X
  1465. XX-Tag: /nop/                { NOP; DELETE };
  1466. X
  1467. XX-Tag: /notify 1/            { NOTIFY msg nobody; DELETE };
  1468. XX-Tag: /notify 2/            { NOTIFY msg "list"; DELETE };
  1469. X
  1470. XX-Tag: /once/
  1471. X{
  1472. X    ONCE (ram,tag,1w) SAVE one;
  1473. X    ONCE (ram,tag,1w) SAVE two;
  1474. X    ONCE (mars,tag,1w) SAVE three;
  1475. X    ONCE (other,tag,0m) SAVE four;
  1476. X}
  1477. X
  1478. XX-Tag: /pass/
  1479. X{
  1480. X    PASS grep -v and;
  1481. X    SAVE output;
  1482. X}
  1483. X
  1484. XX-Tag: /perl/    { REJECT PERL };
  1485. X<PERL>            { PERL perl.1; SAVE never };
  1486. X<PERL>            { PERL perl.2 'arg 1' "arg 2"; SAVE never };
  1487. X<PERL>            { PERL perl.1; SAVE never };
  1488. X<PERL>            { PERL no_such_file; ABORT -f; SAVE never };
  1489. X
  1490. XX-Tag: /pipe/            { PIPE wc > output };
  1491. X
  1492. XX-Tag: /post 1/            { POST alt.test comp.others };
  1493. XX-Tag: /post 2/            { POST -l "list" };
  1494. X
  1495. XX-Tag: /purify/
  1496. X{
  1497. X    PURIFY grep -v Subject:;
  1498. X    SAVE output;
  1499. X}
  1500. X
  1501. XX-Tag: /queue/    { QUEUE; QUEUE; QUEUE; QUEUE };
  1502. X
  1503. XX-Tag: /record #1/                { RECORD; SAVE %u.1 };
  1504. XX-Tag: /record #1/                { SAVE %u.1 };
  1505. X<_SEEN_> X-Tag: /record #1/        { SAVE %u.2 };
  1506. X<RECORD> X-Tag: /record #2/        { SAVE %u.3 };
  1507. XX-Tag: /record #2/                { RECORD -r RECORD; SAVE %u.1 };
  1508. XX-Tag: /record #3/                { RECORD -a; SAVE %u.1 };
  1509. XX-Tag: /record #4/                { RECORD -c; REJECT -f RECORD; SAVE %u.1 };
  1510. X<RECORD> X-Tag: /record #4/        { SAVE %u.2 };
  1511. X
  1512. XX-Tag: /require/        { REJECT REQUIRE; };
  1513. X<REQUIRE>                { REQUIRE non_existent; REJECT -f; SAVE never };
  1514. X<REQUIRE>                { REQUIRE perl.1; REJECT -t; SAVE never };
  1515. X<REQUIRE>                { REQUIRE perl.1; REJECT -t; SAVE never };
  1516. X<REQUIRE>                { REQUIRE perl.2 test_pack; REJECT -t; SAVE never };
  1517. X<REQUIRE>
  1518. X{
  1519. X    MACRO perl_1 = (newcmd'perl_1, FN);
  1520. X    MACRO perl_2 = (&test_pack'perl_2, EXPR);
  1521. X    RUN /bin/echo We got %-(perl_1) and %-(perl_2) here > ok;
  1522. X    DELETE;
  1523. X};
  1524. X
  1525. XX-Tag: /reject/            { REJECT REJ; SAVE %u.1 };
  1526. X<REJ> X-Tag: /reject/    { SAVE always; REJECT -t REJ; SAVE never };
  1527. X
  1528. X<INITIAL> X-Tag: /restart/    { RESTART -t RES; SAVE %u.1; REJECT };
  1529. X<RES> X-Tag: /restart/        { RESTART no_such_mode; SAVE never };
  1530. X
  1531. XX-Tag: /resync/,
  1532. XTo: ram
  1533. X{
  1534. X    PURIFY grep -v To:;
  1535. X    RESYNC;
  1536. X    REJECT;
  1537. X}
  1538. X
  1539. XX-Tag: /resync/, To: ram    { SAVE %u.1 };
  1540. XX-Tag: /resync/                { SAVE output };
  1541. X
  1542. XX-Tag: /run/        { RUN /bin/echo Works. > ok; DELETE };
  1543. X
  1544. XX-Tag: /save #1/    { SAVE mbox };
  1545. XX-Tag: /save #2/    { SAVE path/another/third/mbox };
  1546. X
  1547. XX-Tag: /select/
  1548. X{
  1549. X    SELECT (Jan 2 1970 .. Jan 1 2001) SAVE one;
  1550. X    SELECT (last month .. last minute) SAVE two;
  1551. X    SELECT (last minute .. next minute) SAVE three;
  1552. X    SELECT (now - 10 seconds .. now + 5 minutes) SAVE four;
  1553. X    SELECT (Jan 1 2001 .. Jan 2 1970) SAVE five;
  1554. X}
  1555. X
  1556. XX-Tag: /unknown #1/    { unknown_command; DELETE };
  1557. XX-Tag: /unknown #2/    { DELETE; unknown_command };
  1558. X
  1559. XX-Tag: /split #1/    { SPLIT here; SAVE here };
  1560. XX-Tag: /split #2/    { SPLIT -ida here };
  1561. XX-Tag: /split #3/    { SPLIT -iew here };
  1562. XX-Tag: /split #4/    { SPLIT -iew };
  1563. XX-Tag: /split #5/    { SPLIT -iew here };
  1564. XX-Tag: /digest/        { SAVE here };
  1565. X
  1566. XX-Tag: /store #1/    { STORE mbox };
  1567. XX-Tag: /store #2/    { STORE path/another/third/mbox };
  1568. X
  1569. XX-Tag: /strip/
  1570. X{
  1571. X    STRIP X-N* Received:;
  1572. X    STRIP Received;
  1573. X    STRIP X-N* "header-list";
  1574. X    SAVE ok;
  1575. X    REJECT;
  1576. X}
  1577. XX-Tag: /strip/, To: ram        { SAVE no_resync };
  1578. X
  1579. XX-Tag: /subst/,
  1580. XTo: /(.*)/
  1581. X{
  1582. X    SUBST 1 /com/fr/g;
  1583. X    ASSIGN subject %[Subject];
  1584. X    ASSIGN :persistent '%#subject';
  1585. X    SUBST #subject /^Re:\s+//;
  1586. X    SUBST #:persistent /^Re:\s+//;
  1587. X    RUN /bin/echo '%1,%#subject,%#:persistent' >output;
  1588. X    DELETE;
  1589. X}
  1590. X
  1591. XX-Tag: /tr/,
  1592. XTo: /(.*)/
  1593. X{
  1594. X    TR 1 /a-z/A-Z/;
  1595. X    ASSIGN subject %[Subject];
  1596. X    ASSIGN :persistent '%#subject';
  1597. X    TR #subject /ice/ICE/;
  1598. X    TR #:persistent /ice/ICE/;
  1599. X    RUN /bin/echo '%1,%#subject,%#:persistent' >output;
  1600. X    DELETE;
  1601. X}
  1602. X
  1603. XX-Tag: /unique #1/                { UNIQUE; SAVE %u.1 };
  1604. X<_SEEN_> X-Tag: /unique #1/        { SAVE %u.1 };
  1605. XX-Tag: /unique #1/                { SAVE %u.2 };
  1606. X<UNIQUE> X-Tag: /unique #2/        { SAVE %u.3 };
  1607. XX-Tag: /unique #2/                { UNIQUE -r UNIQUE; SAVE %u.1 };
  1608. XX-Tag: /unique #3/                { UNIQUE -a; SAVE %u.1 };
  1609. XX-Tag: /unique #4/                { UNIQUE -c; REJECT -f UNIQUE; SAVE %u.1 };
  1610. X<UNIQUE> X-Tag: /unique #4/        { SAVE %u.2 };
  1611. X
  1612. XX-Tag: /write #1/    { WRITE mbox };
  1613. XX-Tag: /write #2/    { WRITE path/another/third/mbox };
  1614. X
  1615. XX-Tag: /compress/    { LEAVE; SAVE always; SAVE another };
  1616. XX-Tag: /mmdf/        { LEAVE; SAVE always; SAVE always };
  1617. XX-Tag: /newcmd/        { FIRST_CMD arg1 arg2; SECOND_CMD; DELETE };
  1618. XX-Tag: /usrmac/        { PERL script; ABORT -f; DELETE };
  1619. XX-Tag: /mh/            { SAVE +tmp; SAVE +new; SAVE dir; SAVE simple };
  1620. END_OF_FILE
  1621.   if test 6572 -ne `wc -c <'agent/test/actions'`; then
  1622.     echo shar: \"'agent/test/actions'\" unpacked with wrong size!
  1623.   fi
  1624.   # end of 'agent/test/actions'
  1625. fi
  1626. echo shar: End of archive 17 \(of 26\).
  1627. cp /dev/null ark17isdone
  1628. MISSING=""
  1629. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ; do
  1630.     if test ! -f ark${I}isdone ; then
  1631.     MISSING="${MISSING} ${I}"
  1632.     fi
  1633. done
  1634. if test "${MISSING}" = "" ; then
  1635.     echo You have unpacked all 26 archives.
  1636.     echo "Now run 'sh PACKNOTES', then read README and type Configure.'"
  1637.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1638. else
  1639.     echo You still must unpack the following archives:
  1640.     echo "        " ${MISSING}
  1641. fi
  1642. exit 0
  1643.  
  1644. exit 0 # Just in case...
  1645.