home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / mailagnt / part07 < prev    next >
Encoding:
Text File  |  1992-11-19  |  54.4 KB  |  1,597 lines

  1. Newsgroups: comp.sources.misc
  2. From: ram@eiffel.com (Raphael Manfredi)
  3. Subject:  v33i099:  mailagent - Rule Based Mail Filtering, Part07/17
  4. Message-ID: <1992Nov20.050547.14095@sparky.imd.sterling.com>
  5. X-Md4-Signature: 030d168bfd25baa1f52a02c22aa7c7b1
  6. Date: Fri, 20 Nov 1992 05:05:47 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: ram@eiffel.com (Raphael Manfredi)
  10. Posting-number: Volume 33, Issue 99
  11. Archive-name: mailagent/part07
  12. Environment: Perl, Sendmail, UNIX
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then feed it
  16. # into a shell via "sh file" or similar.  To overwrite existing files,
  17. # type "sh file -c".
  18. # Contents:  agent/pl/filter.pl agent/pl/runcmd.pl bin/perload
  19. # Wrapped by kent@sparky on Wed Nov 18 22:42:23 1992
  20. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  21. echo If this archive is complete, you will see the following message:
  22. echo '          "shar: End of archive 7 (of 17)."'
  23. if test -f 'agent/pl/filter.pl' -a "${1}" != "-c" ; then 
  24.   echo shar: Will not clobber existing file \"'agent/pl/filter.pl'\"
  25. else
  26.   echo shar: Extracting \"'agent/pl/filter.pl'\" \(20556 characters\)
  27.   sed "s/^X//" >'agent/pl/filter.pl' <<'END_OF_FILE'
  28. X;# $Id: filter.pl,v 2.9.1.5 92/11/01 16:01:13 ram Exp $
  29. X;#
  30. X;#  Copyright (c) 1992, Raphael Manfredi
  31. X;#
  32. X;#  You may redistribute only under the terms of the GNU General Public
  33. X;#  Licence as specified in the README file that comes with dist.
  34. X;#
  35. X;# $Log:    filter.pl,v $
  36. X;# Revision 2.9.1.5  92/11/01  16:01:13  ram
  37. X;# patch11: space between command and '(' made optional for ONCE and SELECT
  38. X;# patch11: (feature requested by Nigel Metheringham <nigelm@ohm.york.ac.uk>)
  39. X;# 
  40. X;# Revision 2.9.1.4  92/11/01  15:48:44  ram
  41. X;# patch11: continuation status was not properly updated by ONCE and SELECT
  42. X;# patch11: (matters for ONCE(...) REJECT; commands for instance)
  43. X;# 
  44. X;# Revision 2.9.1.3  92/08/26  13:12:03  ram
  45. X;# patch8: ASSIGN and SUBST/TR now deal with external variables
  46. X;# patch8: new PERL command
  47. X;# 
  48. X;# Revision 2.9.1.2  92/08/02  16:10:28  ram
  49. X;# patch2: added arguments to ABORT, REJECT and RESTART
  50. X;# patch2: moved flow altering functions into actions.pl
  51. X;# patch2: minor modification to ensure meaningful exit status
  52. X;# patch2: added -c option to RECORD and UNIQUE
  53. X;# 
  54. X;# Revision 2.9.1.1  92/07/25  12:38:07  ram
  55. X;# patch1: now correctly tags savings in subfolders as such
  56. X;# 
  57. X;# Revision 2.9  92/07/14  16:49:57  ram
  58. X;# 3.0 beta baseline.
  59. X;# 
  60. X;#
  61. X;# There are a number of variables which are used by the filter commands and
  62. X;# which are in the dynamic scope when those functions are called. The calling
  63. X;# tree being: analyze_mail -> xeqte -> run_command -> run_*, where '*' stands
  64. X;# for the action we are currently executing.
  65. X;#
  66. X;# All the run_* commands are called from within an eval by run_command, so that
  67. X;# any otherwise fatal error can be trapped and reported in the log file. This
  68. X;# is only a precaution against possible typos or other unpredictable errors.
  69. X;#
  70. X;# The following variables are inherited from run_command:
  71. X;#  $mfile is the name of the mail file processed
  72. X;#  $cmd is the command to be run
  73. X;#  $cmd_name is the command name (upper-cased)
  74. X;#  $ever_saved which states whether a saving/discarding action occurred
  75. X;#  $cont is the continuation status, modified by REJECT and friends
  76. X;#  $vacation which is a boolean stating whether vacation messages are allowed
  77. X;# The following variable is inherited from xeqte:
  78. X;#  $lastcmd is the failure status of the last command (among those to be kept)
  79. X;# The working mode is held in $wmode (comes from analyze_mail).
  80. X;#
  81. X;# All the commands return an exit status: 0 for ok, 1 for failure. This status
  82. X;# is normally recorded in $lastcmd by run_command, unless the executed action
  83. X;# belongs to the set of commands whose exit status is discarded (because they
  84. X;# can never fail).
  85. X;#
  86. X#
  87. X# Filter commands are run from here
  88. X#
  89. X
  90. X# Run the PROCESS command
  91. Xsub run_process {
  92. X    if (0 != do process()) {
  93. X        do add_log("ERROR while processing [$mfile]--queing it")
  94. X            if ($loglvl > 0);
  95. X        do queue_mail($file_name);
  96. X        return 1;
  97. X    }
  98. X    do add_log("PROCESSED [$mfile]") if $loglvl > 8;
  99. X    0;
  100. X}
  101. X
  102. X# Run the LEAVE command
  103. Xsub run_leave {
  104. X    local($mbox, $failed) = do leave();
  105. X    unless ($failed) {
  106. X        do add_log("LEFT [$mfile] in mailbox") if $loglvl > 2;
  107. X    }
  108. X    # Even if it failed, mark it as saved anyway, as the default action would
  109. X    # be a saving in mailbox and there is little chance another attempt would
  110. X    # succeed while this one failed.
  111. X    $ever_saved = 1;        # At least we tried to save it
  112. X    $failed;
  113. X}
  114. X
  115. X# Run the SAVE command
  116. Xsub run_save {
  117. X    local($folder) = $cmd =~ /^\w+\s+(\S+)/;    # Get first parameter
  118. X    &save_message($folder);
  119. X}
  120. X
  121. X# Run the STORE command
  122. Xsub run_store {
  123. X    local($folder) = $cmd =~ /^\w+\s+(\S+)/;    # Get first parameter
  124. X    local($mbox, $failed, $log_message) = &run_saving($folder, $FOLDER_APPEND);
  125. X    unless ($failed) {
  126. X        $ever_saved = 1;            # We were able to save it
  127. X        ($mbox, $failed) = do leave();
  128. X        unless ($failed) {
  129. X            do add_log("STORED [$mfile] in $log_message") if $loglvl > 2;
  130. X        } else {
  131. X            do add_log("WARNING only SAVED [$mfile] in $log_message")
  132. X                if $loglvl > 1;
  133. X            return 1;
  134. X        }
  135. X    } else {
  136. X        ($mbox, $failed) = do leave();
  137. X        unless ($failed) {
  138. X            $ever_saved = 1;            # We were able to save it
  139. X            do add_log("WARNING only LEFT [$mfile] in mailbox")
  140. X                if $loglvl > 1;
  141. X        }
  142. X    }
  143. X    $failed;
  144. X}
  145. X
  146. X# Run the WRITE command
  147. Xsub run_write {
  148. X    local($folder) = $cmd =~ /^\w+\s+(\S+)/;    # Get first parameter
  149. X    local($mbox, $failed, $log_message) = &run_saving($folder, $FOLDER_REMOVE);
  150. X    unless ($failed) {
  151. X        do add_log("WROTE [$mfile] in $log_message") if $loglvl > 2;
  152. X        $ever_saved = 1;            # We were able to save it
  153. X    }
  154. X    $failed;
  155. X}
  156. X
  157. X# Run the DELETE command
  158. Xsub run_delete {
  159. X    do add_log("DELETED [$mfile]") if $loglvl > 2;
  160. X    $ever_saved = 1;        # User chose to discard it, it counts as a save
  161. X    0;
  162. X}
  163. X
  164. X# Run the MESSAGE command
  165. Xsub run_message {
  166. X    local($msg) = $cmd =~ m|^\w+\s+(\S+)|;    # Vacation message location
  167. X    $msg =~ s/~/$cf'home/g;                    # ~ substitution
  168. X    local($failed) = do message($msg);
  169. X    unless ($failed) {
  170. X        $msg =~ s|^$cf'home|~|;                # Replace the home directory by ~
  171. X        do add_log("MESSAGE $msg for [$mfile]") if $loglvl > 2;
  172. X    }
  173. X    $failed;
  174. X}
  175. X
  176. X# Run the NOTIFY command
  177. Xsub run_notify {
  178. X    local($address, $msg) = $cmd =~ m|^\w+\s+(\S+)\s+(\S+)|;
  179. X    $msg =~ s/~/$cf'home/g;                    # ~ substitution
  180. X    local($failed) = do notify($msg, $address);
  181. X    unless ($failed) {
  182. X        $msg =~ s|^$cf'home|~|;                # Replace the home directory by ~
  183. X        do add_log("NOTIFIED $msg for [$mfile]") if $loglvl > 2;
  184. X    }
  185. X    $failed;
  186. X}
  187. X
  188. X# Run the REJECT command
  189. Xsub run_reject {
  190. X    local(*perform) = *do_reject;
  191. X    &alter_flow;        # Change control flow by calling &perform
  192. X}
  193. X
  194. X# Run the RESTART command
  195. Xsub run_restart {
  196. X    local(*perform) = *do_restart;
  197. X    &alter_flow;        # Change control flow by calling &perform
  198. X}
  199. X
  200. X# Run the ABORT command
  201. Xsub run_abort {
  202. X    local(*perform) = *do_abort;
  203. X    &alter_flow;        # Change control flow by calling &perform
  204. X}
  205. X
  206. X# Run the RESYNC command
  207. Xsub run_resync {
  208. X    do header_resync();            # Resynchronize the %Header array
  209. X    do add_log("RESYNCED [$mfile]") if $loglvl > 4;
  210. X    0;
  211. X}
  212. X
  213. X# Run the BEGIN command
  214. Xsub run_begin {
  215. X    local($newstate) = $cmd =~ m|^\w+\s+(\S+)|;    # New state wanted
  216. X    $newstate = 'INITIAL' unless $newstate;
  217. X    $wmode = $newstate;            # $wmode comes from analyze_mail
  218. X    do add_log("BEGUN new state $newstate") if $loglvl > 4;
  219. X    0;
  220. X}
  221. X
  222. X# Run the RECORD command
  223. Xsub run_record {
  224. X    local($option, $mode) = $cmd =~ m|^\w+\s+(-\w)?\s*(\w+)?|;
  225. X    local($failed) = 0;
  226. X    if (&history_record) {        # Message already seen
  227. X        $wmode = '_SEEN_';        # Enter special mode ($wmode from analyze_mail)
  228. X        &add_log("NOTICE entering seen mode") if $loglvl > 5;
  229. X        &alter_execution($option, $mode);
  230. X        $failed = 1;            # Make sure it "fails"
  231. X    }
  232. X    &add_log("RECORDED [$mfile]") if $loglvl > 4;
  233. X    $failed;
  234. X}
  235. X
  236. X# Run the UNIQUE command
  237. Xsub run_unique {
  238. X    local($option, $mode) = $cmd =~ m|^\w+\s+(-\w)?\s*(\w+)?|;
  239. X    local($failed) = 0;
  240. X    if (&history_record) {        # Message already seen
  241. X        &add_log("NOTICE message tagged as saved") if $loglvl > 5;
  242. X        $ever_saved = 1;        # In effect, runs a DELETE
  243. X        &alter_execution($option, $mode);
  244. X        $failed = 1;            # Make sure it "fails"
  245. X    }
  246. X    &add_log("UNIQUE [$mfile]") if $loglvl > 4;
  247. X    $failed;
  248. X}
  249. X
  250. X# Run the FORWARD command
  251. Xsub run_forward {
  252. X    local($addresses) = $cmd =~ m|^\w+\s+(.*)|;    # Address(es)
  253. X    local($failed) = do forward($addresses);
  254. X    unless ($failed) {
  255. X        do add_log("FORWARDED [$mfile] to $addresses") if $loglvl > 2;
  256. X        $ever_saved = 1;        # Forwarding succeeded, counts as a save
  257. X    }
  258. X    $failed;
  259. X}
  260. X
  261. X# Run the BOUNCE command
  262. Xsub run_bounce {
  263. X    local($addresses) = $cmd =~ m|^\w+\s+(.*)|;    # Address(es)
  264. X    local($failed) = do bounce($addresses);
  265. X    unless ($failed) {
  266. X        do add_log("BOUNCED [$mfile] to $addresses") if $loglvl > 2;
  267. X        $ever_saved = 1;        # Bouncing succeeded, counts as a save
  268. X    }
  269. X    $failed;
  270. X}
  271. X
  272. X# Run the POST command
  273. Xsub run_post {
  274. X    local($newsgroups) = $cmd =~ m|^\w+\s+(.*)|;    # Newsgroup(s)
  275. X    local($failed) = do post($newsgroups);
  276. X    unless ($failed) {
  277. X        do add_log("POSTED [$mfile] to $newsgroups") if $loglvl > 2;
  278. X        $ever_saved = 1;        # Posting succeeded, counts as a save
  279. X    }
  280. X    $failed;
  281. X}
  282. X
  283. X# Run the RUN command
  284. Xsub run_run {
  285. X    local($program) = $cmd =~ m|^\w+\s+(.*)|;    # Program to run
  286. X    local($failed) = do shell_command($program, $NO_INPUT, $NO_FEEDBACK);
  287. X    unless ($failed) {
  288. X        do add_log("RAN '$program' for [$mfile]") if $loglvl > 4;
  289. X    }
  290. X    $failed;
  291. X}
  292. X
  293. X# Run the PIPE command
  294. Xsub run_pipe {
  295. X    local($program) = $cmd =~ m|^\w+\s+(.*)|;    # Program to run
  296. X    local($failed) = do shell_command($program, $MAIL_INPUT, $NO_FEEDBACK);
  297. X    unless ($failed) {
  298. X        do add_log("PIPED [$mfile] to '$program'") if $loglvl > 4;
  299. X    }
  300. X    $failed;
  301. X}
  302. X
  303. X# Run the GIVE command
  304. Xsub run_give {
  305. X    local($program) = $cmd =~ m|^\w+\s+(.*)|;    # Program to run
  306. X    local($failed) = do shell_command($program, $BODY_INPUT, $NO_FEEDBACK);
  307. X    unless ($failed) {
  308. X        do add_log("GAVE [$mfile] to '$program'") if $loglvl > 4;
  309. X    }
  310. X    $failed;
  311. X}
  312. X
  313. X# Run the PASS command
  314. Xsub run_pass {
  315. X    local($program) = $cmd =~ m|^\w+\s+(.*)|;    # Program to run
  316. X    local($failed) = do shell_command($program, $BODY_INPUT, $FEEDBACK);
  317. X    unless ($failed) {
  318. X        do add_log("PASSED [$mfile] through '$program'") if $loglvl > 4;
  319. X    }
  320. X    $failed;
  321. X}
  322. X
  323. X# Run the FEED command
  324. Xsub run_feed {
  325. X    local($program) = $cmd =~ m|^\w+\s+(.*)|;    # Program to run
  326. X    local($failed) = do shell_command($program, $MAIL_INPUT, $FEEDBACK);
  327. X    unless ($failed) {
  328. X        do add_log("FED [$mfile] through '$program'") if $loglvl > 4;
  329. X    }
  330. X    $failed;
  331. X}
  332. X
  333. X# Run the PURIFY command
  334. Xsub run_purify {
  335. X    local($program) = $cmd =~ m|^\w+\s+(.*)|;    # Program to run
  336. X    local($failed) = do shell_command($program, $HEADER_INPUT, $FEEDBACK);
  337. X    unless ($failed) {
  338. X        do add_log("PURIFIED [$mfile] through '$program'") if $loglvl > 4;
  339. X    }
  340. X    $failed;
  341. X}
  342. X
  343. X# Run the BACK command
  344. X# Manipulates dynamically bound variable $cont (output from xeqte)
  345. Xsub run_back {
  346. X    # BACK command is handled recursively. The local variable $Back will be set
  347. X    # by xeq_back() if any feedback is to ever occur. This routine will be
  348. X    # transparently called instead of the usual handle_output() because of the
  349. X    # dynamic aliasing done here.
  350. X    local($Back) = '';                    # BACK may be nested
  351. X    local(*handle_output) = *xeq_back;    # Any output to be put in $Back
  352. X    local($command) = $cmd =~ m|^BACK\s+(.*)|;
  353. X    local($failed) = 0;
  354. X    $command =~ s/%/%%/g;                # Protect against 2nd macro substitution
  355. X    # Calling run_command will position $lastcmd to be the return status of
  356. X    # the last meaningful command executed. However, we reset $lastcmd before
  357. X    # diving into the execution.
  358. X    $lastcmd = 0;                        # Assume everything went fine
  359. X    &run_command($command);                # Run command (ignore return value)
  360. X    if ($Back ne '') {
  361. X        &add_log("got '$Back' back") if $loglvl > 11;
  362. X        $cont = &xeqte($Back);            # Get continuation status back
  363. X        $@ = '';                        # Avoid cascade of (same) error report
  364. X        &add_log("BACK from '$command'") if $loglvl > 4;
  365. X    } else {
  366. X        &add_log("WARNING got nothing out of '$command'") if $loglvl > 5;
  367. X    }
  368. X    $lastcmd;            # Propage error status we got from the $command
  369. X}
  370. X
  371. X# Run the ONCE command
  372. Xsub run_once {
  373. X    local($_) = $cmd;                    # The whole command line
  374. X    local($hname);                        # Hash name (e-mail address)
  375. X    local($tag);                        # Tag associated with command
  376. X    local($raw_period);                    # The period, as written
  377. X    if (s/^ONCE\s*\(([^,\)]*),\s*([^,;\)]*),\s*(\w+)\s*\)//) {
  378. X        ($hname, $tag, $raw_period) = ($1, $2, $3);
  379. X        do add_log("tag is ($hname, $tag, $raw_period)") if $loglvl > 18;
  380. X    } else {
  381. X        do add_log("ERROR bad once syntax (invalid tag)") if $loglvl > 1;
  382. X        return 1;
  383. X    }
  384. X    s/^\s*//;                            # Remove leading spaces
  385. X    local($period) = do seconds_in_period($raw_period);
  386. X    do add_log("period is $raw_period = $period seconds") if $loglvl > 18;
  387. X
  388. X    # Calling run_command will set $lastcmd to the status of the command. In
  389. X    # case we are running a command which does not alter this status, assume
  390. X    # everything is fine.
  391. X    $lastcmd = 0;                        # Assume command will run correctly
  392. X
  393. X    if (&once_check($hname, $tag, $period)) {
  394. X        do add_log("ONCE ($hname, $tag, $raw_period) $_") if $loglvl > 7;
  395. X        &s_once($cmd_name, $wmode, $tag);
  396. X        s/%/%%/g;                        # Protect against 2nd macro substitution
  397. X        $cont = &run_command($_);        # Run it, update continuation status
  398. X    } else {
  399. X        do add_log("retry time not reached for $_") if $loglvl > 12;
  400. X        &s_noretry($cmd_name, $wmode, $tag);
  401. X    }
  402. X
  403. X    $lastcmd;                            # Propagates execution status
  404. X}
  405. X
  406. X# Run the SELECT command
  407. Xsub run_select {
  408. X    local($_) = $cmd;                    # The whole command line
  409. X    local($start, $end);                # Date strings for start and end
  410. X    if (s/^SELECT\s*\(([^.\)]*)\.\.\s*([^\)]*)\)//) {
  411. X        ($start, $end) = ($1, $2);
  412. X        $start =~ s/\s*$//;                # Remove trailing spaces
  413. X        $end =~ s/\s*$//;
  414. X        &add_log("time is ($start .. $end)") if $loglvl > 18;
  415. X    } else {
  416. X        &add_log("ERROR bad select syntax (invalid time)") if $loglvl > 1;
  417. X        return 1;
  418. X    }
  419. X    local($now) = time;                    # Current time
  420. X    local($sec_start, $sec_end);        # Start and end converted in seconds
  421. X    $sec_start = &getdate($start, $now);
  422. X    if ($sec_start == -1) {
  423. X        &add_log("ERROR in SELECT: 1st time '$start'") if $loglvl > 1;
  424. X        return 1;
  425. X    }
  426. X    $sec_end = &getdate($end, $now);
  427. X    if ($sec_end == -1) {
  428. X        &add_log("ERROR in SELECT: 2nd time '$end'") if $loglvl > 1;
  429. X        return 1;
  430. X    }
  431. X    if ($sec_start > $sec_end) {
  432. X        &add_log("WARNING time selection always impossible?") if $loglvl > 1;
  433. X        return 0;
  434. X    }
  435. X
  436. X    # Calling run_command will set $lastcmd to the status of the command. In
  437. X    # case we are running a command which does not alter this status, assume
  438. X    # everything is fine.
  439. X    $lastcmd = 0;                        # Assume command will run correctly
  440. X
  441. X    s/^\s*//;                            # Remove leading spaces
  442. X    if ($now >= $sec_start && $now <= $sec_end) {
  443. X        &add_log("SELECT ($start .. $end) $_") if $loglvl > 7;
  444. X        s/%/%%/g;                        # Protect against 2nd macro substitution
  445. X        $cont = &run_command($_);        # Run command and update control flow
  446. X    } else {
  447. X        &add_log("time period not good for $_") if $loglvl > 12;
  448. X    }
  449. X
  450. X    $lastcmd;                            # Propagates execution status
  451. X}
  452. X
  453. X# Run the NOP command
  454. Xsub run_nop {
  455. X    do add_log("NOP [$mfile]") if $loglvl > 7;
  456. X    0;
  457. X}
  458. X
  459. X# Run the STRIP command
  460. Xsub run_strip {
  461. X    local($headers) = $cmd =~ m|^\w+\s+(.*)|;    # Headers to remove
  462. X    do alter_header($headers, $HD_STRIP);
  463. X    $headers = join(', ', split(/\s/, $headers));
  464. X    do add_log("STRIPPED $headers from [$mfile]") if $loglvl > 7;
  465. X    0;
  466. X}
  467. X
  468. X# Run the KEEP command
  469. Xsub run_keep {
  470. X    local($headers) = $cmd =~ m|^\w+\s+(.*)|;    # Headers to keep
  471. X    do alter_header($headers, $HD_KEEP);
  472. X    $headers = join(', ', split(/\s/, $headers));
  473. X    do add_log("KEPT $headers from [$mfile]") if $loglvl > 7;
  474. X    0;
  475. X}
  476. X
  477. X# Run the ANNOTATE command
  478. Xsub run_annotate {
  479. X    local($date, $field, $value) = $cmd =~ m|^\w+\s+(-d\s+)?([\w\-]+):?\s*(.*)|;
  480. X    if (0 == &annotate_header($field, $value, $date)) {
  481. X        &add_log("ANNOTATED [$mfile] with $field") if $loglvl > 7;
  482. X    }
  483. X    0;
  484. X}
  485. X
  486. X# Run the ASSIGN command
  487. Xsub run_assign {
  488. X    local($var, $value) = $cmd =~ m|^\w+\s+(:?\w+)\s+(.*)|;
  489. X    local($eval);                        # Evaluated value for expression
  490. X    local($@);
  491. X    # An expression may be provided as a value. If the whole value is enclosed
  492. X    # within simple quotes, then those are stripped and no evaluation is made.
  493. X    unless ($value =~ s/^'(.*)'$/$1/) {
  494. X        eval "\$eval = $value";            # Maybe value is an expression?
  495. X    } else {
  496. X        $eval = $value;                    # Leading and trailing ' trimmed
  497. X    }
  498. X    $value = $eval if $eval && $@ eq '';
  499. X    if ($var =~ s/^://) {
  500. X        &extern'set($var, $value);        # Persistent variable is set
  501. X    } else {
  502. X        $Variable{$var} = $value;        # User defined variable is set
  503. X    }
  504. X    do add_log("ASSGINED '$value' to '$var' [$mfile]") if $loglvl > 7;
  505. X    0;
  506. X}
  507. X
  508. X# Run the TR command
  509. Xsub run_tr {
  510. X    local($variable, $tr) = $cmd =~ m|^\w+\s+(#?:?\w+)\s+(.*)|;
  511. X    &alter_value($variable, "tr$tr");
  512. X}
  513. X
  514. X# Run the SUBST command
  515. Xsub run_subst {
  516. X    local($variable, $s) = $cmd =~ m|^\w+\s+(#?:?\w+)\s+(.*)|;
  517. X    &alter_value($variable, "s$s");
  518. X}
  519. X
  520. X# Run the SPLIT command
  521. Xsub run_split {
  522. X    local($folder) = $cmd =~ m|^\w+\s+(.*)|;    # Folder where split occurs
  523. X    local($failed) = do split($folder);
  524. X    if (0 == $failed % 2) {            # Message was in digest format
  525. X        if ($failed & 0x4) {
  526. X            do add_log("SPLIT [$mfile] in mailagent's queue") if $loglvl > 2;
  527. X        } else {
  528. X            do add_log("SPLIT [$mfile] in $folder") if $loglvl > 2;
  529. X        }
  530. X        # If digest was not in RFC-934 style, there is a chance the split
  531. X        # was not correctly performed. To avoid any accidental loss of
  532. X        # information, the original digest message is also saved if SPLIT
  533. X        # had a folder argument, or it is not tagged saved.
  534. X        if ($failed & 0x8) {        # Digest was not RFC-934 compliant
  535. X            &add_log("NOTICE [$mfile] not RFC-934 compliant") if $loglvl > 6;
  536. X            if ($folder ne '') {
  537. X                &add_log("NOTICE saving original [$mfile] in $folder")
  538. X                    if $loglvl > 6;
  539. X                &save_message($folder);
  540. X            } else {
  541. X                &add_log("NOTICE [$mfile] not tagged as saved")
  542. X                    if $loglvl > 6 && ($failed & 0x2);
  543. X            }
  544. X        } else {
  545. X            $ever_saved = 1 if $failed & 0x2;    # Split -i succeeded
  546. X        }
  547. X        $failed = 0;
  548. X    }
  549. X    # If message was not in digest format and a folder was specified, save
  550. X    # message in that folder.
  551. X    if ($failed < 0 && $folder ne '') {
  552. X        &add_log("NOTICE [$mfile] not in digest format") if $loglvl > 6;
  553. X        $failed = &save_message($folder);
  554. X    }
  555. X    $failed ? 1 : 0;    # Failure status from split can be negative
  556. X}
  557. X
  558. X# Run the VACATION command
  559. Xsub run_vacation {
  560. X    return 0 unless $cf'vacation =~ /on/i;    # Ignore if vacation mode off
  561. X    local($mode) = $cmd =~ m|^\w+\s+(.*)|;    # Vacation mode
  562. X    $vacation = ($mode =~ /on/i) ? 1 : 0;
  563. X    $mode = $vacation ? 'on' : 'off';
  564. X    &add_log("vacation message turned $mode") if $loglvl > 7;
  565. X    0;
  566. X}
  567. X
  568. X# Run the QUEUE command
  569. Xsub run_queue {
  570. X    # Mail is saved as a 'qm' file, to avoid endless loops when mailagent
  571. X    # processes the queue. This means the mail will be deferred for at
  572. X    # least half an hour.
  573. X    local($failed) = &queue_mail('', 1);    # No file name, mail in %Header
  574. X    $ever_saved = 1 unless $failed;            # Queuing counts as saving
  575. X    $failed;
  576. X}
  577. X
  578. X# Run the PERL command
  579. Xsub run_perl {
  580. X    local($script) = $cmd =~ m|^\w+\s+(.*)|;    # Script to be loaded
  581. X    local($failed) = &perl($script);
  582. X    unless ($failed) {
  583. X        $script =~ s/^$cf'home/~/;
  584. X        &add_log("PERLED [$mfile] through $script") if $loglvl > 7;
  585. X    }
  586. X    $failed;
  587. X}
  588. X
  589. X# For SAVE, STORE or WRITE, the job is the same
  590. Xsub run_saving {
  591. X    # If the name is not an absolute path, the folder directory is taken
  592. X    # in the "maildir" environment variable. If none, defaults to ~/Mail.
  593. X    local($folder, $remove) = @_;                # Shall we remove folder first?
  594. X    local($folddir) = $XENV{'maildir'};            # Folder directory location
  595. X    $folder = "~/mbox" unless $folder;            # No folder -> save in mbox
  596. X    $folder =~ s/~/$cf'home/g;                    # ~ substitution
  597. X    $folddir =~ s/~/$cf'home/g;                    # ~ substitution
  598. X    $folddir = "$cf'home/Mail" unless $folddir;    # Default folders in ~/Mail
  599. X    $folder = "$folddir/$folder" unless $folder =~ m|^/|;
  600. X    local($dir) = $folder =~ m|(.*)/.*|;        # Get directory name
  601. X    unless (-d "$dir") {
  602. X        do makedir($dir);
  603. X        unless (-d "$dir") {
  604. X            do add_log("ERROR couldn't create directory $dir")
  605. X                if $loglvl > 0;
  606. X        } else {
  607. X            do add_log("created directory $dir") if $loglvl > 7;
  608. X        }
  609. X    }
  610. X    if ($remove == $FOLDER_REMOVE) {
  611. X        # Folder has to be removed before writting into it. However, if it
  612. X        # is write protected, do not unlink it (save will fail later on anyway).
  613. X        unlink "$folder" if -f "$folder" && -w _;
  614. X    }
  615. X    local($mbox, $failed) = do save($folder);
  616. X    local($log_message);                # Log message to be issued
  617. X    unless ($failed) {
  618. X        local($file) = $folder;            # Work on a copy to detect leading dir
  619. X        $file =~ s|^$folddir/||;        # Preceded by folder directory?
  620. X        if ($file ne $folder) {
  621. X            $log_message = "folder $file";
  622. X        } else {
  623. X            $folder =~ s|^$cf'home|~|;    # Replace the home directory by ~
  624. X            $log_message = "$folder";
  625. X        }
  626. X    }
  627. X
  628. X    # Return the status of the save command and a part of the logging message
  629. X    # to be issued. That way, we get a nice contextual log.
  630. X    ($mbox, $failed, $log_message);
  631. X}
  632. X
  633. X# Perform the appropriate continuation status, depending on the option:
  634. Xsub alter_execution {
  635. X    local($option) = shift(@_);        # The invocation option
  636. X    local($mode) = shift(@_);        # Mode we have to change to
  637. X    if ($mode ne '') {
  638. X        $wmode = $mode;
  639. X        &add_log("entering new state $wmode") if $loglvl > 6;
  640. X    }
  641. X    &add_log("altering execution in mode '$wmode', option '$option'")
  642. X        if $loglvl > 18;
  643. X    if ($option eq '-c') {        # Continue execution
  644. X        0;
  645. X    } elsif ($option eq '-r') {    # Asks for RESTART
  646. X        &do_restart;
  647. X    } elsif ($option eq '-a') {    # Asks for ABORT
  648. X        &do_abort;
  649. X    } else {                    # Default is to REJECT
  650. X        &do_reject;
  651. X    }
  652. X    # Propagate return status.
  653. X}
  654. X
  655. X# Save message in specified folder
  656. Xsub save_message {
  657. X    local($folder) = @_;
  658. X    local($mbox, $failed, $log_message) = &run_saving($folder, $FOLDER_APPEND);
  659. X    unless ($failed) {
  660. X        do add_log("SAVED [$mfile] in $log_message") if $loglvl > 2;
  661. X        $ever_saved = 1;            # We were able to save it
  662. X    }
  663. X    $failed;
  664. X}
  665. X
  666. END_OF_FILE
  667.   if test 20556 -ne `wc -c <'agent/pl/filter.pl'`; then
  668.     echo shar: \"'agent/pl/filter.pl'\" unpacked with wrong size!
  669.   fi
  670.   # end of 'agent/pl/filter.pl'
  671. fi
  672. if test -f 'agent/pl/runcmd.pl' -a "${1}" != "-c" ; then 
  673.   echo shar: Will not clobber existing file \"'agent/pl/runcmd.pl'\"
  674. else
  675.   echo shar: Extracting \"'agent/pl/runcmd.pl'\" \(9795 characters\)
  676.   sed "s/^X//" >'agent/pl/runcmd.pl' <<'END_OF_FILE'
  677. X;# $Id: runcmd.pl,v 2.9.1.2 92/08/26 13:18:01 ram Exp $
  678. X;#
  679. X;#  Copyright (c) 1992, Raphael Manfredi
  680. X;#
  681. X;#  You may redistribute only under the terms of the GNU General Public
  682. X;#  Licence as specified in the README file that comes with dist.
  683. X;#
  684. X;# $Log:    runcmd.pl,v $
  685. X;# Revision 2.9.1.2  92/08/26  13:18:01  ram
  686. X;# patch8: new PERL command
  687. X;# 
  688. X;# Revision 2.9.1.1  92/08/02  16:14:10  ram
  689. X;# patch2: added support for escaping of ';' and backslash
  690. X;# patch2: new %Nostatus table records actions with no exiting status
  691. X;# patch2: the status of the last command is now recorded in lastcmd
  692. X;# 
  693. X;# Revision 2.9  92/07/14  16:50:46  ram
  694. X;# 3.0 beta baseline.
  695. X;# 
  696. X;# 
  697. X# Executing the action enclose in braces. The current working mode 'wmode' is
  698. X# a local variable defined in analyze_mail. But this variable is visible when
  699. X# 'xeqte' is called from within it. Thanks perl.
  700. X
  701. X# The following commands are available (case is irrelevent):
  702. X#  ABORT                    Aborts filtering right away
  703. X#  ANNOTATE field <value>   Annotation in header a la MH
  704. X#  ASSIGN var <value>       Assign value to the user-defined variable
  705. X#  BACK <cmd>               Execute <cmd> and eval its output
  706. X#  BEGIN state              Enter in a new state for analysis
  707. X#  BOUNCE address(es)       As FORWARD but leave header intact
  708. X#  DELETE                   Trash the mail away
  709. X#  FEED program             Same as PASS, but the whole message is given
  710. X#  FORWARD address(es)      Forwards mail to specified addresses
  711. X#  GIVE program             Give the body of the message to a program
  712. X#  KEEP header(s)           Lists the header fields we want to keep
  713. X#  LEAVE                    Leave mail in incomming mailbox
  714. X#  MESSAGE vacation         Sends a vacation-like message back
  715. X#  NOP                      No operation (useful only with ONCE)
  716. X#  NOTIFY address message   Notifies address with a given message
  717. X#  ONCE (period) <cmd>      Executes any other single command once per period
  718. X#  PASS program             Pass body to program and get new body back
  719. X#  PERL script              Run script to perform some filtering actions
  720. X#  PIPE program             Pipes message to program
  721. X#  POST newsgroup(s)        Post message on specified newsgroups
  722. X#  PROCESS                  The mailagent processes the commands in body
  723. X#  PURIFY program           Feed header to program and get new header back
  724. X#  QUEUE                    Queue mail (counts as save if successful)
  725. X#  RECORD                   Record message and REJECT in seen mode if present
  726. X#  REJECT                   Abort execution and continue analysis
  727. X#  RESTART                  Abort execution and restart analysis from scratch
  728. X#  RESYNC                   Resynchronize header (useful only with FEED)
  729. X#  RUN program              Run the specified program
  730. X#  SAVE folder              Saves mail in folder for delayed reading
  731. X#  SELECT (when) <cmd>      Run command only within certain time period
  732. X#  SPLIT folder             Split digest message into folder
  733. X#  STORE folder             Same as SAVE folder; LEAVE
  734. X#  STRIP header(s)          Removes the lines from the message's header
  735. X#  SUBST var //             Apply a substitution on variable
  736. X#  TR var //                Apply a translation on variable
  737. X#  UNIQUE                   Delete message if already in history and REJECT
  738. X#  VACATION on/off            Allow/disallow vacation messages
  739. X#  WRITE folder             Writes mail in folder (replaces, does not append)
  740. X
  741. X# Split the commands and execute them. This function is the main entry point
  742. X# for nesting level (e.g. execution of commands from BACK are driven by xeqte).
  743. X# We wish to keep track of the execution status of the last command, as does
  744. X# the shell with its $? variable. This is done by $lastcmd.
  745. Xsub xeqte {
  746. X    local($line) = shift(@_);        # Commands to execute
  747. X    local(@cmd);                    # The commands to be ran
  748. X    local($status) = $FT_CONT;        # Status returned by run_command
  749. X    local($lastcmd) = 0;            # Failure status from last command
  750. X    local($_);
  751. X
  752. X    # Normally, a ';' separates each action. However, an escaped one as in \;
  753. X    # must not be taken into account. We also need to escape a single \, in
  754. X    # case we want a \ followed by a ; grr...
  755. X    $line =~ s/\\\\/\02/g;            # \\ -> ^B
  756. X    $line =~ s/\\;/\01/g;            # \; -> ^A
  757. X    @cmd = split(/;/, $line);        # Put all commands in an array
  758. X    foreach (@cmd) {                # Now restore orginal escaped sequences
  759. X        s/\01/;/g;                    # ^A -> ;
  760. X        s/\02/\\/g;                    # ^B -> \
  761. X    }
  762. X
  763. X    # Now run each command in turn
  764. X    foreach $cmd (@cmd) {
  765. X        $status = &run_command($cmd);
  766. X        last unless $status == $FT_CONT;
  767. X    }
  768. X
  769. X    # Remap $FT_ABORT on $FT_CONT. In effect, we just skipped the remaining
  770. X    # commands on the line and act as if they had been executed. This indeed
  771. X    # achieves the ABORT command.
  772. X    $status = $FT_CONT if $status == $FT_ABORT;
  773. X    $status;
  774. X}
  775. X
  776. X# Executes a filter command and return continuing status:
  777. X#  FT_CONT to continue
  778. X#  FT_REJECT if a reject was found
  779. X#  FT_RESTART if a restart was found
  780. X#  FT_ABORT if an abort was found
  781. Xsub run_command {
  782. X    local($cmd) = @_;                # Command to be run (passed to subroutines)
  783. X    local($cmd_name);                # Command name
  784. X    local($cont) = $FT_CONT;        # Continue by default
  785. X    local($mfile) = $file_name =~ m|.*/(.*)|;    # Basename of mail file
  786. X    $mfile = $file_name unless $mfile;            # There was no / in name
  787. X    $mfile = '<stdin>' unless $mfile;            # No $file_name if from STDIN
  788. X    do macros_subst(*cmd);            # Macros substitutions
  789. X    $cmd =~ s/^\s*//;                # Remove leading spaces
  790. X    $cmd =~ s/\s*$//;                # And trailing ones
  791. X    return $cont unless $cmd;        # Ignore null instructions
  792. X    ($cmd_name) = $cmd =~ /^(\w+)/;
  793. X    $cmd_name =~ tr/a-z/A-Z/;        # In uppercase from now on
  794. X    # In the special mode _SEEN_, only a restricted set of action are allowed
  795. X    if ($wmode eq '_SEEN_') {
  796. X        if ($Rfilter{$cmd_name}) {
  797. X            do add_log("WARNING command $cmd_name not allowed") if $loglvl > 5;
  798. X            return $cont;
  799. X        }
  800. X    }
  801. X    do add_log("XEQ ($cmd)") if $loglvl > 10;
  802. X    print ">> $cmd\n" if $track_all;        # Option -t
  803. X    local($routine) = $Filter{$cmd_name};
  804. X    # Unknown commands default to LEAVE if no save have ever been done.
  805. X    # Otherwise, they are simply ignored.
  806. X    unless ($routine) {
  807. X        local($what) = 'defaults to LEAVE';
  808. X        $what = 'ignored' if $ever_saved;
  809. X        do add_log("ERROR unknown command $cmd_name ($what)")
  810. X            if $loglvl > 1;
  811. X        $routine = $Filter{'LEAVE'};        # Default action
  812. X        return $cont if $ever_saved;        # Command ignored
  813. X    }
  814. X    local($failed) = eval("&$routine");        # Eval traps all fatal errors
  815. X    $failed = 1 if &eval_error;                # Make sure eval worked
  816. X
  817. X    # If command does not belong to the set of those who do not modify the
  818. X    # last execution status recorded, then update $lastcmd with the failure
  819. X    # status.
  820. X    $lastcmd = $failed unless $Nostatus{$cmd_name};
  821. X
  822. X    # Update statistics
  823. X    unless ($failed) {
  824. X        &s_action($cmd_name, $wmode);
  825. X    } else {
  826. X        &s_failed($cmd_name, $wmode);
  827. X    }
  828. X    $cont;                # Continue status
  829. X}
  830. X
  831. X# Each filter command is handled by a specific function. The Filter array
  832. X# maps an action name to a subroutine, while the Rfilter array lists the
  833. X# authorized actions in the special mode _SEEN_ (used when a mail already
  834. X# filtered is processed).
  835. X# The %Nostatus array records the commands which do not modify the execution
  836. X# status recorded by the last command. Typically, those are commands which can
  837. X# never fail.
  838. Xsub init_filter {
  839. X    %Filter = (
  840. X        'ABORT', 'run_abort',        # Aborts application of filtering rules
  841. X        'ANNOTATE', 'run_annotate',    # Add new field into header
  842. X        'ASSIGN', 'run_assign',        # Assign value to variable
  843. X        'BACK', 'run_back',            # Eval feedback
  844. X        'BEGIN', 'run_begin',        # Enter in a new state
  845. X        'BOUNCE', 'run_bounce',        # Bounce message
  846. X        'DELETE', 'run_delete',        # Throw mail away, explicitely
  847. X        'FEED', 'run_feed',            # Feed back mail through program
  848. X        'FORWARD', 'run_forward',    # Forward mail
  849. X        'GIVE', 'run_give',            # Give body to command
  850. X        'KEEP', 'run_keep',            # Keep only the listed header fields
  851. X        'LEAVE', 'run_leave',        # Saving in incomming mailbox
  852. X        'MESSAGE', 'run_message',    # Send a vacation-like file
  853. X        'NOP', 'run_nop',            # No operation
  854. X        'NOTIFY', 'run_notify',        # Notify reception of message
  855. X        'ONCE', 'run_once',            # Once control
  856. X        'PASS', 'run_pass',            # Pass body to program with feedback
  857. X        'PERL', 'run_perl',            # Perform actions from within a perl script
  858. X        'PIPE', 'run_pipe',            # Pipe message to specified command
  859. X        'POST', 'run_post',            # Post mail to the net
  860. X        'PROCESS', 'run_process',    # Mailagent processing
  861. X        'PURIFY', 'run_purify',        # Purify header through a program
  862. X        'QUEUE', 'run_queue',        # Queue mail
  863. X        'RECORD', 'run_record',        # Record message in history
  864. X        'REJECT', 'run_reject',        # Reject
  865. X        'RESTART', 'run_restart',    # Restart
  866. X        'RESYNC', 'run_resync',        # Resynchronizes the header
  867. X        'RUN', 'run_run',            # Run specified program
  868. X        'SAVE', 'run_save',            # Save in a folder
  869. X        'SELECT', 'run_select',        # Time selection control
  870. X        'SPLIT', 'run_split',        # Split digest message
  871. X        'STORE', 'run_store',        # Save and leave copy in mailbox
  872. X        'STRIP', 'run_strip',        # Strip some header lines
  873. X        'SUBST', 'run_subst',        # Substitution on variable
  874. X        'TR', 'run_tr',                # Translation on variable
  875. X        'UNIQUE', 'run_unique',        # Delete message if already in history
  876. X        'VACATION', 'run_vacation',    # Allow or forbid vacation messages
  877. X        'WRITE', 'run_write',        # Write mail in folder
  878. X    );
  879. X    # Restricted filter actions: the commands listed below cannot be
  880. X    # executed in the special seen mode (in order to avoid loops).
  881. X    %Rfilter = (
  882. X        'BACK', 1,
  883. X        'BOUNCE', 1,
  884. X        'FEED', 1,
  885. X        'FORWARD', 1,
  886. X        'GIVE', 1,
  887. X        'NOTIFY', 1,
  888. X        'PASS', 1,
  889. X        'PIPE', 1,
  890. X        'POST', 1,
  891. X        'PURIFY', 1,
  892. X        'QUEUE', 1,
  893. X        'RUN', 1,
  894. X    );
  895. X    # The following commands do not modify the last status recorded.
  896. X    %Nostatus = (
  897. X        'ABORT', 1,
  898. X        'ASSIGN', 1,
  899. X        'BEGIN', 1,
  900. X        'KEEP', 1,
  901. X        'NOP', 1,
  902. X        'REJECT', 1,
  903. X        'RESTART', 1,
  904. X        'RESYNC', 1,
  905. X        'STRIP', 1,
  906. X        'VACATION', 1,
  907. X    );
  908. X}
  909. X
  910. END_OF_FILE
  911.   if test 9795 -ne `wc -c <'agent/pl/runcmd.pl'`; then
  912.     echo shar: \"'agent/pl/runcmd.pl'\" unpacked with wrong size!
  913.   fi
  914.   # end of 'agent/pl/runcmd.pl'
  915. fi
  916. if test -f 'bin/perload' -a "${1}" != "-c" ; then 
  917.   echo shar: Will not clobber existing file \"'bin/perload'\"
  918. else
  919.   echo shar: Extracting \"'bin/perload'\" \(21206 characters\)
  920.   sed "s/^X//" >'bin/perload' <<'END_OF_FILE'
  921. X# feed this into perl
  922. X'/bin/true' && eval 'exec perl -S $0 "$@"'
  923. X    if $running_under_some_shell;
  924. X'di';
  925. X'ig00';
  926. X
  927. X#
  928. X# This perl script is its own manual page [generated by wrapman]
  929. X#
  930. X
  931. X# $Id: perload,v 2.9.1.4 92/11/10 10:14:47 ram Exp $
  932. X#
  933. X#  Copyright (c) 1992, Raphael Manfredi
  934. X#
  935. X#  You may redistribute only under the terms of the GNU General Public
  936. X#  Licence as specified in the README file that comes with dist.
  937. X#
  938. X# $Log:    perload,v $
  939. X# Revision 2.9.1.4  92/11/10  10:14:47  ram
  940. X# patch12: fixed English typo in manual section
  941. X# 
  942. X# Revision 2.9.1.3  92/08/26  13:22:38  ram
  943. X# patch8: added -t option to include untainting of loaded code
  944. X# 
  945. X# Revision 2.9.1.2  92/08/12  21:36:54  ram
  946. X# patch6: new -o option which optimizes dataloading by building an offset table
  947. X# patch6: loading routines now avoid unnecessary strings operations
  948. X# patch6: previous changes contributed by Wayne Scott <wscott@ecn.purdue.edu>
  949. X# 
  950. X# Revision 2.9.1.1  92/08/02  16:25:43  ram
  951. X# patch2: dataloading routines now fully operate in perload package
  952. X# 
  953. X# Revision 2.9  92/07/14  16:53:40  ram
  954. X# 3.0 beta baseline.
  955. X# 
  956. X
  957. X# Replace each function definition in a loading section by two stubs and
  958. X# reject the definition into the DATA part of the script if in a dataload
  959. X# section or into a FILE if in an autoload section.
  960. X
  961. X$in_load = 0;                    # In a loading section
  962. X$autoload = '';                    # Name of autoloaded file
  963. X$has_invocation_stub = 0;        # True if we detect a #! stub
  964. X$current_package = 'main';        # Current package
  965. X$init_emitted = 0;                # True when dataloading stamp was emitted
  966. X$in_function = 0;
  967. X
  968. Xrequire 'getopt.pl';
  969. X&Getopt;
  970. X
  971. Xwhile (<>) {
  972. X    if ($. == 1 && /^#.*perl/) {    # Invocation stub
  973. X        $has_invocation_stub = 1;
  974. X        print;
  975. X        next;
  976. X    }
  977. X    if ($. <= 3 && $has_invocation_stub) {
  978. X        print;
  979. X        next;
  980. X    }
  981. X    if (/^\s*$/) {
  982. X        &flush_comment;
  983. X        print unless $in_function;
  984. X        print if $in_function && !$in_load;
  985. X        if ($in_function && $in_load) {
  986. X            push(@Data, "\n") unless $autoload;
  987. X            $Auto{$autoload} .= "\n" if $autoload;
  988. X        }
  989. X        next;
  990. X    }
  991. X    if (/^\s*#/) {
  992. X        if (/^#\s*perload on/i) {        # Enter a loading section
  993. X            print unless /:$/;
  994. X            $in_load = 1;
  995. X            next;
  996. X        }
  997. X        if (/^#\s*perload off/i) {        # End a loading section
  998. X            print unless /:$/;
  999. X            $in_load = 0;
  1000. X            next;
  1001. X        }
  1002. X        if (/^#\s*autoload (\S+)/i) {    # Enter autoloading section
  1003. X            print unless /:$/;
  1004. X            push(@autoload, $autoload);    # Directives may be nested
  1005. X            $autoload = $1;
  1006. X            $in_load += 2;
  1007. X            next;
  1008. X        }
  1009. X        if (/^#\s*offload/i) {            # End autoloading section
  1010. X            print unless /:$/;
  1011. X            $autoload = pop(@autoload);    # Revert to previously active file
  1012. X            $in_load -= 2;
  1013. X            next;
  1014. X        }
  1015. X        &emit_init unless $init_emitted;
  1016. X        push(@Comment, $_) unless $in_function;
  1017. X        print if $in_function && !$in_load;
  1018. X        next unless $in_function;
  1019. X        push(@Data, $_) unless $autoload;
  1020. X        $Auto{$autoload} .= $_ if $autoload;
  1021. X        next;
  1022. X    }
  1023. X    &emit_init unless $init_emitted;
  1024. X    /^package (\S+)\s*;/ && ($current_package = $1);
  1025. X    unless ($in_load) {
  1026. X        &flush_comment;
  1027. X        print;
  1028. X        next;
  1029. X    }
  1030. X    # We are in a loading section
  1031. X    if (/^sub\s+([\w']+)\s*\{(.*)/) {
  1032. X        die "line $.: function $1 defined within another function.\n"
  1033. X            if $in_function;
  1034. X        # Silently ignore one-line functions
  1035. X        if (/\}/) {
  1036. X            &flush_comment;
  1037. X            print;
  1038. X            next;
  1039. X        }
  1040. X        $comment = $2;
  1041. X        $in_function = 1;
  1042. X        $function = $1;
  1043. X        ($fn_package, $fn_basename) = $function =~ /^(\w+)'(\w+)/;
  1044. X        unless ($fn_package) {
  1045. X            $fn_package = $current_package;
  1046. X            $fn_basename = $function;
  1047. X        }
  1048. X        # Keep leading function comment
  1049. X        foreach (@Comment) {
  1050. X            push(@Data, $_) unless $autoload;
  1051. X            $Auto{$autoload} .= $_ if $autoload;
  1052. X        }
  1053. X        @Comment = ();
  1054. X        # Change package context for correct compilation: the name is visible
  1055. X        # within the original function package while the body of the function
  1056. X        # is compiled within the current package.
  1057. X        $declaration = "sub $fn_package" . "'load_$fn_basename {$comment\n";
  1058. X        $package_context = "\tpackage $current_package;\n";
  1059. X        if ($autoload) {
  1060. X            $Auto{$autoload} .= $declaration . $package_context;
  1061. X        } else {
  1062. X            push(@Data, $declaration, $package_context);
  1063. X        }
  1064. X        # Emit stubs
  1065. X        print "sub $fn_package", "'$fn_basename";
  1066. X        print " { &auto_$fn_package", "'$fn_basename; }\n";
  1067. X        print "sub auto_$fn_package", "'$fn_basename { ";
  1068. X        print '&main\'dataload' unless $autoload;
  1069. X        print '&main\'autoload(' . "'$autoload'" . ', @_)' if $autoload;
  1070. X        print "; }\n";
  1071. X        next;
  1072. X    }
  1073. X    unless ($in_function) {
  1074. X        &flush_comment;
  1075. X        print;
  1076. X        next;
  1077. X    }
  1078. X    # We are in a loading section and inside a function body
  1079. X    push(@Data, $_) unless $autoload;
  1080. X    $Auto{$autoload} .= $_ if $autoload;
  1081. X    $in_function = 0 if /^\}/;
  1082. X    if (/^\}/) {
  1083. X        push(@Data, "\n") unless $autoload;
  1084. X        $Auto{$autoload} .= "\n" if $autoload;
  1085. X    }
  1086. X}
  1087. X
  1088. X@auto = keys %Auto;
  1089. Xif (@auto > 0) {
  1090. X    print &q(<<'EOC');
  1091. X:# Load the calling function from file and call it. This function is called
  1092. X:# only once per file to be loaded.
  1093. X:sub main'autoload {
  1094. X:    local($__file__) = shift(@_);
  1095. X:    local($__packname__) = (caller(1))[3];
  1096. X:    local($__rpackname__) = $__packname__;
  1097. X:    local($__saved__) = $@;
  1098. X:    $__rpackname__ =~ s/^auto_//;
  1099. X:    &perload'load_from_file($__file__);
  1100. X:    $__rpackname__ =~ s/'/'load_/;
  1101. X:    $@ = $__saved__;        # Restore value $@ had on entrance
  1102. X:    &$__rpackname__(@_);    # Call newly loaded function
  1103. X:}
  1104. X:
  1105. X:# Load file and compile it, substituing the second stub function with the
  1106. X:# loaded ones. Location of the file uses the @AUTO array.
  1107. X:sub perload'load_from_file {
  1108. X:    package perload;
  1109. X:    local($file) = @_;                # File to be loaded
  1110. X:    local($body) = ' ' x 1024;        # Pre-extent
  1111. X:    local($load) = ' ' x 256;        # Loading operations
  1112. X:    # Avoid side effects by protecting special variables which will be
  1113. X:    # changed by the autoloading operation.
  1114. X:    local($., $_, $@);
  1115. X:    $body = '';
  1116. X:    $load = '';
  1117. X:    &init_auto unless defined(@'AUTO);    # Make sure we have a suitable @AUTO
  1118. X:    &locate_file unless -f "$file";        # Locate file if relative path
  1119. X:    open(FILE, $file) ||
  1120. X:        die "Can't load $'__rpackname__ from $file: $!\n";
  1121. X:    while (<FILE>) {
  1122. X:        $load .= '*auto_' . $1 . '\'' . $2 . '= *' . $1 . '\'' . "load_$2;\n"
  1123. X:            if (/^sub\s+(\w+)'load_(\w+)\s*\{/);
  1124. X:        $body .= $_;
  1125. X:    }
  1126. X:    close FILE;
  1127. XEOC
  1128. X    if ($opt_t) {
  1129. X        print &q(<<'EOC');
  1130. X:    # Untaint body when running setuid
  1131. X:    $body =~ /^([^\0]*)/;
  1132. X:    # No need to untaint $load, as it was built using trusted variables
  1133. X:    eval $1 . $load;
  1134. XEOC
  1135. X    } else {
  1136. X        print &q(<<'EOC');
  1137. X:    eval $body . $load;
  1138. XEOC
  1139. X    }
  1140. X    print &q(<<'EOC');
  1141. X:    chop($@) && die "$@, while parsing code of $file.\n";
  1142. X:}
  1143. X:
  1144. X:# Initialize the @AUTO array. Attempt defining it by using the AUTOLIB
  1145. X:# environment variable if set, otherwise look in auto/ first, then in the
  1146. X:# current directory.
  1147. X:sub perload'init_auto {
  1148. X:    if (defined $ENV{'AUTOLIB'} && $ENV{'AUTOLIB'}) {
  1149. X:        @AUTO = split(':', $ENV{'AUTOLIB'});
  1150. X:    } else {
  1151. X:        @AUTO = ('auto', '.');
  1152. X:    }
  1153. X:}
  1154. X:
  1155. X:# Locate to-be-loaded file held in $file by looking through the @AUTO array.
  1156. X:# This variable, defined in 'load_from_file', is modified as a side effect.
  1157. X:sub perload'locate_file {
  1158. X:    package perload;
  1159. X:    local($fullpath);
  1160. X:    foreach $dir (@'AUTO) {
  1161. X:        $fullpath = $dir . '/' . $file;
  1162. X:        last if -f "$fullpath";
  1163. X:        $fullpath = '';
  1164. X:    }
  1165. X:    $file = $fullpath if $fullpath;        # Update var from 'load_from_file'
  1166. X:}
  1167. X:
  1168. XEOC
  1169. X}
  1170. X
  1171. Xif (@Data > 0) {
  1172. X    print &q(<<'EOC');
  1173. X:# Load the calling function from DATA segment and call it. This function is
  1174. X:# called only once per routine to be loaded.
  1175. X:sub main'dataload {
  1176. X:    local($__packname__) = (caller(1))[3];
  1177. X:    local($__rpackname__) = $__packname__;
  1178. X:    local($__at__) = $@;
  1179. X:    $__rpackname__ =~ s/^auto_//;
  1180. X:    &perload'load_from_data($__rpackname__);
  1181. X:    local($__fun__) = "$__rpackname__";
  1182. X:    $__fun__ =~ s/'/'load_/;
  1183. X:    eval "*$__packname__ = *$__fun__;";    # Change symbol table entry
  1184. X:    die $@ if $@;        # Should not happen
  1185. X:    $@ = $__at__;        # Restore value $@ had on entrance
  1186. X:    &$__fun__;            # Call newly loaded function
  1187. X:}
  1188. X:
  1189. X:# Load function name given as argument, fatal error if not existent
  1190. X:sub perload'load_from_data {
  1191. X:    package perload;
  1192. X:    local($pos) = $Datapos{$_[0]};            # Offset within DATA
  1193. X:    # Avoid side effects by protecting special variables which will be changed
  1194. X:    # by the dataloading operation.
  1195. X:    local($., $_, $@);
  1196. X:    $pos = &fetch_function_code unless $pos;
  1197. X:    die "Function $_[0] not found in data section.\n" unless $pos;
  1198. X:    die "Cannot seek to $pos into data section.\n"
  1199. X:        unless seek(main'DATA, $pos, 0);
  1200. X:    local($/) = "\n}";
  1201. X:    local($body) = scalar(<main'DATA>);
  1202. X:    local($*) = 1;
  1203. X:    die "End of file found while loading $_[0].\n" unless $body =~ /^\}$/;
  1204. XEOC
  1205. X    if ($opt_t) {
  1206. X        print &q(<<'EOC');
  1207. X:    # Untaint body when running setuid
  1208. X:    $body =~ /^([^\0]*)/;
  1209. X:    # Now we may safely eval it without getting an insecure dependency
  1210. X:    eval $1;        # Load function into perl space
  1211. XEOC
  1212. X    } else {
  1213. X        print &q(<<'EOC');
  1214. X:    eval $body;        # Load function into perl space
  1215. XEOC
  1216. X    }
  1217. X    print &q(<<'EOC');
  1218. X:    chop($@) && die "$@, while parsing code of $_[0].\n";
  1219. X:}
  1220. X:
  1221. XEOC
  1222. X    print &q(<<'EOC') unless $opt_o;
  1223. X:# Parse text after the END token and record defined loadable functions (i.e.
  1224. X:# those whose name starts with load_) into the %Datapos array. Such function
  1225. X:# definitions must be left adjusted. Stop as soon as the function we want
  1226. X:# has been found.
  1227. X:sub perload'fetch_function_code {
  1228. X:    package perload;
  1229. X:    local($pos) = tell main'DATA;
  1230. X:    local($in_function) = 0;
  1231. X:    local($func_name);
  1232. X:    local($., $_);
  1233. X:    while (<main'DATA>) {
  1234. X:        if (/^sub\s+(\w+)'load_(\w+)\s*\{/) {
  1235. X:            die "DATA line $.: function $1'$2 defined within $func_name.\n"
  1236. X:                if $in_function;
  1237. X:            $func_name = $1 . '\'' . $2;
  1238. X:            $Datapos{$func_name} = $pos;
  1239. X:            $in_function = 1;
  1240. X:            next;
  1241. X:        }
  1242. X:        $in_function = 0 if /^\}/;
  1243. X:        next if $in_function;
  1244. X:        return $pos if $func_name eq $_[0];
  1245. X:        $pos = tell main'DATA;
  1246. X:    }
  1247. X:    0;        # Function not found
  1248. X:}
  1249. X:
  1250. XEOC
  1251. X    print &q(<<'EOC') if $opt_o;
  1252. X:# This function is called only once, and fills in the %Datapos array with
  1253. X:# the offset of each of the dataloaded routines held in the data section.
  1254. X:sub perload'fetch_function_code {
  1255. X:    package perload;
  1256. X:    local($start) = 0;
  1257. X:    local($., $_);
  1258. X:    while (<main'DATA>) {            # First move to start of offset table
  1259. X:        next if /^#/;
  1260. X:        last if /^$/ && ++$start > 2;    # Skip two blank line after end token
  1261. X:    }
  1262. X:    $start = tell(main'DATA);        # Offsets in table are relative to here
  1263. X:    local($key, $value);
  1264. X:    while (<main'DATA>) {            # Load the offset table
  1265. X:        last if /^$/;                # Ends with a single blank line
  1266. X:        ($key, $value) = split(' ');
  1267. X:        $Datapos{$key} = $value + $start;
  1268. X:    }
  1269. X:    $Datapos{$_[0]};        # All that pain to get this offset...
  1270. X:}
  1271. X:
  1272. XEOC
  1273. X    print &q(<<'EOC');
  1274. X:#
  1275. X:# The perl compiler stops here.
  1276. X:#
  1277. X:
  1278. X:__END__
  1279. X:
  1280. X:#
  1281. X:# Beyond this point lie functions we may never compile.
  1282. X:#
  1283. X:
  1284. XEOC
  1285. X    # Option -o directs us to optimize the function location by emitting an
  1286. X    # offset table, which lists all the position within DATA for each possible
  1287. X    # dataloaded routine.
  1288. X    if ($opt_o) {
  1289. X        print &q(<<'EOC');
  1290. X:#
  1291. X:# DO NOT CHANGE A IOTA BEYOND THIS COMMENT!
  1292. X:# The following table lists offsets of functions within the data section.
  1293. X:# Should modifications be needed, change original code and rerun perload
  1294. X:# with the -o option to regenerate a proper offset table.
  1295. X:#
  1296. X:
  1297. XEOC
  1298. X        $trailing_message = &q(<<'EOC');
  1299. X:
  1300. X:#
  1301. X:# End of offset table and beginning of dataloading section.
  1302. X:#
  1303. X:
  1304. XEOC
  1305. X        $pos = 0;            # Offset relative to this point (start of table)
  1306. X        foreach (@Data) {
  1307. X            $Datapos{"$1\'$2"} = $pos - $now
  1308. X                if /^sub\s+(\w+)'load_(\w+)\s*\{/;    # } for vi
  1309. X            $pos += length;
  1310. X        }
  1311. X        @poskeys = keys %Datapos;    # Array of routine names (fully qualified)
  1312. X
  1313. X        # Write out a formatted table, each entry stored on $entry bytes and
  1314. X        # formatted with the $format string.
  1315. X        ($entry, $format) = &get_format(*poskeys);
  1316. X
  1317. X        # The total size occupied by the table is the size of one item times
  1318. X        # the number of items plus the final trailing message at the end of
  1319. X        # the table.
  1320. X        $table_size = $entry * @poskeys + length($trailing_message);
  1321. X
  1322. X        # Output formatted table
  1323. X        foreach (sort @poskeys) {
  1324. X            printf($format, $_, $table_size + $Datapos{$_});
  1325. X        }
  1326. X        print $trailing_message;
  1327. X    }
  1328. X
  1329. X    # Output code for each dataloaded function
  1330. X    foreach (@Data) {
  1331. X        print;
  1332. X    }
  1333. X    print &q(<<'EOC');
  1334. X:#
  1335. X:# End of dataloading section.
  1336. X:#
  1337. X:
  1338. XEOC
  1339. X}
  1340. X
  1341. Xif (@auto > 0) {
  1342. X    mkdir('auto',0755) unless -d 'auto';
  1343. X    foreach $file (@auto) {
  1344. X        unless (open(AUTO, ">auto/$file")) {
  1345. X            warn "Can't create auto/$file: $!\n";
  1346. X            next;
  1347. X        }
  1348. X        print AUTO &q(<<'EOC');
  1349. X:# This file was generated by perload
  1350. X:
  1351. XEOC
  1352. X        print AUTO $Auto{$file};
  1353. X        close AUTO;
  1354. X    }
  1355. X}
  1356. X
  1357. X# Compute optimum format for routine offset table, returning both the size of
  1358. X# each entry and the formating string for printf.
  1359. Xsub get_format {
  1360. X    local(*names) = @_;
  1361. X    local($name_len) = 0;
  1362. X    local($max_len) = 0;
  1363. X    foreach (@names) {
  1364. X        $name_len = length;
  1365. X        $max_len = $name_len if $name_len > $max_len;
  1366. X    }
  1367. X    # The size of each entry (preceded by one tab, followed by 12 chars)
  1368. X    $name_len = $max_len + 1 + 12;
  1369. X    ($name_len, "\t%${max_len}s %10d\n");
  1370. X}
  1371. X
  1372. Xsub emit_init {
  1373. X    print &q(<<'EOC');
  1374. X:#
  1375. X:# This perl program uses dynamic loading [generated by perload]
  1376. X:#
  1377. X:
  1378. XEOC
  1379. X    $init_emitted = 1;
  1380. X}
  1381. X
  1382. Xsub flush_comment {
  1383. X    print @Comment if @Comment > 0;
  1384. X    @Comment = ();
  1385. X}
  1386. X
  1387. Xsub q {
  1388. X    local($_) = @_;
  1389. X    local($*) = 1;
  1390. X    s/^://g;
  1391. X    $_;
  1392. X}
  1393. X
  1394. X#
  1395. X# These next few lines are legal in both perl and nroff.
  1396. X#
  1397. X
  1398. X.00;        # finish .ig
  1399. X'di            \" finish diversion--previous line must be blank
  1400. X.nr nl 0-1    \" fake up transition to first page again
  1401. X.nr % 0        \" start at page 1
  1402. X'; __END__    \" the perl compiler stops here
  1403. X
  1404. X'''
  1405. X''' From here on it's a standard manual page.
  1406. X'''
  1407. X
  1408. X.TH PERLOAD 1 "June 20, 1992"
  1409. X.AT 3
  1410. X.SH NAME
  1411. Xperload \- builds up autoloaded and dataloaded perl scripts
  1412. X.SH SYNOPSIS
  1413. X.B perload
  1414. X[ \fB\-ot\fR ]
  1415. X[ \fIfile\fR ]
  1416. X.SH DESCRIPTION
  1417. X.I Perload
  1418. Xtakes a perl script as argument (or from stdin if no argument is supplied)
  1419. Xand prints out on stdout an equivalent script set-up to perform autoloading
  1420. Xor dataloading. The translation is directed by special comments within the
  1421. Xoriginal script. Using dynamic loading can drastically improve start-up
  1422. Xperformances, both in time and in memory, as perl does not need to compile
  1423. Xthe whole script nor store its whole compiled form in memory.
  1424. X.PP
  1425. X.I Autoloading
  1426. Xdelays compilation of some functions until they are needed. The code for these
  1427. Xfunctions is loaded dynamically at run-time. The atomicity of loading is a
  1428. Xfile, which means that putting more than one function into a file will cause
  1429. Xall these functions to be loaded and compiled as soon as one among them is
  1430. Xneeded.
  1431. X.PP
  1432. X.I Dataloading
  1433. Xis a form of autoloading where no extra file are needed. The script carries
  1434. Xall the functions whose compilation is to be delayed in its data segment
  1435. X(in the \fIperl\fR sense, i.e. they are accessible via the DATA filehandle).
  1436. XThe scripts parses the data segment and extracts only the code for the needed
  1437. Xsubroutine, which means granularity is better than with autloading.
  1438. X.PP
  1439. XIt is possible for a single script to use both autoloading and dataloading at
  1440. Xthe same time. However, it should be noted that a script using only dataloading
  1441. Xis self contained and can be moved or shared accross different platforms without
  1442. Xfear. On the contrary, a script using only autoloading relies on some externally
  1443. Xprovided files. Sharing this script among different platforms requires sharing
  1444. Xof these external files. The script itself cannot be redistributed without
  1445. Xalso giving the extra files holding the autoloaded functions.
  1446. X.PP
  1447. XThe major drawback with dataloading is that the DATA filehandle cannot be used
  1448. Xfor anything else and may result in code duplication when two scripts could
  1449. Xshare the same pieces of code. Autoloading appears as the perfect solution in
  1450. Xthis case since two scripts may freely share the same functions without
  1451. Xactually duplicating them on the disk (hence saving some precious disk blocks
  1452. X:-).
  1453. X.SH CRITERIA
  1454. XFunctions to be dataloaded or autoloaded must meet the following layout
  1455. Xcriteria:
  1456. X.TP 5
  1457. X\-
  1458. XThey must not be one-line functions like \fIsub sorter { $a <=> $b }\fR.
  1459. XThose functions are simply output verbatim, as they are already so
  1460. Xsmall that it would not be worth to dynamically load them,
  1461. X.TP
  1462. X\-
  1463. XThe first line must be of the form \fIsub routine_name {\fR, with an optional
  1464. Xcomment allowed after the '{'.
  1465. X.TP
  1466. X\-
  1467. XThe function definition must end with a single '}' character left aligned.
  1468. X.TP
  1469. X\-
  1470. XPackage directives outside any function must be left aligned.
  1471. X.PP
  1472. XAll the above restrictions should not be source of a problem if "standard"
  1473. Xwriting style is used. There are also some name restrictions: the package
  1474. Xname \fIperload\fR is reserved, as is the \fI@AUTO\fR array when autoloading
  1475. Xis used. Packages must not start with \fIauto_\fR, as this is prepended to
  1476. Xuser's package names when building the stubs. Furthermore, the subroutines
  1477. Xnames \fImain'autoload\fR and
  1478. X\fImain'dataload\fR must not be used by the original script. Again, these
  1479. Xshould not cause any grief.
  1480. X.SH DIRECTIVES
  1481. XThe translation performed by
  1482. X.I Perload
  1483. Xis driven by some special comment directives placed directly within the code.
  1484. XEnding those directives with a ':' character will actually prevent them from
  1485. Xbeing output into the produced script. Case is irrelevant for all the directives
  1486. Xand the comment need not be left-aligned, although it must be the first
  1487. Xnon-space item on the line.
  1488. X.PP
  1489. XThe following directives are available:
  1490. X.TP 10
  1491. X# Perload ON
  1492. XTurns on the \fIperload\fR processing. Any function definition which meets
  1493. Xthe criteria listed in the previous section will be replaced by two stubs and
  1494. Xits actual definition will be rejected into the data segment (default) or a
  1495. Xfile when inside an autoloading section.
  1496. X.TP
  1497. X# Perload OFF
  1498. XTurns off any processing. The script is written as-is on the standard output.
  1499. X.TP
  1500. X# Autoload \fIpath\fR
  1501. XRequests autoloading from file \fIpath\fR, which may be an absolute path or
  1502. Xa relative path. The file will be located at run-time using the @AUTO array
  1503. Xif a non-absolute path is supplied or if the file does not exist as listed.
  1504. XAutoloading directives may be nested.
  1505. X.TP
  1506. X# Offload \fIpath\fR
  1507. XThe argument is not required. The directive ends the previous autoloading
  1508. Xdirective (the inmost one). This does not turn off the \fIperload\fR processing
  1509. Xthough. The \fIpath\fR name is optional here (in fact, it has only a comment
  1510. Xvalue).
  1511. X.SH OPTIONS
  1512. XPerload accepts only two options. Using \fB\-o\fR is meaningful only when
  1513. Xdataloading is used. It outputs an offset table which lists the relative
  1514. Xoffset of the dataloaded functions within the data section. This will spare
  1515. Xperl the run-time parsing needed to locate the function, and results in an good
  1516. Xspeed gain. However, it has one major drawback: it prevents people from
  1517. Xactually modifying the source beyond the start of the table. But anything
  1518. Xbefore can be freely edited, which is particulary useful when tailoring the
  1519. Xscript.
  1520. X.PP
  1521. XThis option should not be used when editing of functions within the data
  1522. Xsection is necessary for whatever reason. When \fB\-o\fR is used, any
  1523. Xchange in the dataloaded function must be committed by re-running perload
  1524. Xon the original script.
  1525. X.PP
  1526. XThe other option \fB\-t\fR is to be used when producing a script which is
  1527. Xgoing to run setuid. The body of the loaded function is untainted before being
  1528. Xfed to eval, which slightly slows down loading (the first time the function is
  1529. Xcalled), but avoids either an insecure dependency report or weird warnings from
  1530. Xtaintperl stating something is wrong (which is the behaviour with 4.0 PL35).
  1531. X.SH FILES
  1532. X.TP 10
  1533. Xauto
  1534. Xthe subdirectory where all produced autoloaded files are written.
  1535. X.SH ENVIRONMENT
  1536. XNo environment variables are used by \fIperload\fR. However, the autoloaded
  1537. Xversion of the script pays attention to the \fIAUTOLIB\fR variable as a colon
  1538. Xseparated set of directories where the to-be-loaded files are to be found
  1539. Xwhen a non-absolute path was specified. If the \fIAUTOLIB\fR variable is not
  1540. Xset, the default value 'auto:.' is used (i.e. look first in the auto/
  1541. Xsubdirectory, then in the current directory.
  1542. X.SH CAVEAT
  1543. XSpecial care is required when using an autoloading script, especially when
  1544. Xexecuted by the super-user: it would be very easy for someone to leave a
  1545. Xspecial version of a routine to be loaded, in the hope the super-user (or
  1546. Xanother suitable target) executes the autoloaded version of the script with
  1547. Xsome \fIad hoc\fR changes...
  1548. X.PP
  1549. XThe directory holding the to-be-loaded files should therefore be protected
  1550. Xagainst unauthorized access, and no file should have write permission on them.
  1551. XThe directory itself should not be world-writable either, or someone might
  1552. Xsubstitute his own version.
  1553. XIt should also be considered wise to manually set the @AUTO variable to a
  1554. Xsuitable value within the script itself.
  1555. X.PP
  1556. XThe \fB\-o\fR option uses \fIperl\fR's special variable \fI$/\fR with a
  1557. Xmulti-character value. I suspect this did not work with versions of \fIperl\fR
  1558. Xprior to 4.0, so any script using this optimized form of dataloading will not
  1559. Xbe 100% backward compatible.
  1560. X.SH AUTHOR
  1561. XRaphael Manfredi <ram@eiffel.com>
  1562. X.SH CREDITS
  1563. XValuable input came from Wayne H. Scott <wscott@ecn.purdue.edu>. He is
  1564. Xmerely the author of the optimizing offset table (\fB\-o\fR option).
  1565. X.PP
  1566. X.I Perload
  1567. Xis based on an article from Tom Christiansen <tchrist@convex.com>,
  1568. X.I Autoloading in Perl,
  1569. Xexplaining the concept of dataloading and giving a basic implementation.
  1570. X.SH "SEE ALSO"
  1571. Xperl(1).
  1572. END_OF_FILE
  1573.   if test 21206 -ne `wc -c <'bin/perload'`; then
  1574.     echo shar: \"'bin/perload'\" unpacked with wrong size!
  1575.   fi
  1576.   chmod +x 'bin/perload'
  1577.   # end of 'bin/perload'
  1578. fi
  1579. echo shar: End of archive 7 \(of 17\).
  1580. cp /dev/null ark7isdone
  1581. MISSING=""
  1582. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ; do
  1583.     if test ! -f ark${I}isdone ; then
  1584.     MISSING="${MISSING} ${I}"
  1585.     fi
  1586. done
  1587. if test "${MISSING}" = "" ; then
  1588.     echo You have unpacked all 17 archives.
  1589.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1590. else
  1591.     echo You still must unpack the following archives:
  1592.     echo "        " ${MISSING}
  1593. fi
  1594. exit 0
  1595. exit 0 # Just in case...
  1596.