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

  1. Newsgroups: comp.sources.misc
  2. From: Raphael Manfredi <ram@acri.fr>
  3. Subject: v41i010:  mailagent - Flexible mail filtering and processing package, v3.0, Part10/26
  4. Message-ID: <1993Dec2.133856.18496@sparky.sterling.com>
  5. X-Md4-Signature: 6438a2029ff359b8d7c5c5f2bc5f554f
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Advanced Computer Research Institute, Lyon, France.
  8. Date: Thu, 2 Dec 1993 13:38:56 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: Raphael Manfredi <ram@acri.fr>
  12. Posting-number: Volume 41, Issue 10
  13. Archive-name: mailagent/part10
  14. Environment: UNIX, Perl
  15. Supersedes: mailagent: Volume 33, Issue 93-109
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then feed it
  19. # into a shell via "sh file" or similar.  To overwrite existing files,
  20. # type "sh file -c".
  21. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  22. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  23. # Contents:  agent/magent.SH agent/pl/dbr.pl bin/perload
  24. #   misc/unkit/kitok.msg
  25. # Wrapped by ram@soft208 on Mon Nov 29 16:49:56 1993
  26. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  27. echo If this archive is complete, you will see the following message:
  28. echo '          "shar: End of archive 10 (of 26)."'
  29. if test -f 'agent/magent.SH' -a "${1}" != "-c" ; then 
  30.   echo shar: Will not clobber existing file \"'agent/magent.SH'\"
  31. else
  32.   echo shar: Extracting \"'agent/magent.SH'\" \(19725 characters\)
  33.   sed "s/^X//" >'agent/magent.SH' <<'END_OF_FILE'
  34. Xcase $CONFIG in
  35. X'')
  36. X    if test -f config.sh; then TOP=.;
  37. X    elif test -f ../config.sh; then TOP=..;
  38. X    elif test -f ../../config.sh; then TOP=../..;
  39. X    elif test -f ../../../config.sh; then TOP=../../..;
  40. X    elif test -f ../../../../config.sh; then TOP=../../../..;
  41. X    else
  42. X        echo "Can't find config.sh."; exit 1
  43. X    fi
  44. X    . $TOP/config.sh
  45. X    ;;
  46. Xesac
  47. Xcase "$0" in
  48. X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
  49. Xesac
  50. Xecho "Extracting agent/magent (with variable substitutions)"
  51. X$spitshell >magent <<!GROK!THIS!
  52. X$startperl
  53. X    eval 'exec perl -S \$0 "\$@"'
  54. X        if \$running_under_some_shell;
  55. X
  56. X# You'll need to set up a .forward file that feeds your mail to this script,
  57. X# via the filter. Mine looks like this:
  58. X#   "|exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"
  59. X
  60. X# $Id: magent.SH,v 3.0 1993/11/29 13:48:22 ram Exp ram $
  61. X#
  62. X#  Copyright (c) 1990-1993, Raphael Manfredi
  63. X#  
  64. X#  You may redistribute only under the terms of the Artistic License,
  65. X#  as specified in the README file that comes with the distribution.
  66. X#  You may reuse parts of this distribution only within the terms of
  67. X#  that same Artistic License; a copy of which may be found at the root
  68. X#  of the source tree for mailagent 3.0.
  69. X#
  70. X# $Log: magent.SH,v $
  71. X# Revision 3.0  1993/11/29  13:48:22  ram
  72. X# Baseline for mailagent 3.0 netwide release.
  73. X#
  74. X
  75. X# Perload ON
  76. X
  77. X#
  78. X# The following were determined by Configure...
  79. X#
  80. X
  81. X# Command used to compute hostname
  82. X\$phostname = '$phostname';
  83. X
  84. X# Our domain name
  85. X\$mydomain = '$mydomain';
  86. X
  87. X# Hidden network (advertised host)
  88. X\$hiddennet = '$hiddennet';
  89. X
  90. X# Directory where mail is spooled
  91. X\$maildir = '$maildir';
  92. X
  93. X# File in which mail is stored
  94. X\$mailfile = '$mailfile';
  95. X
  96. X# Current version number and patchlevel
  97. X\$mversion = '$VERSION';
  98. X\$patchlevel = '$PATCHLEVEL';
  99. X
  100. X# Want to lock mailboxes with flock ?
  101. X\$lock_by_flock = '$lock_by_flock';
  102. X
  103. X# Only use flock() and no .lock file
  104. X\$flock_only = '$flock_only';
  105. X
  106. X# Our organization name
  107. X\$orgname = '$orgname';
  108. X
  109. X# Private mailagent library
  110. X\$privlib = '$privlib';
  111. X
  112. X# News posting program
  113. X\$inews = '$inews';
  114. X
  115. X# Mail sending program
  116. X\$mailer = '$mailer';
  117. X
  118. X#
  119. X# End of configuration section.
  120. X#
  121. X!GROK!THIS!
  122. X
  123. X$spitshell >>magent <<'!NO!SUBS!'
  124. X
  125. X$prog_name = $0;                # Who I am
  126. X$prog_name =~ s|^.*/(.*)|$1|;    # Keep only base name
  127. X$has_option = 0;                # True if invoked with options
  128. X$nolock = 0;                    # Do we need to get a lock file?
  129. X$config_file = '~/.mailagent';    # Default configuration file
  130. X$log_level = -1;                # Changed by -L option
  131. X
  132. X# Calling the mailagent as 'mailqueue' lists the queue
  133. Xif ($prog_name eq 'mailqueue') {
  134. X    unshift(@ARGV, '-l');
  135. X}
  136. X
  137. X# Parse options
  138. Xwhile ($ARGV[0] =~ /^-/) {
  139. X    $_ = shift;
  140. X    last if /--/;
  141. X    if ($_ eq '-c') {        # Specify alternate configuration file
  142. X        ++$nolock;            # Immediate processing wanted
  143. X        $config_file = shift;
  144. X    }
  145. X    elsif ($_ eq '-d') {    # Dump rules
  146. X        ++$has_option;        # Incompatible with other special options
  147. X        ++$dump_rule;
  148. X    }
  149. X    elsif ($_ eq '-e') {    # Rule supplied on command line
  150. X        local($*) = 1;
  151. X        $_ = shift;
  152. X        s/\n/ /g;
  153. X        push(@Linerules, $_);
  154. X        ++$edited_rules;    # Signals rules came from command line
  155. X        ++$nolock;            # Immediate processing wanted
  156. X    }
  157. X    elsif ($_ eq '-f') {    # Take messages from UNIX mailbox
  158. X        ++$nolock;            # Immediate processing wanted
  159. X        ++$mbox_mail;
  160. X        $mbox_file = shift;    # -f followed by file name
  161. X    }
  162. X    elsif ($_ eq '-h') {    # Usage help
  163. X        &usage;
  164. X    }
  165. X    elsif ($_ eq '-i') {    # Interactive mode: log messages also on stderr
  166. X        *add_log = *stderr_log;
  167. X    }
  168. X    elsif ($_ eq '-l') {    # List queue
  169. X        ++$has_option;        # Incompatible with other special options
  170. X        ++$list_queue;
  171. X        ++$norule;            # No need to compile rules
  172. X    }
  173. X    elsif ($_ eq '-o') {    # Overwrite configuration variable
  174. X        ++$nolock;            # Immediate processing wanted
  175. X        $over_config .= "\n" . shift;
  176. X    }
  177. X    elsif ($_ eq '-q') {    # Process the queue
  178. X        ++$has_option;        # Incompatible with other special options
  179. X        ++$run_queue;
  180. X    }
  181. X    elsif ($_ eq '-r') {    # Specify alternate rule file
  182. X        ++$nolock;            # Immediate processing wanted
  183. X        $rule_file = shift;
  184. X    }
  185. X    elsif (/^-s(\S*)/) {    # Print statistics
  186. X        ++$has_option;        # Incompatible with other special options
  187. X        ++$stats;
  188. X        ++$norule;            # No need to compile rules
  189. X        $stats_opt = $1;
  190. X    }
  191. X    elsif ($_ eq '-t') {    # Track rule matches on stdout
  192. X        ++$track_all;
  193. X    }
  194. X    elsif ($_ eq '-L') {    # Specify new logging level
  195. X        $log_level = int(shift);
  196. X    }
  197. X    elsif ($_ eq '-V') {    # Version number
  198. X        print STDERR "$prog_name $mversion PL$patchlevel\n";
  199. X        exit 0;
  200. X    }
  201. X    elsif ($_ eq '-TEST') {    # Mailagent run via TEST (undocumented feature)
  202. X        ++$test_mode;
  203. X    }
  204. X    else {
  205. X        print STDERR "$prog_name: unknown option: $_\n";
  206. X        &usage;
  207. X    }
  208. X}
  209. X
  210. X++$nolock if $has_option;        # No need to take a lock with special options
  211. X
  212. X# Only one option at a time (among those options which change our goal)
  213. Xif ($has_option > 1) {
  214. X    print STDERR "$prog_name: at most one special option may be specified.\n";
  215. X    exit 1;
  216. X}
  217. X
  218. X$file_name = shift;                # File name to be processed (null if stdin)
  219. X$ENV{'IFS'}='' if $ENV{'IFS'};    # Shell separation field
  220. X&get_configuration;                # Get a suitable configuration package (cf)
  221. Xselect(STDOUT);                    # Because the -t option writes on STDOUT,
  222. X$| = 1;                            # make sure it is flushed before we fork()
  223. X$agent_wait = "agent.wait";        # Waiting file for out-of-the-queue mails
  224. X$privlib = "$cf'home/../.." if $test_mode;    # Tests ran from test/out
  225. X
  226. X$orgname = &tilda_expand($orgname);        # Perform run-time ~name substitution
  227. X
  228. Xif ($orgname =~ m|^/|) {        # Name of organization kept in file
  229. X    unless (open(ORG, $orgname)) {
  230. X        &add_log("ERROR cannot read $orgname: $!") if $loglvl;
  231. X    } else {
  232. X        chop($orgname = <ORG>);
  233. X        close ORG;
  234. X    }
  235. X}
  236. X
  237. X$ENV{'HOME'} = $cf'home;
  238. X$ENV{'USER'} = $cf'user;
  239. X$ENV{'NAME'} = $cf'name;
  240. X$baselock = "$cf'spool/perl";    # This file does not exist
  241. X$lockext = ".lock";                # Extension used by lock routines
  242. X$lockfile = $baselock . $lockext;
  243. X
  244. Xumask(077);                        # Files we create are private ones
  245. X$jobnum = &jobnum;                # Compute a job number
  246. X
  247. X# Allow only ONE mailagent at a time (resource consumming)
  248. X&checklock($baselock);            # Make sure old locks do not remain
  249. Xunless (-f $lockfile) {
  250. X    # Try to get the lock file (acting as a token). We do not need locking if
  251. X    # we have been invoked with an option and that option is not -q.
  252. X    if ($nolock && !$run_queue) {
  253. X        &add_log("no need to get a lock") if $loglvl > 19;
  254. X    } elsif (0 == &acs_rqst($baselock)) {
  255. X        &add_log("got the right to process mail") if $loglvl > 19;
  256. X        ++$locked;
  257. X    } else {
  258. X        &add_log("denied right to process mail") if $loglvl > 19;
  259. X    }
  260. X}
  261. X
  262. Xif (!$locked && !$nolock) {
  263. X    # Another mailagent is running somewhere
  264. X    &queue_mail($file_name);
  265. X    exit 0;
  266. X}
  267. X
  268. X# Initialize mail filtering and compile filter rule if necessary
  269. X&init_all;
  270. X&compile_rules unless $norule;
  271. X
  272. X# If rules are to be dumped, this is the only action
  273. Xif ($dump_rule) {
  274. X    &dump_rules(*print_rule_number, *void_func);
  275. X    unlink $lockfile if $locked;
  276. X    exit 0;
  277. X}
  278. X
  279. X# Likewise, statistics dumping is the only option
  280. Xif ($stats) {
  281. X    &report_stats($stats_opt);
  282. X    unlink $lockfile if $locked;
  283. X    exit 0;
  284. X}
  285. X
  286. X# Listing the queue is also the only performed action
  287. Xif ($list_queue) {
  288. X    &list_queue;
  289. X    unlink $lockfile if $locked;
  290. X    exit 0;
  291. X}
  292. X
  293. X# Taking messages from mailbox file
  294. Xif ($mbox_mail) {
  295. X    ++$run_queue if 0 == &mbox_mail($mbox_file);
  296. X    unless ($run_queue) {
  297. X        unlink $lockfile if $locked;
  298. X        exit 1;        # -f failed
  299. X    }
  300. X    &add_log("processing queued mails") if $loglvl > 15;
  301. X}
  302. X
  303. X# Suppress statistics when mailagent invoked manually (i.e. not in test mode)
  304. X&no_stats if $nolock && !$test_mode;
  305. X
  306. X&read_stats;                    # Load statistics into memory for fast update
  307. X&newcmd'load if $cf'newcmd;        # Load user-defined command definitions
  308. X
  309. Xif (!$run_queue) {                # Do not enter here if -q
  310. X    if (0 != &analyze_mail($file_name)) {    # Analyze the mail
  311. X        &add_log("ERROR while processing main message--queing it")
  312. X            if ($loglvl > 0);
  313. X        &queue_mail($file_name);
  314. X        unlink $lockfile;
  315. X        exit 0;                    # Do not continue
  316. X    } else {
  317. X        $file = $file_name;        # Never corrupt $file_name
  318. X        $file =~ s|.*/(.*)|$1|;    # Keep only basename
  319. X        $file = "<stdin>" if $file eq '';
  320. X        &add_log("FILTERED [$file] $Header{'Length'} bytes") if $loglvl > 4;
  321. X    }
  322. X}
  323. X
  324. Xunless ($test_mode) {
  325. X    # Fork a child: we have to take care of the filter script which is waiting
  326. X    # for us to finish processing of the delivered mail.
  327. X    &fork_child() unless $run_queue;
  328. X
  329. X    # From now on, we are in the child process... Don't sleep at all if logging
  330. X    # level is greater that 11 or if $run_queue is true. Logging level of 12
  331. X    # and higher are for debugging and should not be used on a permanent basis
  332. X    # anyway.
  333. X
  334. X    $sleep = 1;                    # Give others a chance to queue their mail
  335. X    $sleep = 0 if $loglvl > 11 || $run_queue;
  336. X
  337. X    while (&pqueue) {            # Eventually process the queue
  338. X        sleep 30 if $sleep;        # Wait in case new mail arrives
  339. X    }
  340. X} else {
  341. X    &pqueue;                    # Process the queue once in test mode
  342. X}
  343. X
  344. X# End of mailagent processing
  345. X&write_stats;                    # Resynchronizes the statistics file
  346. X&compress'recompress;            # Compress some of the folders we delivered to
  347. X&contextual_operations;            # Perform all the contextual operations
  348. X&add_log("mailagent exits") if $loglvl > 17;
  349. Xunlink $lockfile if $locked;
  350. Xexit 0;
  351. X
  352. X# Print usage and exit
  353. Xsub usage {
  354. X    print STDERR <<EOF;
  355. XUsage: $prog_name [-dhilqtV] [-s{umary}] [-f file] [-e rules] [-c config]
  356. X       [-L level] [-r file] [-o def] [mailfile]
  357. X  -c : specify alternate configuration file.
  358. X  -d : dump filter rules (special).
  359. X  -e : enter rules to be applied.
  360. X  -f : get messages from UNIX-style mailbox file.
  361. X  -h : print this help message and exits.
  362. X  -i : interactive usage -- print log messages on stderr.
  363. X  -l : list message queue (special).
  364. X  -L : force logging level.
  365. X  -o : overwrite config file with supplied definition.
  366. X  -q : process the queue (special).
  367. X  -r : sepcify alternate rule file.
  368. X  -s : report gathered statistics (special).
  369. X  -t : track rules on stdout.
  370. X  -V : print version number and exits.
  371. XEOF
  372. X    exit 1;
  373. X}
  374. X
  375. X# Read configuration file and alter it with the values specified via -o.
  376. X# Then apply -r and -t by modifying suitable configuration parameters.
  377. Xsub get_configuration {
  378. X    &read_config($config_file);        # Read configuration file and set vars
  379. X    &cf'parse($over_config);        # Overwrite with command line options
  380. X    $cf'rules = $rule_file if $rule_file;        # -r overwrites rule file
  381. X    $loglvl = $log_level if $log_level >= 0;    # -L overwrites logging level
  382. X}
  383. X
  384. X#
  385. X# The filtering routines
  386. X#
  387. X
  388. X# Start-up initializations
  389. Xsub init_all {
  390. X    &init_signals;        # Trap common signals
  391. X    &init_constants;    # Constants definitions
  392. X    &init_interpreter;    # Initialize tables %Priority, %Function, ...
  393. X    &init_env;            # Initialize the %XENV array
  394. X    &init_matcher;        # Initialize special matching functions
  395. X    &init_pseudokey;    # Initialize the pseudo header keys for H table
  396. X    &init_builtins;        # Initialize built-in commands like @RR
  397. X    &init_filter;        # Initialize filter commands
  398. X    &init_special;        # Initialize special user table %Special
  399. X}
  400. X
  401. X# Protect ourselves (trap common signals)
  402. Xsub init_signals {
  403. X    $SIG{'HUP'} = 'emergency';
  404. X    $SIG{'INT'} = 'emergency';
  405. X    $SIG{'QUIT'} = 'emergency';
  406. X    $SIG{'PIPE'} = 'emergency';
  407. X    $SIG{'IO'} = 'emergency';
  408. X    $SIG{'BUS'} = 'emergency';
  409. X    $SIG{'ILL'} = 'emergency';
  410. X    $SIG{'SEGV'} = 'emergency';
  411. X    $SIG{'ALRM'} = 'emergency';
  412. X    $SIG{'TERM'} = 'emergency';
  413. X}
  414. X
  415. X# Constants definitions
  416. Xsub init_constants {
  417. X    require 'ctime.pl';
  418. X    # Values for flock(), usually in <sys/file.h>
  419. X    $LOCK_SH = 1;                # Request a shared lock on file
  420. X    $LOCK_EX = 2;                # Request an exclusive lock
  421. X    $LOCK_NB = 4;                # Make a non-blocking lock request
  422. X    $LOCK_UN = 8;                # Unlock the file
  423. X
  424. X    # Status used by filter
  425. X    $FT_RESTART = 0;            # Abort current action, restart from scratch
  426. X    $FT_CONT = 1;                # Continue execution
  427. X    $FT_REJECT = 2;                # Abort current action, continue filtering
  428. X    $FT_ABORT = 3;                # Abort filtering process
  429. X
  430. X    # Shall we append or remove folder?
  431. X    $FOLDER_APPEND = 0;            # Append in folder
  432. X    $FOLDER_REMOVE = 1;            # Remove folder
  433. X
  434. X    # Used by shell_command and children
  435. X    $NO_INPUT = 0;                # No input (stdin is closed)
  436. X    $BODY_INPUT = 1;            # Give body of mail as stdin
  437. X    $MAIL_INPUT = 2;            # Pipe the whole mail
  438. X    $HEADER_INPUT = 3;            # Pipe the header only
  439. X    $NO_FEEDBACK = 0;            # No feedback wanted
  440. X    $FEEDBACK = 1;                # Feed result of command back into %Header
  441. X    
  442. X    # The filter message
  443. X    local($address) = &email_addr;
  444. X    $FILTER =
  445. X        "X-Filter: mailagent [version $mversion PL$patchlevel] for $address";
  446. X    $MAILER =
  447. X        "X-Mailer: mailagent [version $mversion PL$patchlevel]";
  448. X
  449. X    # For header fields alteration
  450. X    $HD_STRIP = 0;                # Strip header fields
  451. X    $HD_KEEP = 1;                # Keep header fields
  452. X
  453. X    # Faked leading From line (used for digest items, by SPLIT)
  454. X    local($now) = &ctime(time);
  455. X    chop($now);
  456. X    $FAKE_FROM = "From mailagent " . $now;
  457. X}
  458. X
  459. X# Initializes environment. All the variables are initialized in XENV array
  460. X# The sole purpose of XENV is to be able to know what changes wrt the invoking
  461. X# environment when dumping the rules. It also avoid modifying the environment
  462. X# for our children.
  463. Xsub init_env {
  464. X    foreach (keys(%ENV)) {
  465. X        $XENV{$_} = $ENV{$_};
  466. X    }
  467. X}
  468. X
  469. X# List of special header keys which do not represent a true header field.
  470. Xsub init_pseudokey {
  471. X    %Pseudokey = (
  472. X        'Body', 1,
  473. X        'Head', 1,
  474. X        'All', 1
  475. X    );
  476. X}
  477. X
  478. X#
  479. X# Miscellaneous utilities
  480. X#
  481. X
  482. X# Attempts a mailbox locking. The argument is the name of the file, the file
  483. X# descriptor is the global MBOX, opened for appending.
  484. Xsub mbox_lock {
  485. X    local($file) = @_;                # File name
  486. X    unless ($flock_only) {            # Lock with .lock
  487. X        if (0 != &acs_rqst($file)) {
  488. X            &add_log("WARNING could not lock $file") if $loglvl > 5;
  489. X        }
  490. X    }
  491. X    # Make sure the file is still there and as not been removed while we were
  492. X    # waiting for the lock (in which case our MBOX file descriptor would be
  493. X    # useless: we would write in a ghost file!). This could happen when 'elm'
  494. X    # (or other mail user agent) resynchronizes the mailbox.
  495. X    close MBOX;
  496. X    if (open(MBOX, ">>$file")) {
  497. X        if ($lock_by_flock) {
  498. X            unless (eval 'flock(MBOX, $LOCK_EX)') {    # Ask for exclusive lock
  499. X                &add_log("WARNING could not flock $file: $!") if $loglvl > 5;
  500. X            }
  501. X        }
  502. X    } else {
  503. X        &fatal("could not reopen $file");
  504. X    }
  505. X    seek(MBOX, 0, 2);                # Someone may have appended something
  506. X}
  507. X
  508. X# Remove lock on mailbox and return a failure status if closing failed
  509. Xsub mbox_unlock {
  510. X    local($file) = @_;                # File name
  511. X    local($status);                    # Error status from close
  512. X    $status = close(MBOX);            # Closing will remove flock lock
  513. X    &free_file($file) unless $flock_only;        # Remove the .lock
  514. X    $status ? 0 : 1;                # Return 0 for ok, 1 if close failed
  515. X}
  516. X
  517. X# Computes the e-mail address of the user
  518. Xsub email_addr {
  519. X    $cf'user . '@' . &domain_addr;        # E-mail address in internet format
  520. X}
  521. X
  522. X# Domain name address for current host
  523. Xsub domain_addr {
  524. X    local($_);                            # Our host name
  525. X    $_ = $hiddennet if $hiddennet ne '';
  526. X    if ($_ eq '') {
  527. X        $_ = &hostname;                    # Must fork to get hostname, grr...
  528. X        $_ .= $mydomain unless /\./;    # We want something fully qualified
  529. X    }
  530. X    $_;
  531. X}
  532. X
  533. X# Strip out leading path to home directory and replace it by a ~
  534. Xsub tilda {
  535. X    local($path) = @_;                    # Path we wish to shorten
  536. X    local($home) = $cf'home;
  537. X    $home =~ s/(\W)/\\$1/g;                # Escape possible meta-characters
  538. X    $path =~ s/^$home/~/;                # Replace the home directory by ~
  539. X    $path;                                # Return possibly stripped path
  540. X}
  541. X
  542. X# Compute the system mailbox file name
  543. Xsub mailbox_name {
  544. X    # If ~/.mailagent provides us with a mail directory, use it and possibly
  545. X    # override value computed by Configure.
  546. X    $maildir = $cf'maildrop if $cf'maildrop ne '';
  547. X    # If Configure gave a valid 'maildir', use it. Otherwise compute one now.
  548. X    unless ($maildir ne '' && -d "$maildir") {
  549. X        $maildir = "/usr/spool/mail";        # Default spooling area
  550. X        -d "/usr/mail" && ($maildir = "/usr/mail");
  551. X        -d "$maildir" || ($maildir = "$cf'home");
  552. X    }
  553. X    local($mbox) = $cf'user;                    # Default mailbox file name
  554. X    $mbox = $cf'mailbox if $cf'mailbox ne '';    # Priority to config variable
  555. X    $mailbox = "$maildir/$mbox";                # Full mailbox path
  556. X    if (! -f "$mailbox" && ! -w "$maildir") {
  557. X        # No mailbox already exists and we can't write in the spool directory.
  558. X        # Use mailfile then, and if we can't write in the directory and the
  559. X        # mail file does not exist either, use ~/mbox.$cf'user as mailbox.
  560. X        $mailbox = $mailfile;        # Determined by configure (%~ and %L form)
  561. X        $mailbox =~ s/%~/$cf'home/go;    # %~ stands for the user directory
  562. X        $mailbox =~ s/%L/$cf'user/go;    # %L stands for the user login name
  563. X        $mailbox =~ m|(.*)/.*|;            # Extract dirname
  564. X        $mailbox = "$cf'home/mbox.$cf'user" unless (-f "mailbox" || -w "$1");
  565. X        &add_log("WARNING using $mailbox for mailbox") if $loglvl > 5;
  566. X    }
  567. X    $mailbox;
  568. X}
  569. X
  570. X# Fork a new mailagent and update the pid in the perl.lock file. The parent
  571. X# then exits and the child continues. This enables the filter which invoked
  572. X# us to finally exit.
  573. Xsub fork_child {
  574. X    local($pid) = fork;
  575. X    if ($pid == -1) {                # We cannot fork, exit.
  576. X        &add_log("ERROR couldn't fork to process the queue") if $loglvl > 5;
  577. X        unlink $lockfile if $locked;
  578. X        exit 0;
  579. X    } elsif ($pid == 0) {            # The child process
  580. X        # Update the pid in the perl.lock file, so that any process which will
  581. X        # use the kill(pid, 0) feature to check whether we are alive or not will
  582. X        # get a meaningful status.
  583. X        if ($locked) {
  584. X            chmod 0644, $lockfile;
  585. X            open(LOCK, ">$lockfile");    # Ignore errors
  586. X            chmod 0444, $lockfile;        # Now it's open, so we may restore mode
  587. X            print LOCK "$$\n";            # Write child's PID
  588. X            close LOCK;
  589. X        }
  590. X        sleep(2);                    # Give filter time to clean up
  591. X    } else {                        # Parent process
  592. X        exit 0;                        # Exit without removing lock, of course
  593. X    }
  594. X    # Only the child comes here and returns
  595. X    &add_log("mailagent continues") if $loglvl > 17;
  596. X}
  597. X
  598. X# Report any eval error and returns 1 if error detected.
  599. Xsub eval_error {
  600. X    if ($@ ne '') {
  601. X        $@ =~ s/ in file \(eval\) at line \d+//;
  602. X        chop($@);
  603. X        &add_log("ERROR $@") if $loglvl > 1;
  604. X    }
  605. X    $@ eq '' ? 0 : 1;
  606. X}
  607. X
  608. X!NO!SUBS!
  609. X$grep -v '^;#' pl/jobnum.pl >>magent
  610. X$grep -v '^;#' pl/read_conf.pl >>magent
  611. X$grep -v '^;#' pl/acs_rqst.pl >>magent
  612. X$grep -v '^;#' pl/free_file.pl >>magent
  613. X$grep -v '^;#' pl/add_log.pl >>magent
  614. X$grep -v '^;#' pl/checklock.pl >>magent
  615. X$grep -v '^;#' pl/lexical.pl >>magent
  616. X$grep -v '^;#' pl/parse.pl >>magent
  617. X$grep -v '^;#' pl/analyze.pl >>magent
  618. X$grep -v '^;#' pl/runcmd.pl >>magent
  619. X$grep -v '^;#' pl/filter.pl >>magent
  620. X$grep -v '^;#' pl/matching.pl >>magent
  621. X$grep -v '^;#' pl/locate.pl >>magent
  622. X$grep -v '^;#' pl/rfc822.pl >>magent
  623. X$grep -v '^;#' pl/macros.pl >>magent
  624. X$grep -v '^;#' pl/header.pl >>magent
  625. X$grep -v '^;#' pl/actions.pl >>magent
  626. X$grep -v '^;#' pl/stats.pl >>magent
  627. X$grep -v '^;#' pl/queue_mail.pl >>magent
  628. X$grep -v '^;#' pl/pqueue.pl >>magent
  629. X$grep -v '^;#' pl/builtins.pl >>magent
  630. X$grep -v '^;#' pl/rules.pl >>magent
  631. X$grep -v '^;#' pl/period.pl >>magent
  632. X$grep -v '^;#' pl/eval.pl >>magent
  633. X$grep -v '^;#' pl/dbr.pl >>magent
  634. X$grep -v '^;#' pl/history.pl >>magent
  635. X$grep -v '^;#' pl/once.pl >>magent
  636. X$grep -v '^;#' pl/makedir.pl >>magent
  637. X$grep -v '^;#' pl/emergency.pl >>magent
  638. X$grep -v '^;#' pl/listqueue.pl >>magent
  639. X$grep -v '^;#' pl/mbox.pl >>magent
  640. X$grep -v '^;#' pl/context.pl >>magent
  641. X$grep -v '^;#' pl/extern.pl >>magent
  642. X$grep -v '^;#' pl/mailhook.pl >>magent
  643. X$grep -v '^;#' pl/interface.pl >>magent
  644. X$grep -v '^;#' pl/getdate.pl >>magent
  645. X$grep -v '^;#' pl/include.pl >>magent
  646. X$grep -v '^;#' pl/plural.pl >>magent
  647. X$grep -v '^;#' pl/hostname.pl >>magent
  648. X$grep -v '^;#' pl/mmdf.pl >>magent
  649. X$grep -v '^;#' pl/compress.pl >>magent
  650. X$grep -v '^;#' pl/newcmd.pl >>magent
  651. X$grep -v '^;#' pl/q.pl >>magent
  652. X$grep -v '^;#' pl/hook.pl >>magent
  653. X$grep -v '^;#' pl/secure.pl >>magent
  654. X$grep -v '^;#' pl/cmdserv.pl >>magent
  655. X$grep -v '^;#' pl/power.pl >>magent
  656. X$grep -v '^;#' pl/file_edit.pl >>magent
  657. X$grep -v '^;#' pl/dynload.pl >>magent
  658. X$grep -v '^;#' pl/gensym.pl >>magent
  659. X$grep -v '^;#' pl/usrmac.pl >>magent
  660. X$grep -v '^;#' pl/tilde.pl >>magent
  661. X$grep -v '^;#' pl/mh.pl >>magent
  662. Xchmod 755 magent
  663. X$eunicefix magent
  664. END_OF_FILE
  665.   if test 19725 -ne `wc -c <'agent/magent.SH'`; then
  666.     echo shar: \"'agent/magent.SH'\" unpacked with wrong size!
  667.   fi
  668.   chmod +x 'agent/magent.SH'
  669.   # end of 'agent/magent.SH'
  670. fi
  671. if test -f 'agent/pl/dbr.pl' -a "${1}" != "-c" ; then 
  672.   echo shar: Will not clobber existing file \"'agent/pl/dbr.pl'\"
  673. else
  674.   echo shar: Extracting \"'agent/pl/dbr.pl'\" \(10634 characters\)
  675.   sed "s/^X//" >'agent/pl/dbr.pl' <<'END_OF_FILE'
  676. X;# $Id: dbr.pl,v 3.0 1993/11/29 13:48:39 ram Exp ram $
  677. X;#
  678. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  679. X;#  
  680. X;#  You may redistribute only under the terms of the Artistic License,
  681. X;#  as specified in the README file that comes with the distribution.
  682. X;#  You may reuse parts of this distribution only within the terms of
  683. X;#  that same Artistic License; a copy of which may be found at the root
  684. X;#  of the source tree for mailagent 3.0.
  685. X;#
  686. X;# $Log: dbr.pl,v $
  687. X;# Revision 3.0  1993/11/29  13:48:39  ram
  688. X;# Baseline for mailagent 3.0 netwide release.
  689. X;#
  690. X;# 
  691. X;# This is a simple database. Items are sorted by key, and have a tag
  692. X;# associated with it. Both are necessary to access the database. Every record
  693. X;# also carries a time stamp and associated values.
  694. X;#
  695. X;# The hashing is done like that: If the key is shorter than two characters,
  696. X;# an X is appended. Then, let 'a' and 'b' be the first and second character of
  697. X;# the name. Then the file 'b' is stored under directory 'a', and in 'b' there
  698. X;# are entries with the following format (separtion is the TAB character).
  699. X;#
  700. X;#     key tag timestamp <values>
  701. X;#
  702. Xpackage dbr;
  703. X
  704. X# Compute the relative path under the once directory for a given name
  705. Xsub hash_path {
  706. X    local($hname) = @_;
  707. X    # Ensure at least 2 characters. Fill in missing chars with 'X'.
  708. X    $hname .= "X" if (length($hname) < 2);
  709. X    $hname .= "X" if (length($hname) < 2);
  710. X    $hname =~ s/[^A-Za-z0-9_]/X/g;    # Don't want funny chars in path name
  711. X    # Get only the 2 first characters
  712. X    local(@chars) = split(//, substr($hname, 0, 2));
  713. X    '/' . join('/', @chars);
  714. X}
  715. X
  716. X# Fetch the entry in a dbr file and return the value of the timestamp and
  717. X# the line number in the file. Return (0,0) if no previous record was found
  718. X# for the name/tag association. An error is signaled by (-1,0). A line number
  719. X# different from 0, as in (0, 10), indicates that an entry was found but the
  720. X# selection did not succeed. Note that the timestamp returned is > 0 iff the
  721. X# entry was found and the selection was done completely.
  722. X# All the attached values are returned at the end of the list. It is possible
  723. X# to filter among those values by specifying a list of regular expressions, at
  724. X# the end of the argument list. An empty regular expression means the item is
  725. X# not to be filtered on (equivalent of '/.*/'). Expressions provided are
  726. X# taken as exact values to be matched against unless they start with '/' or '&'.
  727. X# A '/' denotes a regular expression to be applied, whilst '&' denotes function
  728. X# to be called with the actual value argument: function should return zero
  729. X# for rejection or any other value for selection.
  730. Xsub info {
  731. X    local($hname, $tag, @what) = @_;
  732. X    local($file);                        # DBR file associated with '$hname'
  733. X    local(@values);                        # Attached values to the item
  734. X    local($_);
  735. X    ($hname, $tag) = &default($hname, $tag);
  736. X    $file = $cf'hashdir . &hash_path($hname);
  737. X    return (0,0) unless -f "$file";
  738. X    unless (open(DBR, $file)) {
  739. X        &'add_log("ERROR could not open dbr file $file: $!") if $'loglvl;
  740. X        return (-1, 0);
  741. X    }
  742. X    local($linenum) = 0;                # Value of line if found
  743. X    local($timestamp) = 0;                # Associated time stamp
  744. X    &'acs_rqst($file);                    # Lock file (avoid concurrent updating)
  745. X    while (<DBR>) {
  746. X        if (s/^(\S+)\s([\w-]+)\s(\d+)\t*//) {
  747. X            next unless $1 eq $hname;
  748. X            next unless $2 eq $tag;
  749. X            $linenum = $.;                # Record line number
  750. X            $timestamp = int($3);        # And timestamp
  751. X            last if &match;                # Found it if matches @what filter
  752. X            $timestamp = 0;                # Not found yet
  753. X        } else {                        # Invalid entry
  754. X            &'add_log("ERROR $file corrupted, line $.") if $'loglvl;
  755. X            $timestamp = -1;            # Signals error
  756. X            last;                        # Abort processing
  757. X        }
  758. X    }
  759. X    &'free_file($file);                    # Remove lock on file
  760. X    close DBR;                            # Close file
  761. X    ($timestamp, $linenum, @values);    # Return item information
  762. X}
  763. X
  764. X# Apply match from @what, and fill in @values as a side effect if matched.
  765. Xsub match {
  766. X    local(@target) = split(/\t|\n/);    # Get values from line
  767. X    local($idx) = -1;                    # Index within @target
  768. X    local($matched) = 1;                # Assume selection will match
  769. X    local($res);                        # Eval result
  770. X    local($@);                            # Eval error report string
  771. X    foreach $what (@what) {
  772. X        $idx++;                            # Advance in @target
  773. X        next if $what eq '';            # Skip empty selection
  774. X        if ($what =~ m|^/|) {            # Regular expression
  775. X            $res = eval '$target[$idx] =~ ' . $what;
  776. X            &'add_log("WARNING dbr error: $@") if $@ && $'loglvl > 5;
  777. X            next if $@;
  778. X            $matched = $res;
  779. X        } elsif ($what =~ m|^&|) {        # Function to apply
  780. X            $res = eval "$what('" . $target[$idx] . "')";
  781. X            &'add_log("WARNING dbr error: $@") if chop($@) && $'loglvl > 5;
  782. X            next if $@;
  783. X            $matched = $res;
  784. X        } else {                        # Regular string comparaison
  785. X            $matched = $target[$idx] eq $what;
  786. X        }
  787. X        last unless $matched;
  788. X    }
  789. X    @values = @target if $matched;        # Fill in values if selection ok
  790. X    $matched;                            # Return matching status
  791. X}
  792. X
  793. X# Update the entry ($hname, $tag) in file to hold the current timestamp. If the
  794. X# $linenum parameter is non-null, we know we may copy the old file until that
  795. X# line (excluded), then replace the current line with the new timestamp.
  796. X# If $linenum is null, then we may safely append the entry in the file. If
  797. X# the $linenum parameter is 'undef', then the user does not have it precomputed
  798. X# or wishes to have the line number re-computed.
  799. X# The new values held in @values replace the old ones for the entry. If 'undef'
  800. X# is given instead, then the corresponding entry is deleted from the database.
  801. Xsub update {
  802. X    local($hname, $tag, $linenum, @values) = @_;
  803. X    local($now) = time;                    # Current time
  804. X    local($file);                        # DBR file associated with '$hname'
  805. X    local($_);
  806. X    ($hname, $tag) = &default($hname, $tag);
  807. X    $file = $cf'hashdir . &hash_path($hname);
  808. X    unless (-f "$file") {
  809. X        local($dirname) = $file =~ m|^(.*)/.*|;
  810. X        &'makedir($dirname);
  811. X    }
  812. X    $linenum = (&info($hname, $tag))[1] unless defined($linenum);
  813. X    if ($linenum == 0) {                # No entry previously recorded
  814. X        return unless defined(@values);    # Nothing to delete
  815. X        unless(open(DBR, ">>$file")) {
  816. X            &'add_log("ERROR cannot append in $file: $!") if $'loglvl;
  817. X            return;
  818. X        }
  819. X        &'acs_rqst($file);                # Lock file (avoid concurrent updating)
  820. X        print DBR "$hname $tag $now\t";    # The name, command tag and timestamp
  821. X        print DBR join("\t", @values);    # Associated values
  822. X        print DBR "\n";
  823. X        close DBR;
  824. X        &'free_file($file);                # Remove lock on file
  825. X    } else {                            # An entry existed already
  826. X        unless (open(DBR, ">$file.x")) {
  827. X            &'add_log("ERROR cannot create $file.x: $!") if $'loglvl;
  828. X            return;
  829. X        }
  830. X        unless (open(OLD, "$file")) {
  831. X            &'add_log("ERROR couldn't reopen $file: $!") if $'loglvl;
  832. X            close DBR;
  833. X            return;
  834. X        }
  835. X        &'acs_rqst($file);                # Lock file (avoid concurrent updating)
  836. X        while (<OLD>) {
  837. X            if ($. < $linenum) {        # Before line to update
  838. X                print DBR;                # Print line verbatim
  839. X            } elsif ($. == $linenum) {    # We reached line to be updated
  840. X                next unless defined(@values);
  841. X                print DBR "$hname $tag $now\t";
  842. X                print DBR join("\t", @values);
  843. X                print DBR "\n";
  844. X            } else {                    # Past updating point
  845. X                print DBR;                # Print line verbatim
  846. X            }
  847. X        }
  848. X        close OLD;
  849. X        close DBR;
  850. X        unless (rename("$file.x", "$file")) {
  851. X            &'add_log("ERROR cannot rename $file.x to $file: $!") if $'loglvl;
  852. X        }
  853. X        &'free_file($file);                # Remove lock on file
  854. X    }
  855. X}
  856. X
  857. X# Delete entry. This is really a wrapper to the more general update routine
  858. X# and is provided as a convenience only.
  859. Xsub delete {
  860. X    local($hname, $tag, $linenum) = @_;
  861. X    &update($hname, $tag, defined($linenum) ? $linenum : undef, undef);
  862. X}
  863. X
  864. X# Make sure the hashing name and the tag are correct, or use default values.
  865. Xsub default {
  866. X    local($hname, $tag) = @_;
  867. X    $hname =~ s/^\s+//;                    # Leading blanks would perturb dbr
  868. X    $hname =~ s/\s/_/g;                    # All other spaces replaced by _
  869. X    $hname = 'X' unless $hname;            # Hashing name cannot be empty
  870. X    $tag =~ s/\s/_/g;                    # Tag has to be a single word
  871. X    $tag = 'UNKNOWN' unless $tag;        # Tag cannot be empty
  872. X    ($hname, $tag);
  873. X}
  874. X
  875. X# Cleaning operation. Remove all the entries in the file whose timestamp is
  876. X# older than the supplied date limit.
  877. Xsub clean {
  878. X    local($agemax) = @_;
  879. X    local($limit) = time - $agemax;        # Everything newer is kept
  880. X    &recursive_clean($cf'hashdir);        # Recursively scan directory
  881. X}
  882. X
  883. X# Recursively scan the direcroy and deal with each file
  884. Xsub recursive_clean {
  885. X    local($dir) = @_;                    # Directory to scan
  886. X    local(@contents);                    # Contents of the directory
  887. X    unless (opendir(DIR, $dir)) {
  888. X        &'add_log("WARNING cannot open directory $dir: $!") if $'loglvl > 5;
  889. X        return;
  890. X    }
  891. X    @contents = readdir(DIR);            # Slurp the whole thing
  892. X    closedir DIR;                        # And close dir, ready for recursion
  893. X    local($_);
  894. X    foreach (@contents) {
  895. X        next if $_ eq '.' || $_ eq '..';
  896. X        if (-d "$dir/$_") {
  897. X            &recursive_clean("$dir/$_");
  898. X            next;
  899. X        }
  900. X        &clean_file("$dir/$_");
  901. X    }
  902. X    unless (opendir(DIR, $dir)) {
  903. X        &'add_log("WARNING cannot re-open directory $dir: $!") if $'loglvl > 5;
  904. X        return;
  905. X    }
  906. X    @contents = readdir(DIR);            # Slurp the whole thing
  907. X    closedir DIR;
  908. X    unless (@contents > 2) {            # Has at least . and ..
  909. X        unless (rmdir($dir)) {            # Don't leave empty directories
  910. X            &'add_log("SYSERR rmdir: $!") if $'loglvl;
  911. X            &'add_log("ERROR could not remove directory $dir") if $'loglvl;
  912. X        }
  913. X    }
  914. X}
  915. X
  916. X# Clean single dbr file, using $limit as the oldest allowed time stamp
  917. Xsub clean_file {
  918. X    local($file) = @_;            # File to be cleaned
  919. X    &'add_log("processing $file") if $'loglvl > 18;
  920. X    unless (open(FILE, $file)) {
  921. X        &'add_log("WARNING cannot open file $file: $!") if $'loglvl > 5;
  922. X        return;
  923. X    }
  924. X    unless (open(NEW, ">$file.x")) {
  925. X        &'add_log("ERROR cannot create $file.x: $!") if $'loglvl > 1;
  926. X        close FILE;
  927. X        return;
  928. X    }
  929. X    &'acs_rqst($file);            # Lock file to prevent concurrent mods
  930. X    local($warns) = 0;            # Avoid cascade warnings
  931. X    local($_, $.);
  932. X    while (<FILE>) {
  933. X        if (/^(\S+)\s([\w-]+)\s(\d+)\t*/) {
  934. X            # Variable $limit was set in 'clean'
  935. X            if ($3 > $limit) {            # File new enough
  936. X                next if (print NEW);    # Copy line verbatim
  937. X                &'add_log("SYSERR write: $!") if $'loglvl;
  938. X                &'add_log("WARNING truncated $file at line $.") if $'loglvl > 5;
  939. X                last;
  940. X            }
  941. X        } else {
  942. X            # Skip bad lines, up to a maximum of 10
  943. X            if (++$warns > 10) {
  944. X                &'add_log("WARNING $file truncated at line $.") if $'loglvl > 5;
  945. X                last;
  946. X            } else {
  947. X                &'add_log("NOTICE $file corrupted, line $.") if $'loglvl > 6;
  948. X                next;
  949. X            }
  950. X        }
  951. X    }
  952. X    close FILE;
  953. X    close NEW;
  954. X    unless (rename("$file.x", $file)) {
  955. X        &'add_log("ERROR cannot rename $file.x to $file: $!") if $'loglvl;
  956. X    }
  957. X    unless (-s "$file") {
  958. X        unless (unlink($file)) {    # Don't leave empty files behind
  959. X            &'add_log("SYSERR unlink: $!") if $'loglvl;
  960. X            &'add_log("ERROR could not remove $file") if $'loglvl;
  961. X        }
  962. X    }
  963. X    &'free_file($file);                # Remove lock on file
  964. X}
  965. X
  966. Xpackage main;
  967. X
  968. END_OF_FILE
  969.   if test 10634 -ne `wc -c <'agent/pl/dbr.pl'`; then
  970.     echo shar: \"'agent/pl/dbr.pl'\" unpacked with wrong size!
  971.   fi
  972.   # end of 'agent/pl/dbr.pl'
  973. fi
  974. if test -f 'bin/perload' -a "${1}" != "-c" ; then 
  975.   echo shar: Will not clobber existing file \"'bin/perload'\"
  976. else
  977.   echo shar: Extracting \"'bin/perload'\" \(20834 characters\)
  978.   sed "s/^X//" >'bin/perload' <<'END_OF_FILE'
  979. X: # feed this into perl
  980. X'/bin/true' && eval 'exec perl -S $0 "$@"'
  981. X    if $running_under_some_shell;
  982. X'di';
  983. X'ig00';
  984. X
  985. X#
  986. X# This perl script is its own manual page [generated by wrapman]
  987. X#
  988. X
  989. X# $Id: perload,v 3.0 1993/11/29 13:50:28 ram Exp ram $
  990. X#
  991. X#  Copyright (c) 1990-1993, Raphael Manfredi
  992. X#  
  993. X#  You may redistribute only under the terms of the Artistic License,
  994. X#  as specified in the README file that comes with the distribution.
  995. X#  You may reuse parts of this distribution only within the terms of
  996. X#  that same Artistic License; a copy of which may be found at the root
  997. X#  of the source tree for mailagent 3.0.
  998. X#
  999. X# $Log: perload,v $
  1000. X# Revision 3.0  1993/11/29  13:50:28  ram
  1001. X# Baseline for mailagent 3.0 netwide release.
  1002. X#
  1003. X
  1004. X# Replace each function definition in a loading section by two stubs and
  1005. X# reject the definition into the DATA part of the script if in a dataload
  1006. X# section or into a FILE if in an autoload section.
  1007. X
  1008. X$in_load = 0;                    # In a loading section
  1009. X$autoload = '';                    # Name of autoloaded file
  1010. X$has_invocation_stub = 0;        # True if we detect a #! stub
  1011. X$current_package = 'main';        # Current package
  1012. X$init_emitted = 0;                # True when dataloading stamp was emitted
  1013. X$in_function = 0;
  1014. X
  1015. Xrequire 'getopt.pl';
  1016. X&Getopt;
  1017. X
  1018. Xwhile (<>) {
  1019. X    if ($. == 1 && /^(:|#).*perl/) {    # Invocation stub
  1020. X        $has_invocation_stub = 1;
  1021. X        print;
  1022. X        next;
  1023. X    }
  1024. X    if ($. <= 3 && $has_invocation_stub) {
  1025. X        print;
  1026. X        next;
  1027. X    }
  1028. X    if (/^\s*$/) {
  1029. X        &flush_comment;
  1030. X        print unless $in_function;
  1031. X        print if $in_function && !$in_load;
  1032. X        if ($in_function && $in_load) {
  1033. X            push(@Data, "\n") unless $autoload;
  1034. X            $Auto{$autoload} .= "\n" if $autoload;
  1035. X        }
  1036. X        next;
  1037. X    }
  1038. X    if (/^\s*;?#/) {
  1039. X        if (/#\s*perload on/i) {        # Enter a loading section
  1040. X            print unless /:$/;
  1041. X            $in_load = 1;
  1042. X            next;
  1043. X        }
  1044. X        if (/#\s*perload off/i) {        # End a loading section
  1045. X            print unless /:$/;
  1046. X            $in_load = 0;
  1047. X            next;
  1048. X        }
  1049. X        if (/#\s*autoload (\S+)/i) {    # Enter autoloading section
  1050. X            print unless /:$/;
  1051. X            push(@autoload, $autoload);    # Directives may be nested
  1052. X            $autoload = $1;
  1053. X            $in_load += 2;
  1054. X            next;
  1055. X        }
  1056. X        if (/#\s*offload/i) {            # End autoloading section
  1057. X            print unless /:$/;
  1058. X            $autoload = pop(@autoload);    # Revert to previously active file
  1059. X            $in_load -= 2;
  1060. X            next;
  1061. X        }
  1062. X        &emit_init unless $init_emitted;
  1063. X        push(@Comment, $_) unless $in_function;
  1064. X        print if $in_function && !$in_load;
  1065. X        next unless $in_function;
  1066. X        push(@Data, $_) unless $autoload;
  1067. X        $Auto{$autoload} .= $_ if $autoload;
  1068. X        next;
  1069. X    }
  1070. X    &emit_init unless $init_emitted;
  1071. X    /^package (\S+)\s*;/ && ($current_package = $1);
  1072. X    unless ($in_load) {
  1073. X        &flush_comment;
  1074. X        print;
  1075. X        next;
  1076. X    }
  1077. X    # We are in a loading section
  1078. X    if (/^sub\s+([\w']+)\s*\{(.*)/) {
  1079. X        die "line $.: function $1 defined within another function.\n"
  1080. X            if $in_function;
  1081. X        # Silently ignore one-line functions
  1082. X        if (/\}/) {
  1083. X            &flush_comment;
  1084. X            print;
  1085. X            next;
  1086. X        }
  1087. X        $comment = $2;
  1088. X        $in_function = 1;
  1089. X        $function = $1;
  1090. X        ($fn_package, $fn_basename) = $function =~ /^(\w+)'(\w+)/;
  1091. X        unless ($fn_package) {
  1092. X            $fn_package = $current_package;
  1093. X            $fn_basename = $function;
  1094. X        }
  1095. X        # Keep leading function comment
  1096. X        foreach (@Comment) {
  1097. X            push(@Data, $_) unless $autoload;
  1098. X            $Auto{$autoload} .= $_ if $autoload;
  1099. X        }
  1100. X        @Comment = ();
  1101. X        # Change package context for correct compilation: the name is visible
  1102. X        # within the original function package while the body of the function
  1103. X        # is compiled within the current package.
  1104. X        $declaration = "sub $fn_package" . "'load_$fn_basename {$comment\n";
  1105. X        $package_context = "\tpackage $current_package;\n";
  1106. X        if ($autoload) {
  1107. X            $Auto{$autoload} .= $declaration . $package_context;
  1108. X        } else {
  1109. X            push(@Data, $declaration, $package_context);
  1110. X        }
  1111. X        # Emit stubs
  1112. X        print "sub $fn_package", "'$fn_basename";
  1113. X        print " { &auto_$fn_package", "'$fn_basename; }\n";
  1114. X        print "sub auto_$fn_package", "'$fn_basename { ";
  1115. X        print '&main\'dataload' unless $autoload;
  1116. X        print '&main\'autoload(' . "'$autoload'" . ', @_)' if $autoload;
  1117. X        print "; }\n";
  1118. X        next;
  1119. X    }
  1120. X    unless ($in_function) {
  1121. X        &flush_comment;
  1122. X        print;
  1123. X        next;
  1124. X    }
  1125. X    # We are in a loading section and inside a function body
  1126. X    push(@Data, $_) unless $autoload;
  1127. X    $Auto{$autoload} .= $_ if $autoload;
  1128. X    $in_function = 0 if /^\}/;
  1129. X    if (/^\}/) {
  1130. X        push(@Data, "\n") unless $autoload;
  1131. X        $Auto{$autoload} .= "\n" if $autoload;
  1132. X    }
  1133. X}
  1134. X
  1135. X@auto = keys %Auto;
  1136. Xif (@auto > 0) {
  1137. X    print &q(<<'EOC');
  1138. X:# Load the calling function from file and call it. This function is called
  1139. X:# only once per file to be loaded.
  1140. X:sub main'autoload {
  1141. X:    local($__file__) = shift(@_);
  1142. X:    local($__packname__) = (caller(1))[3];
  1143. X:    local($__rpackname__) = $__packname__;
  1144. X:    local($__saved__) = $@;
  1145. X:    $__rpackname__ =~ s/^auto_//;
  1146. X:    &perload'load_from_file($__file__);
  1147. X:    $__rpackname__ =~ s/'/'load_/;
  1148. X:    $@ = $__saved__;        # Restore value $@ had on entrance
  1149. X:    &$__rpackname__(@_);    # Call newly loaded function
  1150. X:}
  1151. X:
  1152. X:# Load file and compile it, substituing the second stub function with the
  1153. X:# loaded ones. Location of the file uses the @AUTO array.
  1154. X:sub perload'load_from_file {
  1155. X:    package perload;
  1156. X:    local($file) = @_;                # File to be loaded
  1157. X:    local($body) = ' ' x 1024;        # Pre-extent
  1158. X:    local($load) = ' ' x 256;        # Loading operations
  1159. X:    # Avoid side effects by protecting special variables which will be
  1160. X:    # changed by the autoloading operation.
  1161. X:    local($., $_, $@);
  1162. X:    $body = '';
  1163. X:    $load = '';
  1164. X:    &init_auto unless defined(@'AUTO);    # Make sure we have a suitable @AUTO
  1165. X:    &locate_file unless -f "$file";        # Locate file if relative path
  1166. X:    open(FILE, $file) ||
  1167. X:        die "Can't load $'__rpackname__ from $file: $!\n";
  1168. X:    while (<FILE>) {
  1169. X:        $load .= '*auto_' . $1 . '\'' . $2 . '= *' . $1 . '\'' . "load_$2;\n"
  1170. X:            if (/^sub\s+(\w+)'load_(\w+)\s*\{/);
  1171. X:        $body .= $_;
  1172. X:    }
  1173. X:    close FILE;
  1174. XEOC
  1175. X    if ($opt_t) {
  1176. X        print &q(<<'EOC');
  1177. X:    # Untaint body when running setuid
  1178. X:    $body =~ /^([^\0]*)/;
  1179. X:    # No need to untaint $load, as it was built using trusted variables
  1180. X:    eval $1 . $load;
  1181. XEOC
  1182. X    } else {
  1183. X        print &q(<<'EOC');
  1184. X:    eval $body . $load;
  1185. XEOC
  1186. X    }
  1187. X    print &q(<<'EOC');
  1188. X:    chop($@) && die "$@, while parsing code of $file.\n";
  1189. X:}
  1190. X:
  1191. X:# Initialize the @AUTO array. Attempt defining it by using the AUTOLIB
  1192. X:# environment variable if set, otherwise look in auto/ first, then in the
  1193. X:# current directory.
  1194. X:sub perload'init_auto {
  1195. X:    if (defined $ENV{'AUTOLIB'} && $ENV{'AUTOLIB'}) {
  1196. X:        @AUTO = split(':', $ENV{'AUTOLIB'});
  1197. X:    } else {
  1198. X:        @AUTO = ('auto', '.');
  1199. X:    }
  1200. X:}
  1201. X:
  1202. X:# Locate to-be-loaded file held in $file by looking through the @AUTO array.
  1203. X:# This variable, defined in 'load_from_file', is modified as a side effect.
  1204. X:sub perload'locate_file {
  1205. X:    package perload;
  1206. X:    local($fullpath);
  1207. X:    foreach $dir (@'AUTO) {
  1208. X:        $fullpath = $dir . '/' . $file;
  1209. X:        last if -f "$fullpath";
  1210. X:        $fullpath = '';
  1211. X:    }
  1212. X:    $file = $fullpath if $fullpath;        # Update var from 'load_from_file'
  1213. X:}
  1214. X:
  1215. XEOC
  1216. X}
  1217. X
  1218. Xif (@Data > 0) {
  1219. X    print &q(<<'EOC');
  1220. X:# Load the calling function from DATA segment and call it. This function is
  1221. X:# called only once per routine to be loaded.
  1222. X:sub main'dataload {
  1223. X:    local($__packname__) = (caller(1))[3];
  1224. X:    local($__rpackname__) = $__packname__;
  1225. X:    local($__at__) = $@;
  1226. X:    $__rpackname__ =~ s/^auto_//;
  1227. X:    &perload'load_from_data($__rpackname__);
  1228. X:    local($__fun__) = "$__rpackname__";
  1229. X:    $__fun__ =~ s/'/'load_/;
  1230. X:    eval "*$__packname__ = *$__fun__;";    # Change symbol table entry
  1231. X:    die $@ if $@;        # Should not happen
  1232. X:    $@ = $__at__;        # Restore value $@ had on entrance
  1233. X:    &$__fun__;            # Call newly loaded function
  1234. X:}
  1235. X:
  1236. X:# Load function name given as argument, fatal error if not existent
  1237. X:sub perload'load_from_data {
  1238. X:    package perload;
  1239. X:    local($pos) = $Datapos{$_[0]};            # Offset within DATA
  1240. X:    # Avoid side effects by protecting special variables which will be changed
  1241. X:    # by the dataloading operation.
  1242. X:    local($., $_, $@);
  1243. X:    $pos = &fetch_function_code unless $pos;
  1244. X:    die "Function $_[0] not found in data section.\n" unless $pos;
  1245. X:    die "Cannot seek to $pos into data section.\n"
  1246. X:        unless seek(main'DATA, $pos, 0);
  1247. X:    local($/) = "\n}";
  1248. X:    local($body) = scalar(<main'DATA>);
  1249. X:    local($*) = 1;
  1250. X:    die "End of file found while loading $_[0].\n" unless $body =~ /^\}$/;
  1251. XEOC
  1252. X    if ($opt_t) {
  1253. X        print &q(<<'EOC');
  1254. X:    # Untaint body when running setuid
  1255. X:    $body =~ /^([^\0]*)/;
  1256. X:    # Now we may safely eval it without getting an insecure dependency
  1257. X:    eval $1;        # Load function into perl space
  1258. XEOC
  1259. X    } else {
  1260. X        print &q(<<'EOC');
  1261. X:    eval $body;        # Load function into perl space
  1262. XEOC
  1263. X    }
  1264. X    print &q(<<'EOC');
  1265. X:    chop($@) && die "$@, while parsing code of $_[0].\n";
  1266. X:}
  1267. X:
  1268. XEOC
  1269. X    print &q(<<'EOC') unless $opt_o;
  1270. X:# Parse text after the END token and record defined loadable functions (i.e.
  1271. X:# those whose name starts with load_) into the %Datapos array. Such function
  1272. X:# definitions must be left adjusted. Stop as soon as the function we want
  1273. X:# has been found.
  1274. X:sub perload'fetch_function_code {
  1275. X:    package perload;
  1276. X:    local($pos) = tell main'DATA;
  1277. X:    local($in_function) = 0;
  1278. X:    local($func_name);
  1279. X:    local($., $_);
  1280. X:    while (<main'DATA>) {
  1281. X:        if (/^sub\s+(\w+)'load_(\w+)\s*\{/) {
  1282. X:            die "DATA line $.: function $1'$2 defined within $func_name.\n"
  1283. X:                if $in_function;
  1284. X:            $func_name = $1 . '\'' . $2;
  1285. X:            $Datapos{$func_name} = $pos;
  1286. X:            $in_function = 1;
  1287. X:            next;
  1288. X:        }
  1289. X:        $in_function = 0 if /^\}/;
  1290. X:        next if $in_function;
  1291. X:        return $pos if $func_name eq $_[0];
  1292. X:        $pos = tell main'DATA;
  1293. X:    }
  1294. X:    0;        # Function not found
  1295. X:}
  1296. X:
  1297. XEOC
  1298. X    print &q(<<'EOC') if $opt_o;
  1299. X:# This function is called only once, and fills in the %Datapos array with
  1300. X:# the offset of each of the dataloaded routines held in the data section.
  1301. X:sub perload'fetch_function_code {
  1302. X:    package perload;
  1303. X:    local($start) = 0;
  1304. X:    local($., $_);
  1305. X:    while (<main'DATA>) {            # First move to start of offset table
  1306. X:        next if /^#/;
  1307. X:        last if /^$/ && ++$start > 2;    # Skip two blank line after end token
  1308. X:    }
  1309. X:    $start = tell(main'DATA);        # Offsets in table are relative to here
  1310. X:    local($key, $value);
  1311. X:    while (<main'DATA>) {            # Load the offset table
  1312. X:        last if /^$/;                # Ends with a single blank line
  1313. X:        ($key, $value) = split(' ');
  1314. X:        $Datapos{$key} = $value + $start;
  1315. X:    }
  1316. X:    $Datapos{$_[0]};        # All that pain to get this offset...
  1317. X:}
  1318. X:
  1319. XEOC
  1320. X    print &q(<<'EOC');
  1321. X:#
  1322. X:# The perl compiler stops here.
  1323. X:#
  1324. X:
  1325. X:__END__
  1326. X:
  1327. X:#
  1328. X:# Beyond this point lie functions we may never compile.
  1329. X:#
  1330. X:
  1331. XEOC
  1332. X    # Option -o directs us to optimize the function location by emitting an
  1333. X    # offset table, which lists all the position within DATA for each possible
  1334. X    # dataloaded routine.
  1335. X    if ($opt_o) {
  1336. X        print &q(<<'EOC');
  1337. X:#
  1338. X:# DO NOT CHANGE A IOTA BEYOND THIS COMMENT!
  1339. X:# The following table lists offsets of functions within the data section.
  1340. X:# Should modifications be needed, change original code and rerun perload
  1341. X:# with the -o option to regenerate a proper offset table.
  1342. X:#
  1343. X:
  1344. XEOC
  1345. X        $trailing_message = &q(<<'EOC');
  1346. X:
  1347. X:#
  1348. X:# End of offset table and beginning of dataloading section.
  1349. X:#
  1350. X:
  1351. XEOC
  1352. X        $pos = 0;            # Offset relative to this point (start of table)
  1353. X        foreach (@Data) {
  1354. X            $Datapos{"$1\'$2"} = $pos - $now
  1355. X                if /^sub\s+(\w+)'load_(\w+)\s*\{/;    # } for vi
  1356. X            $pos += length;
  1357. X        }
  1358. X        @poskeys = keys %Datapos;    # Array of routine names (fully qualified)
  1359. X
  1360. X        # Write out a formatted table, each entry stored on $entry bytes and
  1361. X        # formatted with the $format string.
  1362. X        ($entry, $format) = &get_format(*poskeys);
  1363. X
  1364. X        # The total size occupied by the table is the size of one item times
  1365. X        # the number of items plus the final trailing message at the end of
  1366. X        # the table.
  1367. X        $table_size = $entry * @poskeys + length($trailing_message);
  1368. X
  1369. X        # Output formatted table
  1370. X        foreach (sort @poskeys) {
  1371. X            printf($format, $_, $table_size + $Datapos{$_});
  1372. X        }
  1373. X        print $trailing_message;
  1374. X    }
  1375. X
  1376. X    # Output code for each dataloaded function
  1377. X    foreach (@Data) {
  1378. X        print;
  1379. X    }
  1380. X    print &q(<<'EOC');
  1381. X:#
  1382. X:# End of dataloading section.
  1383. X:#
  1384. X:
  1385. XEOC
  1386. X}
  1387. X
  1388. Xif (@auto > 0) {
  1389. X    mkdir('auto',0755) unless -d 'auto';
  1390. X    foreach $file (@auto) {
  1391. X        unless (open(AUTO, ">auto/$file")) {
  1392. X            warn "Can't create auto/$file: $!\n";
  1393. X            next;
  1394. X        }
  1395. X        print AUTO &q(<<'EOC');
  1396. X:# This file was generated by perload
  1397. X:
  1398. XEOC
  1399. X        print AUTO $Auto{$file};
  1400. X        close AUTO;
  1401. X    }
  1402. X}
  1403. X
  1404. X# Compute optimum format for routine offset table, returning both the size of
  1405. X# each entry and the formating string for printf.
  1406. Xsub get_format {
  1407. X    local(*names) = @_;
  1408. X    local($name_len) = 0;
  1409. X    local($max_len) = 0;
  1410. X    foreach (@names) {
  1411. X        $name_len = length;
  1412. X        $max_len = $name_len if $name_len > $max_len;
  1413. X    }
  1414. X    # The size of each entry (preceded by one tab, followed by 12 chars)
  1415. X    $name_len = $max_len + 1 + 12;
  1416. X    ($name_len, "\t%${max_len}s %10d\n");
  1417. X}
  1418. X
  1419. Xsub emit_init {
  1420. X    print &q(<<'EOC');
  1421. X:#
  1422. X:# This perl program uses dynamic loading [generated by perload]
  1423. X:#
  1424. X:
  1425. XEOC
  1426. X    $init_emitted = 1;
  1427. X}
  1428. X
  1429. Xsub flush_comment {
  1430. X    print @Comment if @Comment > 0;
  1431. X    @Comment = ();
  1432. X}
  1433. X
  1434. Xsub q {
  1435. X    local($_) = @_;
  1436. X    local($*) = 1;
  1437. X    s/^://g;
  1438. X    $_;
  1439. X}
  1440. X
  1441. X#
  1442. X# These next few lines are legal in both perl and nroff.
  1443. X#
  1444. X
  1445. X.00;        # finish .ig
  1446. X'di            \" finish diversion--previous line must be blank
  1447. X.nr nl 0-1    \" fake up transition to first page again
  1448. X.nr % 0        \" start at page 1
  1449. X'; __END__    \" the perl compiler stops here
  1450. X
  1451. X'''
  1452. X''' From here on it's a standard manual page.
  1453. X'''
  1454. X
  1455. X.TH PERLOAD 1 "June 20, 1992"
  1456. X.AT 3
  1457. X.SH NAME
  1458. Xperload \- builds up autoloaded and dataloaded perl scripts
  1459. X.SH SYNOPSIS
  1460. X.B perload
  1461. X[ \fB\-ot\fR ]
  1462. X[ \fIfile\fR ]
  1463. X.SH DESCRIPTION
  1464. X.I Perload
  1465. Xtakes a perl script as argument (or from stdin if no argument is supplied)
  1466. Xand prints out on stdout an equivalent script set-up to perform autoloading
  1467. Xor dataloading. The translation is directed by special comments within the
  1468. Xoriginal script. Using dynamic loading can drastically improve start-up
  1469. Xperformances, both in time and in memory, as perl does not need to compile
  1470. Xthe whole script nor store its whole compiled form in memory.
  1471. X.PP
  1472. X.I Autoloading
  1473. Xdelays compilation of some functions until they are needed. The code for these
  1474. Xfunctions is loaded dynamically at run-time. The atomicity of loading is a
  1475. Xfile, which means that putting more than one function into a file will cause
  1476. Xall these functions to be loaded and compiled as soon as one among them is
  1477. Xneeded.
  1478. X.PP
  1479. X.I Dataloading
  1480. Xis a form of autoloading where no extra file are needed. The script carries
  1481. Xall the functions whose compilation is to be delayed in its data segment
  1482. X(in the \fIperl\fR sense, i.e. they are accessible via the DATA filehandle).
  1483. XThe scripts parses the data segment and extracts only the code for the needed
  1484. Xsubroutine, which means granularity is better than with autloading.
  1485. X.PP
  1486. XIt is possible for a single script to use both autoloading and dataloading at
  1487. Xthe same time. However, it should be noted that a script using only dataloading
  1488. Xis self contained and can be moved or shared accross different platforms without
  1489. Xfear. On the contrary, a script using only autoloading relies on some externally
  1490. Xprovided files. Sharing this script among different platforms requires sharing
  1491. Xof these external files. The script itself cannot be redistributed without
  1492. Xalso giving the extra files holding the autoloaded functions.
  1493. X.PP
  1494. XThe major drawback with dataloading is that the DATA filehandle cannot be used
  1495. Xfor anything else and may result in code duplication when two scripts could
  1496. Xshare the same pieces of code. Autoloading appears as the perfect solution in
  1497. Xthis case since two scripts may freely share the same functions without
  1498. Xactually duplicating them on the disk (hence saving some precious disk blocks
  1499. X:-).
  1500. X.SH CRITERIA
  1501. XFunctions to be dataloaded or autoloaded must meet the following layout
  1502. Xcriteria:
  1503. X.TP 5
  1504. X\-
  1505. XThey must not be one-line functions like \fIsub sorter { $a <=> $b }\fR.
  1506. XThose functions are simply output verbatim, as they are already so
  1507. Xsmall that it would not be worth to dynamically load them,
  1508. X.TP
  1509. X\-
  1510. XThe first line must be of the form \fIsub routine_name {\fR, with an optional
  1511. Xcomment allowed after the '{'.
  1512. X.TP
  1513. X\-
  1514. XThe function definition must end with a single '}' character left aligned.
  1515. X.TP
  1516. X\-
  1517. XPackage directives outside any function must be left aligned.
  1518. X.PP
  1519. XAll the above restrictions should not be source of a problem if "standard"
  1520. Xwriting style is used. There are also some name restrictions: the package
  1521. Xname \fIperload\fR is reserved, as is the \fI@AUTO\fR array when autoloading
  1522. Xis used. Packages must not start with \fIauto_\fR, as this is prepended to
  1523. Xuser's package names when building the stubs. Furthermore, the subroutines
  1524. Xnames \fImain'autoload\fR and
  1525. X\fImain'dataload\fR must not be used by the original script. Again, these
  1526. Xshould not cause any grief.
  1527. X.SH DIRECTIVES
  1528. XThe translation performed by
  1529. X.I Perload
  1530. Xis driven by some special comment directives placed directly within the code.
  1531. XEnding those directives with a ':' character will actually prevent them from
  1532. Xbeing output into the produced script. Case is irrelevant for all the directives
  1533. Xand the comment need not be left-aligned, although it must be the first
  1534. Xnon-space item on the line.
  1535. X.PP
  1536. XThe following directives are available:
  1537. X.TP 10
  1538. X# Perload ON
  1539. XTurns on the \fIperload\fR processing. Any function definition which meets
  1540. Xthe criteria listed in the previous section will be replaced by two stubs and
  1541. Xits actual definition will be rejected into the data segment (default) or a
  1542. Xfile when inside an autoloading section.
  1543. X.TP
  1544. X# Perload OFF
  1545. XTurns off any processing. The script is written as-is on the standard output.
  1546. X.TP
  1547. X# Autoload \fIpath\fR
  1548. XRequests autoloading from file \fIpath\fR, which may be an absolute path or
  1549. Xa relative path. The file will be located at run-time using the @AUTO array
  1550. Xif a non-absolute path is supplied or if the file does not exist as listed.
  1551. XAutoloading directives may be nested.
  1552. X.TP
  1553. X# Offload \fIpath\fR
  1554. XThe argument is not required. The directive ends the previous autoloading
  1555. Xdirective (the inmost one). This does not turn off the \fIperload\fR processing
  1556. Xthough. The \fIpath\fR name is optional here (in fact, it has only a comment
  1557. Xvalue).
  1558. X.SH OPTIONS
  1559. XPerload accepts only two options. Using \fB\-o\fR is meaningful only when
  1560. Xdataloading is used. It outputs an offset table which lists the relative
  1561. Xoffset of the dataloaded functions within the data section. This will spare
  1562. Xperl the run-time parsing needed to locate the function, and results in an good
  1563. Xspeed gain. However, it has one major drawback: it prevents people from
  1564. Xactually modifying the source beyond the start of the table. But anything
  1565. Xbefore can be freely edited, which is particulary useful when tailoring the
  1566. Xscript.
  1567. X.PP
  1568. XThis option should not be used when editing of functions within the data
  1569. Xsection is necessary for whatever reason. When \fB\-o\fR is used, any
  1570. Xchange in the dataloaded function must be committed by re-running perload
  1571. Xon the original script.
  1572. X.PP
  1573. XThe other option \fB\-t\fR is to be used when producing a script which is
  1574. Xgoing to run setuid. The body of the loaded function is untainted before being
  1575. Xfed to eval, which slightly slows down loading (the first time the function is
  1576. Xcalled), but avoids either an insecure dependency report or weird warnings from
  1577. Xtaintperl stating something is wrong (which is the behaviour with 4.0 PL35).
  1578. X.SH FILES
  1579. X.TP 10
  1580. Xauto
  1581. Xthe subdirectory where all produced autoloaded files are written.
  1582. X.SH ENVIRONMENT
  1583. XNo environment variables are used by \fIperload\fR. However, the autoloaded
  1584. Xversion of the script pays attention to the \fIAUTOLIB\fR variable as a colon
  1585. Xseparated set of directories where the to-be-loaded files are to be found
  1586. Xwhen a non-absolute path was specified. If the \fIAUTOLIB\fR variable is not
  1587. Xset, the default value 'auto:.' is used (i.e. look first in the auto/
  1588. Xsubdirectory, then in the current directory.
  1589. X.SH CAVEAT
  1590. XSpecial care is required when using an autoloading script, especially when
  1591. Xexecuted by the super-user: it would be very easy for someone to leave a
  1592. Xspecial version of a routine to be loaded, in the hope the super-user (or
  1593. Xanother suitable target) executes the autoloaded version of the script with
  1594. Xsome \fIad hoc\fR changes...
  1595. X.PP
  1596. XThe directory holding the to-be-loaded files should therefore be protected
  1597. Xagainst unauthorized access, and no file should have write permission on them.
  1598. XThe directory itself should not be world-writable either, or someone might
  1599. Xsubstitute his own version.
  1600. XIt should also be considered wise to manually set the @AUTO variable to a
  1601. Xsuitable value within the script itself.
  1602. X.PP
  1603. XThe \fB\-o\fR option uses \fIperl\fR's special variable \fI$/\fR with a
  1604. Xmulti-character value. I suspect this did not work with versions of \fIperl\fR
  1605. Xprior to 4.0, so any script using this optimized form of dataloading will not
  1606. Xbe 100% backward compatible.
  1607. X.SH AUTHOR
  1608. XRaphael Manfredi <ram@acri.fr>
  1609. X.SH CREDITS
  1610. XValuable input came from Wayne H. Scott <wscott@ecn.purdue.edu>. He is
  1611. Xmerely the author of the optimizing offset table (\fB\-o\fR option).
  1612. X.PP
  1613. X.I Perload
  1614. Xis based on an article from Tom Christiansen <tchrist@convex.com>,
  1615. X.I Autoloading in Perl,
  1616. Xexplaining the concept of dataloading and giving a basic implementation.
  1617. X.SH "SEE ALSO"
  1618. Xperl(1).
  1619. END_OF_FILE
  1620.   if test 20834 -ne `wc -c <'bin/perload'`; then
  1621.     echo shar: \"'bin/perload'\" unpacked with wrong size!
  1622.   fi
  1623.   chmod +x 'bin/perload'
  1624.   # end of 'bin/perload'
  1625. fi
  1626. if test -f 'misc/unkit/kitok.msg' -a "${1}" != "-c" ; then 
  1627.   echo shar: Will not clobber existing file \"'misc/unkit/kitok.msg'\"
  1628. else
  1629.   echo shar: Extracting \"'misc/unkit/kitok.msg'\" \(161 characters\)
  1630.   sed "s/^X//" >'misc/unkit/kitok.msg' <<'END_OF_FILE'
  1631. XSubject: Kit %-(name) is available
  1632. X
  1633. XThe %-(parts) parts of the %-(name) kit package
  1634. Xhave been successfully unpacked in %-(kitdir).
  1635. X
  1636. X-- mailagent speaking for %u
  1637. END_OF_FILE
  1638.   if test 161 -ne `wc -c <'misc/unkit/kitok.msg'`; then
  1639.     echo shar: \"'misc/unkit/kitok.msg'\" unpacked with wrong size!
  1640.   fi
  1641.   # end of 'misc/unkit/kitok.msg'
  1642. fi
  1643. echo shar: End of archive 10 \(of 26\).
  1644. cp /dev/null ark10isdone
  1645. MISSING=""
  1646. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ; do
  1647.     if test ! -f ark${I}isdone ; then
  1648.     MISSING="${MISSING} ${I}"
  1649.     fi
  1650. done
  1651. if test "${MISSING}" = "" ; then
  1652.     echo You have unpacked all 26 archives.
  1653.     echo "Now run 'sh PACKNOTES', then read README and type Configure.'"
  1654.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1655. else
  1656.     echo You still must unpack the following archives:
  1657.     echo "        " ${MISSING}
  1658. fi
  1659. exit 0
  1660.  
  1661. exit 0 # Just in case...
  1662.