home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / comp / sources / misc / 4104 < prev    next >
Encoding:
Text File  |  1992-11-19  |  54.7 KB  |  1,603 lines

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