#!/bin/sh # This is a shell archive (produced by GNU sharutils 4.1). # To extract the files from this archive, save it to some FILE, remove # everything before the `!/bin/sh' line above, then type `sh FILE'. # # Made on 1997-04-02 18:06 MET DST by . # Source directory was `/var/home/olaf/mysrc'. # # Existing files will *not* be overwritten unless `-c' is specified. # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 14663 -r--r--r-- psl/psl.README # 6035 -r--r--r-- psl/psl.1 # 2380 -r--r--r-- psl/psl-tools.1 # 3709 -r-xr-xr-x psl/psl-init # 2850 -r-xr-xr-x psl/psl-salvage # 8208 -r-xr-xr-x psl/psl-subscribe # 14458 -r-xr-xr-x psl/psl-run # 4170 -r--r--r-- psl/psl-aux.pl # 1241 -r--r--r-- psl/lockfiles.pl # 1107 -r--r--r-- psl/mkid.pl # 2039 -r-xr-xr-x psl/addgrps # 3289 -r-xr-xr-x psl/xmvjunk # 1758 -r-xr-xr-x psl/docheckg # 1538 -r--r--r-- psl/psl-parm.proto # 2007 -r--r--r-- psl/perl.patch # 139 -r--r--r-- psl/substman.pl # 2012 -r--r--r-- psl/Makefile # 17982 -r--r--r-- psl/COPYING # 513 -r--r--r-- psl/getnews/README # 881 -r--r--r-- psl/getnews/getnews # 404 -r-xr-xr-x psl/getnews/getnews-setup # 1671 -r--r--r-- psl/getnews/setup.mesg # 1666 -r--r--r-- psl/getnews/setup.german.mesg # 1116 -r--r--r-- psl/getnews/messages # 1014 -r-xr-xr-x psl/getnews/mbc # 248 -r--r--r-- psl/getnews/Makefile # touch -am 1231235999 $$.touch >/dev/null 2>&1 if test ! -f 1231235999 && test -f $$.touch; then shar_touch=touch else shar_touch=: echo echo 'WARNING: not restoring timestamps. Consider getting and' echo "installing GNU \`touch', distributed in GNU File Utilities..." echo fi rm -f 1231235999 $$.touch # # ============= psl/psl.README ============== if test ! -d 'psl'; then echo 'x - creating directory psl' mkdir 'psl' fi if test -f 'psl/psl.README' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl.README (file already exists)' else echo 'x - extracting psl/psl.README (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl.README' && --- psl - a system for managing passive NNTP feeds --- X 1. Intro X The conventional NNTP feed, as managed by programs like "nntpxmit", "nntplink" or the INN news system, runs in active mode. That means, the feeding machine acts as an NNTP client and sends each article (via the IHAVE NNTP command) to its downlinks by itself. This is modeled after the old UUCP feeding method. It has the advantage that it uses exactly the same mechanisms for configuration and feed management as UUCP links, so little extra work is involved. X However, the drawback is that conventional feed management is already cumbersome. There are a number of packages like "gup" that minimize manual work, but such software has to be installed and set up at both ends of each link involved. NNTP provides another possibility: a passive feed, where the receiving end initiates the connection and specifically asks for each article to send. From the server side, this looks like an NNTP news reader. X There is a program called "slurp" that implements a passive NNTP feed. I took this somewhat as a model for this project, which also is the source of its name (the p is for perl, as all of the programs are written in perl rather than C as the original "slurp"). I didn't use slurp proper because of the strain it puts on the server and somewhat limited flexibility. X Actually, this project grew out of a smaller set of programs which implement an offline-news reading system on the user level, somewhat like a half-baked newsfeed, which is used by a handful of people on a large (>10000 users) Unix installation at our University, and now has been merged back into the package. X 2. How psl works X psl consists of the "psl-run" main program, which does the actual news transfer, the "psl-subscribe" utility for generating subscription lists and "psl-init" for setting up and reorganizing feed databases. X "psl-run" can be run regularly out of the crontab. It takes one argument, the name of the server to fetch news from. For more servers, run several instances of "psl-run". (Performance is best when those do not run concurrently, but proper lock file handling ensures that no access conflicts occur.) "psl-run" creates batch files that are suitable for processing by C News' "newsrun" or "relaynews". "psl-run" acts like a news reader in that it reads a database of article numbers (which is actually in .newsrc format) and fetches all new articles in subscribed groups. X "psl-subscribe" is run when a subscription list has changed, e.g. after an incoming "gup" request has been processed. Give to "psl-subscribe" first the server to handle, then any number of subscripton list files as command line arguments. Subscription lists are in INNs "wildmat" format, e.g.: de.*, !de.alt.binaries.*, comp.sys.foo.* (each pattern has to go on one line by itself). A ! denotes exclusion, the _longest_ match (measured by dots in the patterns, inclusion supercedes exclusion) counts. This is the format of the gup "groups" files. So a possible way to run this is like psl-subscribe news.blurfl.com gup/feeds/*/groups X "psl-init" creates or reorganizes all the necessary files for a passive feed. Run this (again, with the server name(s) as argument) for the initial setup and to re-read the active file from a server, see below. X All of the programs write progress indicators on standard output. For crontab use, redirect it to /dev/null. Error messages always go to standard error (not advisable to redirect this). Also, each program records all of its activities in a log file. Each feed has its own log file. The programs are protected against access conflicts via lock files, again one per feed. No locking interaction is necessary with the rest of the news system (the only files that are accessed are "active" and the history database, and this access is read only). Except for one special case of psl-subscribe, psl does not need supervisor privileges (or even the newsmaster account), it operates strictly on its own files and interacts with the news system only by (a) reading the mentioned files and (b) preparing input files, which can be processed by "rnews". (Or by writing files into the in.coming spool, which needs just write access to that directory.) X 3. Setting up psl X First, edit the global parameters in psl-parms.pl to suit your needs. Check if the system constants fit your system. All programs need to be run from the psl directory to find this parameter file. X For all the following discussion, "server" means the fully qualified host name of the NNTP server with an optional port number appended (separated by a : (colon) symbol). "workdir" refers to the directory set in the parameter file, e.g. /var/lib/news/nntp. "server..." denotes a list of (probably all) your servers. "Subscription lists" are files that contain - possibly negated - wildmat(3) patterns, each on its own line (i.e. gup "groups" files). X For each server you want to access, (assuming you have already verified that this server allows you read access), run psl-init This creates a directory / with the following files in it: sactive - a copy of the server's active file log - a log file subscriptions - subscription patterns you want from this server Edit the subscriptions file to suit your needs! That means, enter in it the patterns you want to get from this server. Assuming you want only "comp.*" and "soc.*" but not "comp.sys.foo.*" from this feed, write the three lines comp.* !comp.sys.foo.* soc.* into this file. It is initialized with "!*" (nothing subscribed) for a purpose. You want to control what you get from where. X Then, assuming you already have subscription list files, run psl-subscribe This leaves an additional file in each of the / directories: articles - list of newsgroups and article numbers to get This file has the .newsrc format. It is updated by each run of "psl-run" and "psl-subscribe". The last version of this file is always left in articles.old. If something goes wrong and "psl-run" or "psl-subscribe" exits with an error, you'll be left with files like LOCK.server and articles+. You can delete them. The programs will however clean them up by themselves on the next run. X You can then run psl-run and watch the first news coming in. By default it only fetches news arriving after the psl-init run, but you can override this (see below on options). You'll get transfer statistics after the run that contain the number of groups processed, articles and bytes fetched, and time taken. A net CPS rating is calculated from the latter two - the time it takes to fetch headers can push down the CPS rating. It's up to you to determine if you get sufficient throughput. :-) You get a CPS rating for the raw article transfer too. On fast feeds, this is not very accurate, however (the granularity of the system clock is one second). X If "psl-run" receives a signal, it will take some time to clean up. Don't kill -9 it if not necessary. After it has been forcefully terminated or after a crash, if you have an "articles+" file, run psl-salvage to attempt to restore the articles file. (Alternatively, you can just delete the articles+ file (preferred) or re-run psl-subscribe, but this will take more bandwith and much more processing time on the next run.) X Run psl-run from your crontab, one per feed, like in: 10 * * * * psl-run news.foo.edu 40 * * * * psl-run news.bar.edu 50 3 * * * psl-run news.baz.com X Run psl-init once a week or so to update the "sactive" files. The first two fields of this file are used to determine which newsgroups to get and an approximate lower bound on the article numbers. (The second field is the higher bound, which is here used as a lower bound, which means you don't get the whole article base of one group after first subscribing it, but rather only new articles. You can, however, override this with the -a switch to psl-subscribe.) If new newsgroups arrive, the "sactive" files will not be updated automatically, so you won't get them unless you re-read the files. (This will probably change in later releases of psl.) psl-init does not generate much load, so you can even run it daily. X Each time any of your subscription lists changes, run psl-subscribe This updates the articles files to reflect the subscription changes. X If the articles file gets corrupted beyond repair, delete it and run psl-subscribe If the sactive file ever gets corrupted (it shouldn't since nothing needs to write to it), if a server announces changes in the hierarchies it runs, etc., just run psl-init . Subscriptions and articles files won't get changed in this case. X You even can delete an entire server directory and start over again at any time without losing any vital information. It will just take more time (beware, much more time) on the next psl-run run to rebuild the article number information in the articles file (if you run psl-subscribe -a) or you are likely to miss some articles. X If a server refuses to serve you a group (for whatever reason), this group gets marked as unsubscribed in the articles file and this fact is logged. psl-subscribe will probably re-subscribe the group, you should then check the sactive file. X 4. psl internals X These programs are written in perl (version 4.036). Effort has been made to keep them sufficiently readable and non-system-dependent. The programs are probably significantly shorter than expected thanks to the high code density of perl. :-) I still consider them rather simple. X "psl-run" reads your system's history file and therefore needs a version of perl that supports the proper dbm format. Most C News sites use dbz for this purpose. In this case, you will have to recompile perl (get it from any GNU archive). perl 5 does not work by now with dbz. The perl 4 source tree knows about "make dbzperl" which will create the right version for you, but at least the dbz I use (dated Jul 23, 1993) will not find the history lines due to an apparent off-by-one error. I have provided a patch to perl that will fix this (and break nothing else with non-dbz versions of perl). Check if your dbzperl works with the following script: X #!/usr/local/bin/dbzperl $histfile = "/var/lib/news/history"; # change if necessary dbmopen(%history, $histfile, 0) || die "dbmopen"; open(H, $histfile) || die "open"; foreach $i (0...7) { X $_=; split; X $a = $history{$_[0]}; X print "$_[0] not found\n" unless $a; } Xexit 0; X This script should produce no output. "psl-run" will run with a broken db[mz] or with no dbm access at all, but it will load the network significantly more in this case, so better use a working version. X You probably want to take a short look at "lockfiles.pl" and "mkid.pl" which could be useful in other contexts too. The latter produces unique strings, suitable for use as file names or Message-IDs. (It combines the current time, PID and an internal counter and encodes them using a high-base system. Currently not used in psl but I left it in if anyone needs such a thing.) "lockfiles.pl" is a routine for the everywhere needed lock file handling. It is modeled after the C News locking, but this version is capable of breaking stale locks. X psl-subscribe automatically subscribes to the "control" pseudo-group(s). This is necessary in order to get control messages. You can subscribe to the "junk" pseudo-group if you want. In these two cases, psl-run performs an additional check on the Newsgroups lines and accepts only postings for at least one group in the local active file. X 5. Command reference X [See the supplied man pages for details.] X psl-init server... Set up the server dir and "sactive" file for one or more servers. (Can also be used to fetch any server's active file, if you just want to check for groups.) X psl-subscribe [options] server file... Subscribe to one server's groups according to the given subscription files. The subscription files are in the format provided by "gup". Generate/update the "articles" (.newsrc format) file. X psl-run [options] server Fetch news from a server according to its "articles" file. X psl-salvage server... Attempt to restore articles files after a crashed/interrupted psl-run. Perhaps of dubious value. X A few more utilities are provided which are not psl-specific but use its code: X docheckg This program processes a checkgroups report as generated by C News Cleanup Release G. I.e. pipe the piece of mail you receive from a checkgroups (not the checkgroups article!) into this program, and it will do the necessary changes. X xmvjunk [-r] [-n] [-b size] Look for junked articles, ask to create their newsgroups, and repost the articles locally. -r: Remove the junked articles. -n: Don't manipulate the active file. -b: Maximum batch size. (Inspired by a "mvjunk" script supplied with some news systems, but more convenient.) X cleangroups [-d] Check for (and with -d, delete) empty newsgroups. (This program has to be tailored in the source!) X 6. Miscellaneous X For other news managing stuff look at my WWW pages: http://www.inka.de/sites/bigred/sw/ (also accessible via FTP at the same path name) X INKA (Individual Network Karlsruhe) is a local private non-profit organization whose goal is to provide Internet connections to everybody at minimum cost. Information about INKA is at http://www.inka.de/ Our three main newsfeeds are all run exclusively via psl for some months now. X I wish to thank everybody who has contributed to making this possible, especially all the people involved in the development of the Linux operating system, C News, perl and a number of other fine software packages which are in daily use here, as well as everybody who has contributed hardware to the project. X December 1994-June 1995 X X Olaf Titz X # (c) 1994-95 Olaf Titz and INKA e.V. # $Id: psl.README,v 2.4 1997/04/02 16:03:50 olaf Exp $ X This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. X This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. X You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. SHAR_EOF $shar_touch -am 0402180697 'psl/psl.README' && chmod 0444 'psl/psl.README' || echo 'restore of psl/psl.README failed' shar_count="`wc -c < 'psl/psl.README'`" test 14663 -eq "$shar_count" || echo "psl/psl.README: original size 14663, current size $shar_count" fi # ============= psl/psl.1 ============== if test -f 'psl/psl.1' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl.1 (file already exists)' else echo 'x - extracting psl/psl.1 (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl.1' && ''' $Id: psl.1,v 1.4 1996/02/23 00:21:38 olaf Exp $ X.\"-*-nroff-*- X.TH psl 1 X.SH NAME psl-init \- set up the psl files and fetch active file from server X.br psl-subscribe \- build a subscription list X.br psl-run \- transfer news from server X.SH SYNOPSIS X.B psl-init [-s] server... X.br X.B psl-subscribe [-w secs] [-m num] [-a] [-g] server file... X.br X.B psl-run [-d days] [-b size] [-s] [-l] [-x n] [-v] [-y group] server X.SH DESCRIPTION X.TP X.B psl-init fetches the X.I active file(s) from the named server(s) and builds the directories and files needed for X.B psl operations. X.B psl-init can also be used as a convenient means for just fetching a server's X.I active file. X.PP X.I Options X.TP X.I \-s sort the active file in a random order, to prevent alphabetic skew of backlog. The control group(s) come first, since control messages are to be distributed fast. X.TP X.B psl-subscribe subscribes to the newsgroups given by the subscription lists. Subscription lists have the format given by X.B gup, see below. A group is subscribed to if and only if it matches the server's global subscription file X.B and any of the subscription files given on the command line. X.B psl-subscribe builds the X.I articles file. If this file is already present, it corrects the subscriptions in this file. X.PP X.I Options X.TP X.I \-w secs wait X.I secs seconds if the server is locked (e.g. by a running X.B psl-run ), then try again. (Default: 30 minutes.) X.TP X.I \-m num How often to repeat if server is locked. (Default: 2) X.TP X.I \-a If a group is newly subscribed to, fetch X.I all articles on the next run instead of new articles only. This ensures that no articles are missed at the expense of much more time needed for the next X.B psl-run. X.TP X.I \-g Automatically add newsgroups to the system's X.I active file which get subscribed but are not present. X.TP X.B psl-run does the actual news transfer. It connects to the server, reads all groups which are marked as subscribed in the X.I articles file, fetches all articles in these groups that are not marked as already read, and leaves them in the form of batches in the incoming spool. X.PP X.I Options X.TP X.I \-d days Don't fetch articles which are older than the given number of days (by their "Date" lines). 0 means disble this check. (Default: 7) X.TP X.I \-b size Give an approximate upper bound (in bytes) on the batch size. (Note that individual batches may be bigger.) Default: 1 million. X.TP X.I \-s Don't check disk space for batches. X.TP X.I \-l Accept articles with "Distribution: local". Inadvisable. X.TP X.I \-x num Disable the various checks on the history file and crosspostings. With num>=1, don't check on the history file. With num>=2, don't check for crossposting. With num>=3, don't check on the active file. With num>=3, no access to the news systems' files is necessary. X.TP X.I \-v Produce verbose tracing output. X.TP X.I \-y group Do not fetch any articles from the server, instead generate a list of Message-IDs and post it as a X.I sendme control message to the named newsgroup (usually to.system where system names the remote server). Used to establish what I call a "youhave-sendme" feed where the articles are transferred as compressed batches. This option is incompatible with -x. X.SH SUBSCRIPTION FILES The subscription list files used by X.B psl are the same as those used by the X.B gup subscribe-per-mail package. A X.I subscription list consists of one or more X.I subscription patterns, each one on a line. A X.I subscription pattern is a X.I newsgroup pattern, optionally prefixed with a "!" character, meaning "not". A X.I newsgroup pattern is a newsgroup name, in which components can be replaced by a "*" character, meaning "all". X.TP X.I Example X.B "comp.*" X.br X.B "!comp.sys.foo" X.PP means: subscribe to all comp.* groups except comp.sys.foo. X.SH DIAGNOSTICS The programs write progress indicators on standard output. All actions are logged into the server log files. Various possible failures are reported on standard error. X.SH BUGS Not all possible forms of X.I newsgroup patterns are supported. It is advisable to refrain from using wildcards in the middle of a word. X.br X.B psl-subscribe is too slow, especially for long subscription lists with few wildcards. X.br X.B psl-run does not obey distributions. X.br X.B psl-run reports inaccurate "raw article" cps ratings due to poor (1 second) resolution of the system clock. X.SH FILES In the directory X.I __WORKDIR each server has its own directory. Inside a server directory: X.TP X.I sactive the server's X.I active file, possibly in randomized order of groups. X.TP X.I articles The X.I ".newsrc" format file which stores the information which groups are subscribed to, and which articles have already been fetched. X.TP X.I articles.old Last version of X.I articles. X.TP X.I subscriptions contains a subscription list for the server. Only groups that match here are subscribed to by X.B psl-subscribe. X.TP X.I log Logs all actions of the X.B psl programs. X.TP X.I LOCK.server Lock file, active when running any of the X.B psl programs on the server. X.TP X.I __INPUTDIR Batches are prepared and left for further processing here. A batch is complete if and only if its name is all numbers with a ".t" suffix. X.SH PRIVILEGES NEEDED X.B psl-subscribe with the X.I \-g option needs to run under the newsmaster account. X.br X.B psl-run without the X.I \-x3 option needs read access to the news control files. X.br X.B psl-run needs write permission on the incoming spool, but can be configured to use its own spool area. In this case, some means of piping the batches into X.B rnews or other processing has to be set up. X.br Everything else does not need any special privileges. Nothing in X.B psl needs root privileges. X.SH SEE ALSO wildmat(3), gup(8), rnews(8). X.SH AUTHOR Olaf Titz X.SH COPYING This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SHAR_EOF $shar_touch -am 0223012196 'psl/psl.1' && chmod 0444 'psl/psl.1' || echo 'restore of psl/psl.1 failed' shar_count="`wc -c < 'psl/psl.1'`" test 6035 -eq "$shar_count" || echo "psl/psl.1: original size 6035, current size $shar_count" fi # ============= psl/psl-tools.1 ============== if test -f 'psl/psl-tools.1' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl-tools.1 (file already exists)' else echo 'x - extracting psl/psl-tools.1 (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl-tools.1' && ''' $Id: psl-tools.1,v 1.3 1996/02/23 00:21:38 olaf Exp $ X.\"-*-nroff-*- X.TH psl-tools 1 X.SH NAME addgrps \- add groups from servers' active files X.br docheckg \- do the modifications advised by checkgroups X.br xmvjunk \- move articles out of junk X.SH SYNOPSIS X.B addgrps [ -c ] active-file... X.br X.B docheckg X.br X.B xmvjunk [ -r ] [ -n ] [ -b size ] X.SH DESCRIPTION X.TP X.B addgrps adds groups to the local X.I active file as given by one or more server X.I active file(s). This results in that the local system will know every group that at least one of the X.B psl servers has, if the X.I sactive files are used as input. X.br With the X.I \-c option, X.B addgrps will additionally check the X.I active.times and X.I newsgroups files for consistency and add any missing entries. X.TP X.B docheckg processes the mail generated by X.B checkgroups messages on C News Cleanup Release systems. It takes the mail message ("If you believe this checkgroups...") as input and does all the advised modifications. X.TP X.B xmvjunk takes all articles out of the X.I junk pseudo-group, looks for their Newsgroups line, asks the user whether to create these newsgroups and reposts the articles locally after creating the groups. X.TP X.I Options X.TP X.I \-r Remove articles after reposting from X.I junk. X.TP X.I \-n Don't add new groups, just repost if necessary. X.TP X.I \-b size Maximum size of batches left (see the description of X.B psl-run ). X.SH DIAGNOSTICS These programs give progress indicators on standard output. Actions are logged to a common log file. X.SH NOTE Use these tools with care, you need to know how a news system works. X.SH BUGS It's too easy to get unintended results (i.e., mess up the news system). X.SH FILES In X.I __NEWSLIB: X.TP X.I active, active.times, newsgroups The news system's files that register valid newsgroups. X.TP X.I active.o, active.times.o, newsgroups.o Old versions of these files. X.TP X.I psl.log Common log file for these programs. X.TP X.I LOCK News system lock. X.SH PRIVILEGES NEEDED These programs have to be run under the newsmaster account. X.SH SEE ALSO psl(1), news(5). X.SH AUTHOR Olaf Titz X.SH COPYING This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SHAR_EOF $shar_touch -am 0223012196 'psl/psl-tools.1' && chmod 0444 'psl/psl-tools.1' || echo 'restore of psl/psl-tools.1 failed' shar_count="`wc -c < 'psl/psl-tools.1'`" test 2380 -eq "$shar_count" || echo "psl/psl-tools.1: original size 2380, current size $shar_count" fi # ============= psl/psl-init ============== if test -f 'psl/psl-init' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl-init (file already exists)' else echo 'x - extracting psl/psl-init (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl-init' && #!/usr/bin/perl # psl-init - set up files and directories for psl -*- perl -*- X # (c) 1994-95 Olaf Titz and INKA e.V. # $Id: psl-init,v 2.5 1996/02/23 00:21:38 olaf Exp $ X # Usage: psl-init server... # Set up the necessary files for a feed from "server". # Read the server's active file, etc. Also used to update the active # file (leaving the articles file alone). X # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. X require "getopts.pl"; require "lockfiles.pl"; require "psl-parm.pl"; X # ---------------------------------------------------------------------------- X &Getopts("s"); srand; while ($server=shift) { X if (!chdir("$workdir/$server")) { X if (mkdir("$workdir/$server", 0755)) { X chdir("$workdir/$server"); X } else { X print STDERR "mkdir($workdir/$server) failed\n"; X next; X } X } X if (!&lockfile("LOCK.", "server")) { X if ($?<0) { X print STDERR "Can't establish lock for $server"; X } else { X print STDERR "$server is locked by pid $?\n"; X if (++$waits{$server} < $maxwaits) { X push(@ARGV, $wait, $server); X } X } X next; X } X if (!open(LOG, ">>$logfile")) { X print STDERR "Can't open logfile for $server\n"; X } X &process_server; X unlink("LOCK.server"); } X Xexit 0; X # ---------------------------------------------------------------------------- X sub process_server { X $count=0; X print STDOUT "$server "; X @_ = localtime; $_[5]+=1900 if ($_[5]<1901); X printf (LOG "\n+++ psl-init %02d.%02d.%04d %02d:%02d\n", X $_[3], ++$_[4], $_[5], $_[2], $_[1]); X select(SACT); $|=1; X socket(NNTP, $AF_INET, $SOCK_STREAM, $IPPROTO_TCP); X ($serv,$port) = split(/:/, $server); X $port = $PORT_NNTP unless $port; X @_ = gethostbyname($serv); $? && exit(1); # die "gethostbyname($serv)"; X connect(NNTP, pack("S n a4 x8", $AF_INET, $port, $_[4])) X || exit(1); # die "connect($server)"; X select(NNTP); $|=1; X select(STDOUT); $|=1; X $_ = ; y/\r//; split; X $_[0] =~ /^20[01]/ X || die "server failed ($_)"; X print NNTP "LIST ACTIVE\r\n"; X $_ = ; y/\r//d; split; X if ($_[0] == 215) { X open(SACT, ">$sactfile+") X || die "open new active file failed: $!\n"; X undef @sact; X while ($_ = , y/\r//d, $_ ne ".\n") { X split; X eof(NNTP) && die "Lost connection"; X if ($_[3] =~ /^[ynm]/) { X ++$count; X if ($opt_s) { X if ($_[0] =~ /^control/) { X $o=$count; X } else { X $o=int(rand(800000))+100000; X } X push(@sact, sprintf("%06d %s", $o, $_)); X } else { X push(@sact, $_); X } X } X if ((++$progress)>50) { X print STDOUT "."; $progress=0; X } X } X if ($opt_s) { X foreach $_ (sort @sact) { X s/^[0-9]+\s+//; X print SACT; X } X } else { X foreach $_ (@sact) { X print SACT; X } X } X close SACT; X unlink($sactfile); rename("$sactfile+", $sactfile); X } else { X die "$server: $_"; X } X close NNTP; X print STDOUT "$count\n"; X print LOG " server has $count groups.\n"; X close LOG; X if (! -f $subfile) { X open(SUB, ">$subfile") || die "open $subfile"; X print SUB "!*\n"; X print STDOUT "Remember to edit $server/$subfile as desired!\n"; X close SUB; X } } X # ---------------------------------------------------------------------------- SHAR_EOF $shar_touch -am 0223012296 'psl/psl-init' && chmod 0555 'psl/psl-init' || echo 'restore of psl/psl-init failed' shar_count="`wc -c < 'psl/psl-init'`" test 3709 -eq "$shar_count" || echo "psl/psl-init: original size 3709, current size $shar_count" fi # ============= psl/psl-salvage ============== if test -f 'psl/psl-salvage' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl-salvage (file already exists)' else echo 'x - extracting psl/psl-salvage (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl-salvage' && #!/usr/bin/perl # psl-salvage - restore corrupt psl subscription files -*- perl -*- X # (c) 1994-95 Olaf Titz and INKA e.V. # $Id: psl-salvage,v 2.2 1995/11/05 13:10:52 olaf Exp $ X # Usage: psl-salvage server... # If a psl-run process crashes, you are possibly left with an incomplete # articles+ file and the old one unchanged. # This program can then be run to at least incorporate those updates # into the articles file that are in the articles+ file. # DO NOT run this after a crash of psl-subscribe - re-run psl-subscribe # instead. X # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. X require "lockfiles.pl"; require "psl-parm.pl"; X # ---------------------------------------------------------------------------- X while ($server=shift) { X if (!chdir("$workdir/$server")) { X print STDERR "chdir($workdir/$server) failed\n"; next; X } X if (!&lockfile("LOCK.", "server")) { X if ($?<0) { X print STDERR "Can't establish lock for $server"; X } else { X print STDERR "$server is locked by pid $?\n"; X } X next; X } X if (!open(LOG, ">>$logfile")) { X print STDERR "Can't open logfile for $server\n"; X } X select(LOG); $|=1; X &process_server; X unlink("LOCK.server"); } X Xexit 0; X # ---------------------------------------------------------------------------- X sub process_server { X @_ = localtime; X printf (LOG "\n+++ psl-salvage %02d.%02d.%02d %02d:%02d\n", X $_[3], ++$_[4], $_[5], $_[2], $_[1]); X if (!open(OSUB, "<$artfile")) { X print LOG " open old file failed: $!\n"; X return; X } X if (!open(SUB, "<$artfile+")) { X print LOG " open crash file failed: $!\n"; X return; X } X if (!open(NSUB, ">$artfile++")) { X print LOG " open new file failed: $!\n"; X return; X } X X print STDOUT "$server "; X while () { X $o = || die "Can't read old file"; X if (/^[a-zA-Z0-9._+-]+[:!]\s*[0-9,-]+\n$/) { X print NSUB; X ++$ncount; X } else { X print NSUB $o; X ++$ocount; X last; X } X } X while () { X ++$ocount; X print NSUB; X } X print STDOUT "$ocount/$ncount\n"; X print LOG " $ocount lines from old file, $ncount from new file\n"; X close OSUB; close SUB; close NSUB; close LOG; X unlink("$artfile.old"); X rename("$artfile", "$artfile.old"); X rename("$artfile++", "$artfile"); X unlink("$artfile+"); } X # ---------------------------------------------------------------------------- SHAR_EOF $shar_touch -am 1105141295 'psl/psl-salvage' && chmod 0555 'psl/psl-salvage' || echo 'restore of psl/psl-salvage failed' shar_count="`wc -c < 'psl/psl-salvage'`" test 2850 -eq "$shar_count" || echo "psl/psl-salvage: original size 2850, current size $shar_count" fi # ============= psl/psl-subscribe ============== if test -f 'psl/psl-subscribe' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl-subscribe (file already exists)' else echo 'x - extracting psl/psl-subscribe (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl-subscribe' && #!/usr/bin/perl # psl-subscribe - update psl subscription files -*- perl -*- X # (c) 1994-95 Olaf Titz and INKA e.V. # $Id: psl-subscribe,v 2.6 1997/04/02 16:03:50 olaf Exp $ X # Usage: psl-subscribe server file... # Take the respective server active file, look what's in to # subscribe, and write a new articles file (taking the numbers from the # old one) reflecting the current subscriptions. # What to subscribe is given by pattern lists in the named files (e.g. # feed subscription lists), they are ORed. # The files are lists of subscription patterns (INN wildmat(3) style # with ! for exclusions), e.g. GUP "groups" files. X # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. X require "getopts.pl"; require "lockfiles.pl"; require "psl-parm.pl"; X $opt_w = 1800; # how long to wait (default 30 min) if server is locked $opt_m = 2; # and how often to repeat this (default 2) # $opt_a # get all articles, including old ones # $opt_g # automatically add new newsgroups # $opt_d # debug X &Getopts("w:m:agd"); $aform = $opt_a ? "1" : "1-%d"; $server = shift || die "Usage: $0 "; X # ---------------------------------------------------------------------------- $nsubs = 1; &patnew("asubl1"); &patpush("asubl1", "control"); # we always want this &patfin("asubl1"); $rate[1] = -1e9; while ($f = shift) { X if (open(S, "<$f")) { X print STDOUT "$f "; X ++$nsubs; $r=0; X local($sub) = "asubl$nsubs"; X &patnew($sub); X while () { X chop; X # attempt to rate the patterns X /\*/ && do {$r-=5; $r-=10 if (/^!/);}; # prefer wildcards X $r += length; X &patpush($sub, $_); X } X close S; X &patfin($sub); X $rate[$nsubs] = $r; X } else { X print STDERR "Can't read subscription file $f\n"; X } } # reorder subscription lists foreach $n (1..$nsubs) { X $m = $n; X foreach $i (1..$nsubs) { X if ($rate[$i] < $rate[$m]) { X $m = $i; X } X } X local(*asubp, *asubn) = ("asubl${m}P", "asubl${m}N"); X local(*subp, *subn) = ("subl${n}P", "subl${n}N"); X @subp = @asubp; @subn = @asubn; X undef @asub; undef @asubn; X $rate[$m] = 1e9; } X chdir("$workdir/$server") || X die "chdir($workdir/$server) failed"; while (--$opt_m) { X if (&lockfile("LOCK.", "server")) { X last; X } X if ($?<0) { X print STDERR "Can't establish lock for $server"; X } else { X print STDERR "$server is locked by pid $?\n"; X sleep $opt_w; X } } if (!open(LOG, ">>$logfile")) { X print STDERR "Can't open logfile for $server\n"; } select(LOG); $|=1; &process_server; X if ($opt_g) { X chdir("$newslib") || X die "chdir($newslib) failed"; X $count = 0; X while (1) { X if (&lockfile("LOCK", "")) { X last; X } X if ($?<0) { X print STDERR "Can't lock the news system"; X } else { X sleep 10; X } X } X $tstr = sprintf("%d %s@%s (psl-subscribe)", time, $logname, $hostname); X if (open(A, "+<$active")) { X while () { X ($g,$x,$y,$f) = split; X $active{$g} = $f; X } X if (open(T, ">>active.times")) { X foreach $g (keys %subscr) { X if (!$active{$g}) { # add this group X $x = $subscr{$g}; X print A "$g 0000000000 00001 $x\n"; X &mkdirs($g); X print T "$g $tstr\n"; X ++$count; X } X } X } else { X print STDERR "Can't open active.times file: $!\n"; X } X print LOG " $count groups added.\n"; X } else { X print STDERR "Can't open the active file: $!\n"; X } X unlink("LOCK"); } close LOG; unlink("$workdir/$server/LOCK.server"); Xexit 0; X # ---------------------------------------------------------------------------- X sub process_server { X $count = 0; X @_ = localtime; $_[5]+=1900 if ($_[5]<1901); X printf (LOG "\n+++ psl-subscribe %02d.%02d.%04d %02d:%02d\n", X $_[3], ++$_[4], $_[5], $_[2], $_[1]); X if (!open(NSUB, ">$artfile+")) { X print LOG " open new file failed: $!\n"; X return; X } # this hook is for later ## if (!open(SACT, ">>$sactfile")) { ## print LOG " open active file failed: $!\n"; ## return; ## } ## close SACT; X if (!open(SACT, "<$sactfile")) { X print LOG " open active file failed: $!\n"; X return; X } X print STDOUT "\n$server"; X if (open(ART, "<$artfile")) { X print STDOUT " O"; X while () { X ($g,$l) = split(/[\s:!]+/, $_); X $grp{$g} = $l; X } X close ART; X } X &patnew("sublist"); X if (open(SUB, "<$subfile")) { X print STDOUT " S"; X while () { X chop; X &patpush("sublist", $_); X } X close SUB; X } else { X &patpush("sublist", "*"); X } X &patfin("sublist"); X if ($opt_d) { X &dump; X } X print STDOUT " A"; X while () { X ($g,$x,$y,$f) = split; X $s = &globsublistmat($g); X if ($s) { # subscribe! X ($l = $grp{$g}) || do {$l=sprintf($aform,$x);}; X printf(NSUB "%s: %s\n", $g, $l); X ++$count; X if ($opt_g) { X $subscr{$g} = $f; X } X } elsif ($l=$grp{$g}) { X printf(NSUB "%s! %s\n", $g, $l); X } X if ((++$progress)>50) { X print STDOUT "."; $progress=0; X } X } X close SACT; X print STDOUT " $count\n"; X print LOG " $count groups subscribed.\n"; X unlink("$artfile.old"); X rename("$artfile", "$artfile.old"); X rename("$artfile+", "$artfile"); } X # ---------------------------------------------------------------------------- X sub convpat # convert wildmat -> regexp { X local($_) = @_; X s/(\W)/\\\1/g; X s/\\\*/.\*/g; X return $_; } X sub patnew # init a subscription array # Format of the subscription array: the name "foo" refers to two arrays, # @fooP and @fooN, contaning positive and negative patterns. # @fooP[$n] contains a regexp that matches all patterns with at least # $n dots in them. { X local($a) = @_; X local(*ap,*an) = ("${a}P", "${a}N"); X undef @ap; X undef @an; } X sub patpush # push wildmat on subscription array { X local($a,$_) = @_; X local(*ap,*an) = ("${a}P", "${a}N"); X local($n)=tr/.//; X if (s/^!//) { X $an[$n] .= sprintf("|%s", &convpat($_)); X } else { X $ap[$n] .= sprintf("|%s", &convpat($_)); X } } X sub patfin # finish a subscription array { X local($a) = @_; X local(*ap,*an) = ("${a}P", "${a}N"); X foreach $_ (@ap) { X if (defined($_)) { X s/^\|?/^(/; s/$/\)/; X } X } X foreach $_ (@an) { X if (defined($_)) { X s/^\|?/^(/; s/$/\)/; X } X } } X sub sublistmat # sublistmat(string, array) - test if string matches into array { X local($_,$a) = @_; X local(*ap,*an) = ("${a}P", "${a}N"); X local($p,$n) = (-1,-1); X foreach $i (0..$#ap) { X if (defined($ap[$i]) && /$ap[$i]/i) { X $p=$i; X } X } X foreach $i (0..$#an) { X if (defined($an[$i]) && /$an[$i]/i) { X $n=$i; X } X } X if ($opt_d) { X printf STDOUT "%s\@%s->%d,%d ", $_, $a, $p, $n; X } X return (($p>=$n) && ($p>=0)); } X sub globsublistmat # globsublistmat(string) # test if string matches into @sublist *and* _either one_ of @subl1... { X local($s) = @_; X study $s; X return 0 unless &sublistmat($s,"sublist"); X foreach $i (1..$nsubs) { X return 1 if &sublistmat($s,"subl$i"); X } X return 0; } X sub dumppat { # Dump a precompiled pattern list. X local($a) = @_; X local(*ap,*an) = ("${a}P", "${a}N"); X print STDOUT "- List: $a\n"; X printf STDOUT "-- positive matches: %s\n", join(" , ", @ap); X printf STDOUT "-- negative matches: %s\n", join(" , ", @an); } sub dump { X print STDOUT "\n## Dump of internal subscription structures\n"; X &dumppat("sublist"); X foreach $i (1..$nsubs) { X &dumppat("subl$i"); X } X print STDOUT "## End of dump.\n"; } X sub mkdirs # mkdirs(group) # make the spool directory for "group" # Assumes you have the news UID! { X local(@x) = split(/\./, $_[0]); X local($i); X foreach $i (0..$#x) { X mkdir(join("/", ($newsarts, @x[0..$i])), 0775); # ignore error X } } X # ---------------------------------------------------------------------------- SHAR_EOF $shar_touch -am 0402180697 'psl/psl-subscribe' && chmod 0555 'psl/psl-subscribe' || echo 'restore of psl/psl-subscribe failed' shar_count="`wc -c < 'psl/psl-subscribe'`" test 8208 -eq "$shar_count" || echo "psl/psl-subscribe: original size 8208, current size $shar_count" fi # ============= psl/psl-run ============== if test -f 'psl/psl-run' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl-run (file already exists)' else echo 'x - extracting psl/psl-run (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl-run' && #!/usr/local/bin/dbzperl # psl-run - get news from an NNTP server -*- perl -*- X # (c) 1994-95 Olaf Titz and INKA e.V. # $Id: psl-run,v 2.6 1997/04/02 16:03:50 olaf Exp $ X # This program does roughly the same as "slurp" does. The difference # is the way this one determines which articles to fetch: it takes a # .newsrc-formatted file (in $workdir/$server/articles) and attempts # to read all articles with higher numbers than marked in the file, # just like newsreaders. This saves the huge overhead of the NEWNEWS # command. X # "psl-run" won't fetch an article if either one of the following # conditions are met: the article is in the local history file, the # article's posting date is more than $opt_d days ago, or the # distribution is "local". These conditions are checked with XHDR # queries before attempting to read articles. X # "psl-run" builds proper batches into the in.coming directory just # like nntpd. They can then be processed by "newsrun". (For C News XT, # just feed them into "rnews".) A batch is ready _iff_ its name # matches the pattern /^[0-9]+\.t$/. The approximate maximum size of # the batches is controlled by $opt_b. "psl-run" does its own locking # via $workdir/$server/LOCK.server files. It does not do any # interlocking with the news system. If successful, the articles file # is updated. Failed groups are marked as unsubscribed. X # In order to grok the dbz history file, this needs perl with dbz # support. Perhaps you need a patch to get this working (see file # perl.patch). This program does not need any privileges to access the # news system. It only needs to read the active and history file. X # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ---------------------------------------------------------------------------- require "getopts.pl"; require "lockfiles.pl"; require "psl-parm.pl"; X # options and defaults $opt_d = 7; # maximum age of articles (0=turn off checking) $opt_b = 1000000; # approx. maximum of batch size # $opt_s # don't check space # $opt_l # accept "local" # $opt_x # don't check history, active # $opt_o # don't send MODE READER # $opt_v # log verbosely # $opt_y # generate sendme controls $opt_t = 20; # transfer timeout (minutes) $opt_c = 250; # article chunk size &Getopts("d:b:slx:ovy:t:c:"); X $server = $ARGV[0] || die "Usage: $0 "; chdir("$workdir/$server") || die "chdir($workdir/$server) failed"; X %daybymonthstr = X ( "Jan", 0, "Feb", 31, "Mar", 59, "Apr", 90, "May", 120, "Jun", 151, X "Jul", 181, "Aug", 212, "Sep", 243, "Oct", 273, "Nov", 304, "Dec", 334 ); @monthstr = X ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ); X # ---------------------------------------------------------------------------- X if (! -s $artfile) { X exit 0; } if (!&lockfile("LOCK.", "server")) { X if ($?<0) { X die "$server: Can't establish lock"; X } else { X print STDERR "$server is locked by pid $?\n" if ($opt_v); X exit 1; X } } if (!$opt_x) { X dbmopen(history, $history, 0664) || die "dbmopen($history)"; } #if ($opt_x<3) { # open(ACT, $active) || die "open($active)"; # while () { # split; # $actgroup{$_[0]} = 1; # } # close ACT; #} open(LOG, ">>$logfile") || die "open($logfile)"; select(LOG); $|=1; X $Sbatch=$Sgread=$Sgfail=$Sgskip=$Saread=$Safail=$Sarej=$Saxpost=$Sbread=0; $starttime = time; @_ = localtime; $_[5]+=1900 if ($_[5]<1901); printf (LOG "\n+++ psl-run %02d.%02d.%04d %02d:%02d\n", X $_[3], ++$_[4], $_[5], $_[2], $_[1]); $cutdate = 365*($_[5]-1900) + $_[7] - $opt_d; $SIG{"HUP"} = "IGNORE"; $SIG{"INT"} = "signal_catch"; $SIG{"TERM"} = "signal_catch"; $SIG{"PIPE"} = "signal_catch"; $SIG{"ALRM"} = "signal_catch"; alarm($opt_t*=60); X socket(NNTP, $AF_INET, $SOCK_STREAM, $IPPROTO_TCP); ($serv,$port) = split(/:/, $server); $port = $PORT_NNTP unless $port; @_ = gethostbyname($serv); if ($?) { X &die_catch("gethostbyname($serv): $?"); } if (!connect(NNTP, pack("S n a4 x8", $AF_INET, $port, $_[4]))) { X &die_catch("connect"); } select(NNTP); $|=1; if (eof(NNTP)) { X &die_catch("connect starting up"); } $_ = ; s/\r\n$//; if (!/^20[01]/) { X &die_catch("server ($_)"); } if (!$opt_o) { X print NNTP "MODE READER\r\n"; X if (eof(NNTP)) { X &die_catch("connection after MODE READER"); X } X $_ = ; s/\r\n$//; X if (!/^[25]0[01]/) { X &die_catch("MODE READER ($_)"); X } } open(SUB, "<$artfile") || do { &die_catch("open($artfile)"); }; while () { X push(@sub, $_); X s/[:! ].*$//; X $subgroup{$_}=1; } close SUB; @ssub=@sub; open(NSUB, ">$artfile+") || do { &die_catch("open($artfile+)"); }; $havensub=1; select(NSUB); $|=1; if (open(TSUB, "<$artfile.last")) { X $lastgroup=; X if ($lastgroup =~ tr/A-Za-z0-9_+.-//c) { X undef $lastgroup; # invalid X } X close TSUB; } X # ---------------------------------------------------------------------------- X &open_batch; subloop: while ($_=shift(@sub)) { X split; $group = shift(@_); X if ($lastgroup) { X if ($group eq $lastgroup) { X undef $lastgroup; X } else { X print NSUB; X ++$Sgskip; X next; X } X } X $latest = pop(@_); split(/[,-]/,$latest); $latest = pop(@_); X if ($group =~ /!/) { X print NSUB; X } else { X $group =~ s/://; X $subline=$_; X &progress($group); X alarm($opt_t); X &nntp_command("GROUP $group"); X if ($status == 211) { X ($numb, $first, $last) = @resp; X if (($last>=$latest) && ($first<=$latest)) { X $first = $latest+1; X } X $enumb = $last-$first+1; X if (($numb>0) && ($enumb>0)) { X &progress(" $numb/$enumb", X " group $group with $numb articles, $enumb new"); X ($f,$l)=($first,$last); X while ($f<=$last) { X if ($last-$f>$opt_c) { X $l=$f+$opt_c; X } else { X $l=$last; X } X &process_group($group, $f, $l); X $subline="$group: 1-$l\n"; X $f=$l+1; X } X undef $subline; X print NSUB "$group: 1-$last\n"; X ++$Sgread; X } else { X &progress(" 0\n", " $group empty or no new articles"); X undef $subline; X print NSUB "$group: 1-$latest\n"; X ++$Sgskip; X } X } else { X &progress(" -G\n", " Failed $group (status=$status)"); X undef $subline; X print NSUB "$group! 1-$latest\n"; X ++$Sgfail; X } X } } if ($lastgroup) { # skipped everything, restart X unlink("$artfile.last"); X @sub=@ssub; X open(NSUB, ">$artfile+") || do { &die_catch("reopen($artfile+)"); }; X undef $lastgroup; $Sgskip=0; X goto subloop; } undef $group; &finish(0); Xexit 0; X # ---------------------------------------------------------------------------- X sub process_group # $group, $first, $last { X local($group, $first, $last)=@_; X undef %dont; X undef %mid; X &progress(" H"); X if (($opt_x<3) && ($group =~ /^(control)|(junk)/)) { # special handling X &get_xhdr("Newsgroups") || return 0; X while ($_=, s/\r\n$//, $_ ne ".") { X eof(NNTP) && &die_catch("connection"); X ($n,$i) = split(/\s+/, $_, 2); X $dont{$n} = "j"; X split(/,/, $i); X while($i=shift(@_)) { X if ($subgroup{$i}) { X undef $dont{$n}; last; X } X } X } X } X if ($opt_x<2) { X &get_xhdr("Message-Id") || return 0; X while ($_=, s/\r\n$//, $_ ne ".") { X eof(NNTP) && &die_catch("connection"); X split(/\s+/, $_, 2); X $mid{$_[0]} = $_[1]; X $dont{$_[0]} = "d" if (($opt_x<1) && ($history{$_[1]})); X if ($gotten{$_[1]}) { X $dont{$_[0]} = "g"; ++$Saxpost; --$Sarej; X } X } X } X if ($opt_d) { X &get_xhdr("Date") || return 0; X while ($_=, s/\r\n$//, $_ ne ".") { X eof(NNTP) && &die_catch("connection"); X split(/\s+/, $_, 2); X $date = &parse_date($_[1]); X $dont{$_[0]} = "o" if ($date < $cutdate); X } X } X if (!$opt_l) { X &get_xhdr("Distribution") || return 0; X while ($_=, s/\r\n$//, $_ ne ".") { X eof(NNTP) && &die_catch("connection"); X split(/\s+/, $_, 2); X $dont{$_[0]} = "l" if ($_[1] eq "local"); X } X } X X &progress(" A"); X $arttime=time; $artsproc = 0; X foreach $anr ($first..$last) { X alarm($opt_t); X &chprogress; X if ($dont{$anr}) { X ++$Sarej; X # print STDOUT $dont{$anr}; X } else { X if ($opt_y) { X if ($mid{$anr}) { X $b = 30; # avg msg-id len X $gotten{$mid{$anr}} = 1; X printf BATCH "%s\n", $mid{$anr}; X } else { X ++$Safail; X next; X } X } else { X &nntp_command("ARTICLE $anr"); X if ($status==220) { X $b = 0; X $gotten{$mid{$anr}} = 1; X print BATCH "#! rnews \n" # 20 chars X || &die_catch("print_head"); X while ($_=, s/\r\n$/\n/, $_ ne ".\n") { X eof(NNTP) && &die_catch("connection"); X s/^\.\././; X $b += length; X print BATCH || &die_catch("print_art"); X } X seek(BATCH, -($b+11), $SEEK_CUR) || &die_catch("seek_cur"); X print BATCH "$b" || &die_catch("print_num"); X seek(BATCH, 0, $SEEK_END) || &die_catch("seek_end"); X } else { X ++$Safail; X next; X } X } X if (($batchsize += $b) > $opt_b) { X &close_batch; &open_batch; X } X ++$artsproc; $Sbread += $b; X } X } X &progress(" $artsproc\n", " $artsproc articles read."); X $Spsecs += (time - $arttime); X $Saread += $artsproc; $artsproc = 0; } X # ---------------------------------------------------------------------------- X sub get_xhdr { X local($h) = @_; X alarm($opt_t); X &nntp_command("XHDR $h $first-$last"); X if ($status != 221) { X &progress("-X$h\n", " Can't read $h headers"); X return 0; X } X &progress("."); X return 1; } X # ---------------------------------------------------------------------------- X sub parse_date # return date as number (of days since 1900) { X local(@_) = split(/\s+/, $_[0]); X # convert form: Fri, 09 Sep 1994 08:18:54 -0500 X local($f) = @_[0] =~ /...,?/; X # standard form: 9 Sep 1994 08:18:31 GMT X # @_[$f+...] 0 1 2 3 4 X local($y) = @_[$f+2]; X $y -= 1900 if ($y >= 1900); X return @_[$f] + $daybymonthstr{"@_[$f+1]"} + 365*$y; } X # ---------------------------------------------------------------------------- X sub nntp_command { X local($_) = join(" ", @_) . "\r\n"; X print NNTP; X eof(NNTP) && &die_catch("connection"); X $_ = ; X s/\r\n$//; X ($status, @resp) = split; } X # ---------------------------------------------------------------------------- X sub progress { X print STDOUT $_[0]; X local($a) = $_[1]; X if ($a) { X print LOG "$a\n" if ($opt_v || ($a =~ /^[^ ]/)) ; X } X $progress = 0; } X sub chprogress { X if ((++$progress) > 50) { X &progress("."); X } } X # ---------------------------------------------------------------------------- X sub open_batch { X if ($dfcommand && !$opt_s) { X open(DF, sprintf("$dfcommand|", $inputdir)) X || &die_catch("df"); X $_ = ; X close DF; X split; X if (1024*$_[$dffield] < 2*$opt_b) { X &die_catch("Filesystem for $inputdir full"); X } X } X foreach $i (0..9) { X ++$batchcount; X $batchname = sprintf("$inputdir/nntp.%05d%04d", $$, $batchcount); X if (!-e $batchname) { X open (BATCH, ">$batchname") X || &die_catch("open $batchname"); X select BATCH; $|=1; X if ($opt_y) { X @_=gmtime; $_[5]+=1900 if ($_[5]<1900); X print BATCH "Path: psl\nNewsgroups: $opt_y\n"; X print BATCH "Control: sendme $hostname\nSubject: sendme\n"; X print BATCH "From: news@$hostname\n"; X printf BATCH "Message-ID: \n", X time, $$, $batchcount, $hostname; X printf BATCH "Date: %d %s %d %02d:%02d:%02d GMT\n\n", X $_[3], $monthstr[$_[4]], $_[5], $_[2], $_[1], $_[0]; X } X $batchsize = 0; X return; X } X } X $batchname = ""; X &die_catch("get batchname"); } X sub close_batch { X return unless ($batchname); X close BATCH; X if ($batchsize) { X ++$Sbatch; X foreach $i (0..9) { X rename($batchname, sprintf("$inputdir/%d.t", time)) && last; X sleep 10-$i; X } X } else { X unlink $batchname; X } X $batchname = ""; } X # ---------------------------------------------------------------------------- X sub finish { X local($st) = @_; X alarm(0); X $SIG{"HUP"} = "IGNORE"; X $SIG{"INT"} = "IGNORE"; X $SIG{"TERM"} = "IGNORE"; X $SIG{"PIPE"} = "IGNORE"; X $SIG{"ALRM"} = "IGNORE"; X &close_batch; X if ($subline) { X if ($anr) { X # interrupted in the middle of a group X printf NSUB "%s: 1-%d\n", $group, $anr-1; X } else { X print NSUB $subline; X } X } X while ($_=shift(@sub)) { X ++$Sgskip; X print NSUB; X } X close NSUB; X if ($group && open(TSUB, ">$artfile.last")) { X print TSUB $group; X close TSUB; X } else { X unlink("$artfile.last"); X } X dbmclose(history) unless $opt_x; X X $Saread += $artsproc; X $Ssecs = time - $starttime; X ++$Ssecs unless $Ssecs; X ++$Spsecs unless $Spsecs; X $Scps = int($Sbread / $Ssecs); X $Spcps = int($Sbread / $Spsecs); X X if ($opt_y) { X &progress("${Sgread}g, ${Saread}a, ${Ssecs}s, ${Sbatch} batches\n", X "Groups: read $Sgread, fail $Sgfail, skip $Sgskip\n". X "Articles: read $Saread, fail $Safail, reject $Sarej, xpost $Saxpost\n". X "batches $Sbatch, seconds $Ssecs"); X } else { X &progress("${Sgread}g, ${Saread}a, ${Sbread}b, ${Ssecs}/${Spsecs}s, ". X "${Scps}/${Spcps} cps, ${Sbatch} batches\n", X "Groups: read $Sgread, fail $Sgfail, skip $Sgskip\n". X "Articles: read $Saread, fail $Safail, reject $Sarej, xpost $Saxpost\n". X "Bytes: read $Sbread, batches $Sbatch, ". X "seconds $Ssecs ($Spsecs), CPS $Scps ($Spcps)"); X } X X close NNTP; close LOG; X X if ($havensub) { X unlink("$artfile.old"); X rename("$artfile", "$artfile.old") X && rename("$artfile+", "$artfile"); X } X unlink("LOCK.server"); X exit $st; } X sub signal_catch { X local($s) = @_; X &progress(" SIG$s\n", "\n$server: Got SIG$s, bailing out"); X &finish(1); } X sub die_catch { X local($s) = @_; X &progress("\nF:$s\n", "\n$server: $s failed ($!), bailing out"); X &finish(1); } X # ---------------------------------------------------------------------------- SHAR_EOF $shar_touch -am 0402180697 'psl/psl-run' && chmod 0555 'psl/psl-run' || echo 'restore of psl/psl-run failed' shar_count="`wc -c < 'psl/psl-run'`" test 14458 -eq "$shar_count" || echo "psl/psl-run: original size 14458, current size $shar_count" fi # ============= psl/psl-aux.pl ============== if test -f 'psl/psl-aux.pl' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl-aux.pl (file already exists)' else echo 'x - extracting psl/psl-aux.pl (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl-aux.pl' && # routines common to the auxiliary programs -*- perl -*- # $Id: psl-aux.pl,v 1.3 1996/02/23 00:21:38 olaf Exp $ X # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ---------------------------------------------------------------------------- X require "lockfiles.pl"; require "psl-parm.pl"; X sub auxinit { X local($name) = @_; X chdir("$newslib") || X die "chdir($newslib) failed"; X $SIG{"HUP"} = "IGNORE"; X $SIG{"INT"} = "signal_catch"; X $SIG{"TERM"} = "signal_catch"; X $SIG{"PIPE"} = "signal_catch"; X while (1) { X if (&lockfile("LOCK", "")) { X last; X } X if ($?<0) { X die "Can't lock the news system"; X } else { X print "News system locked, waiting...\n"; X sleep 10; X } X } X open(LOG, ">>$globlog") || die "open log"; X select(LOG); $|=1; X $d=`date`; print LOG "## $name << $d"; X select(STDOUT); $|=1; X X print STDOUT "<< "; X &Read("active", active); X &Read("active.times", activetimes); X &Read("newsgroups", newsgroups); X print STDOUT "\n"; X $tstr = sprintf("%d %s@%s (%s)", time, $logname, $hostname, $name); } X sub auxexit { X local($dowrite) = @_; X print STDOUT "\n"; X $d=`date`; print LOG "## >> $d\n"; X $SIG{"INT"} = "IGNORE"; X $SIG{"TERM"} = "IGNORE"; X $SIG{"PIPE"} = "IGNORE"; X if ($dowrite) { X print STDOUT ">> "; X &Write("active", active, " "); X &Write("active.times", activetimes, " "); X &Write("newsgroups", newsgroups, "\t"); X print STDOUT "\n"; X foreach $i ("active", "newsgroups") { X unlink("${i}.o"); X rename("$i", "${i}.o") && rename("${i}+", "$i"); X } X unlink("active.times.o"); X rename("active.times", "active.times.o") && X system("sort -n +1 active.times+ > active.times") || X unlink("active.times+"); X } X unlink("LOCK"); X exit 0; } X sub addgroup { X local($g,$f) = @_; X print LOG "created $g $f\n"; X $active{$g} = "0000000000 00001 $f"; X $activetimes{$g} = $tstr; X $newsgroups{$g} = ($f eq "m") ? "(Moderated)" : ""; } X sub delgroup { X local($g) = @_; X print LOG "deleted $g\n"; X $deleted{$g} = 1; X if (open(TBD, ">>$newslib/dirs.tbd")) { X $g =~ tr:.:/:; X print TBD "$g\n"; X close TBD; X } } X X # ---------------------------------------------------------------------------- X sub Read { X local($fn,*ar) = @_; X open(F, "<$fn") || die "open $fn"; X print STDOUT "$fn "; X while() { X chop; X ($g,$x) = split(/[ \t]+/, $_, 2); X $ar{$g} = $x; X } X close F; } X sub Write { X local($fn,*ar,$s) = @_; X open(F, ">$fn+") || die "open $fn+"; X print STDOUT "$fn "; X foreach $i (sort keys %ar) { X if (!$deleted{$i}) { X printf(F "%s%s%s\n", $i, $s, $ar{$i}); X } X } X close F; } X sub signal_catch { X print LOG "\n## break\n"; X unlink("LOCK"); X exit 1; } X sub die_catch { X print STDERR "\n$_[0]"; X print LOG "\n## $_[0]"; X unlink("LOCK"); X exit 1; } X # ---------------------------------------------------------------------------- X sub open_batch { X if ($dfcommand && !$opt_s) { X open(DF, sprintf("$dfcommand|", $inputdir)) X || &die_catch("df"); X $_ = ; X close DF; X split; X if (1024*$_[$dffield] < 2*$opt_b) { X &die_catch("Filesystem for $inputdir full"); X } X } X while (1) { X ++$batchcount; X $batchname = sprintf("$inputdir/mvj.%05d%04d", $$, $batchcount); X last if (!-e $batchname); X } X open (BATCH, ">$batchname") X || &die_catch("open $batchname"); X select BATCH; $|=1; X $batchsize = 0; } X sub close_batch { X close BATCH; X if ($batchsize) { X ++$Sbatch; X while (1) { X rename($batchname, sprintf("$inputdir/%d.t", time)) && last; X sleep 2; X } X } else { X unlink $batchname; X } } X # ---------------------------------------------------------------------------- 1; SHAR_EOF $shar_touch -am 0223012396 'psl/psl-aux.pl' && chmod 0444 'psl/psl-aux.pl' || echo 'restore of psl/psl-aux.pl failed' shar_count="`wc -c < 'psl/psl-aux.pl'`" test 4170 -eq "$shar_count" || echo "psl/psl-aux.pl: original size 4170, current size $shar_count" fi # ============= psl/lockfiles.pl ============== if test -f 'psl/lockfiles.pl' && test X"$1" != X"-c"; then echo 'x - skipping psl/lockfiles.pl (file already exists)' else echo 'x - extracting psl/lockfiles.pl (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/lockfiles.pl' && package lockfiles; # Lock file handling. -*- perl -*- # $Id: lockfiles.pl,v 2.1 1995/03/26 19:18:49 olaf Exp $ X # Call &lockfile(base, name) e.g. &lockfile("/var/locks/LCK..", "ttyS1") # to establish a lock. Returns 1 if locking successful, 0 if not. # In latter case, $? is set to the PID of the holding process if locked # by another process, negative (-errno or something) if an error occurred. X # Parameters to set: $lockfiles'maxtries: How many times to try if failed # in-between, $lockfiles'wait: How long to wait. This does not apply if # a lock is already there ($?>0 return). X sub main'lockfile { X $tmp = "$_[0].$$"; $lock = "$_[0]$_[1]"; X $locktries = 0; X if (!open(LOCK, ">$tmp")) { X $? = -$!; return 0; X } X print LOCK "$$"; close LOCK; X while (!link($tmp, $lock)) { X if (open(LOCK, "<$lock")) { X $lockpid = ; close LOCK; X if (kill(0, $lockpid)) { X unlink($tmp); X $? = $lockpid; return 0; X } X unlink($lock); # break stale lock X } X if (++$locktries > $maxtries) { X unlink($tmp); X $? = -1; return 0; X } X sleep $wait; X } X unlink($tmp); X return 1; } X $maxtries = 10; $wait = 2; 1; SHAR_EOF $shar_touch -am 1105141195 'psl/lockfiles.pl' && chmod 0444 'psl/lockfiles.pl' || echo 'restore of psl/lockfiles.pl failed' shar_count="`wc -c < 'psl/lockfiles.pl'`" test 1241 -eq "$shar_count" || echo "psl/lockfiles.pl: original size 1241, current size $shar_count" fi # ============= psl/mkid.pl ============== if test -f 'psl/mkid.pl' && test X"$1" != X"-c"; then echo 'x - skipping psl/mkid.pl (file already exists)' else echo 'x - extracting psl/mkid.pl (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/mkid.pl' && package mkid; # generate a provably unique name (file, message-id) -*- perl -*- # $Id: mkid.pl,v 2.1 1995/03/26 19:18:49 olaf Exp $ X # Call &mkid to get an unique identifier. # If wanted, set the mode: # &mkid'mode(0): generates names that are 13 chars and all lower case (default) # &mkid'mode(1): generates names that are 11 chars and case sensitive X sub mode { X if (@_[0]) { X $BaseChars="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-"; X $InBetween=","; $Bits=6; $Mask=63; $Rep=4; X } else { X $BaseChars="0123456789abcdefghijklmnopqrstuv"; X $InBetween="-"; $Bits=5; $Mask=31; $Rep=5; X } } X sub main'mkid { X local($t,$u) = (time>>2, ($$<<14)+(++$IntCount)); X return &base192($t).$InBetween.&base192($u); } # Sufficient conditions for duplicates: a PID gets reused in 4 seconds # or: this routine is called 16384 times in 4 seconds (both unlikely...) X sub base192 # 30 bit number -> 5 or 6 char string { X local ($i,$j,$a) = (0,$_[0],""); X foreach $i (0...$Rep) { X $a .= substr($BaseChars, $j&$Mask, 1); X $j >>= $Bits; X } X return $a; } X &mode(0); 1; SHAR_EOF $shar_touch -am 1105141195 'psl/mkid.pl' && chmod 0444 'psl/mkid.pl' || echo 'restore of psl/mkid.pl failed' shar_count="`wc -c < 'psl/mkid.pl'`" test 1107 -eq "$shar_count" || echo "psl/mkid.pl: original size 1107, current size $shar_count" fi # ============= psl/addgrps ============== if test -f 'psl/addgrps' && test X"$1" != X"-c"; then echo 'x - skipping psl/addgrps (file already exists)' else echo 'x - extracting psl/addgrps (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/addgrps' && #!/usr/bin/perl # add unknown groups from foreign active files -*- perl -*- X # Examine the active file(s) given as input for unknown groups and add # them to your own active file. X # Olaf Titz, Sep. 95. Public domain. # $Id: addgrps,v 1.2 1996/02/23 00:21:38 olaf Exp $ # ---------------------------------------------------------------------------- X require "psl-aux.pl"; require "getopts.pl"; X &auxinit("addgrps"); &Getopts("cm"); X $havenew=0; while(<>) { X if (++$progress>50) { X print STDOUT "."; $progress=0; X } X ($g,$x,$y,$f) = split; X if (!$active{$g}) { X &addgroup($g,$f); X ++$havenew; X } } X # Bonus feature: Check newsgroups, active.times consistency if ($opt_c) { X $Cat=$Can=$Cdt=$Cdn=0; X foreach $i (keys %active) { X next if ($i =~ /^(control|general|junk)/); X ($x,$y,$f) = split(/ /, $active{$i}); X next unless ($f =~ /^[ymn]/); X if (!$activetimes{$i}) { X $activetimes{$i} = $tstr; X ++$Cat; X } X while ($newsgroups{$i} =~ s/\s*\(Moderated\)$//i) {}; X if ($f eq "m") { X $newsgroups{$i} .= " (Moderated)"; X } X } # foreach $i (keys %activetimes) { # if (!$active{$i}) { # delete $activetimes{$i}; # ++$Cdt; # } # } X foreach $i (keys %newsgroups) { X if (!$active{$i}) { X delete $newsgroups{$i}; X ++$Cdn; X } X } X print LOG "Corrected:"; X print LOG " added $Cat to active.times, $Can to newsgroups\n"; X print LOG " deleted $Cdt from active.times, $Cdn from newsgroups.\n"; } X # If X and X.misc exist, alias X to X.misc if ($opt_m) { X $Cal=0; X foreach $i (keys %active) { X next if ($i =~ /^(control|general|junk)/); X ($x,$y,$f) = split(/ /, $active{$i}); X next unless ($f =~ tr/ymn//); X $m = "${i}.misc"; X if ($n=$active{$m}) { X ($x2,$y2,$f2) = split(/ /, $n); X if ($f2 eq $f) { X print LOG "aliased $i to $m\n"; X $active{$i} = "$x $y =$m"; X ++$Cal; X } X } X } X print LOG "Corrected: aliased $Cal\n"; } X print LOG "Added $havenew groups.\n"; &auxexit($havenew||$Cat||$Can||$Cdt||$Cdn||$Cdl||$Cal); unlink("LOCK"); Xexit 0; SHAR_EOF $shar_touch -am 0223012396 'psl/addgrps' && chmod 0555 'psl/addgrps' || echo 'restore of psl/addgrps failed' shar_count="`wc -c < 'psl/addgrps'`" test 2039 -eq "$shar_count" || echo "psl/addgrps: original size 2039, current size $shar_count" fi # ============= psl/xmvjunk ============== if test -f 'psl/xmvjunk' && test X"$1" != X"-c"; then echo 'x - skipping psl/xmvjunk (file already exists)' else echo 'x - extracting psl/xmvjunk (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/xmvjunk' && #!/usr/bin/perl # move junked articles -*- perl -*- X # Look for articles in "junk", ask to create the newsgroups they occupy, # and repost the junked articles locally. # The reposting is done by leaving batches in in.coming. # The program asks interactively for the newsgroups flags, leave it empty to # not create a group. Parameter -n for not creating groups at all, # -r to remove junk articles after reposting. X # Assembled from spare parts of the psl project by Olaf Titz, Jun.95. # $Id: xmvjunk,v 1.6 1995/12/03 18:45:37 olaf Exp $ X # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ---------------------------------------------------------------------------- X require "getopts.pl"; require "psl-aux.pl"; X $opt_b = 1000000; # approx. maximum of batch size &Getopts("rnb:"); if (!-t STDIN) { X $opt_n=1; } &auxinit("xmvjunk"); X @arts=(<$newsarts/junk/[1-9]*>); $progress=50; X # First pass: Collect unknown newsgroups foreach $art (@arts) { X if (++$progress>50) { X print STDOUT "."; $progress=0; X } X if (open(A, $art)) { X while() { X /^Newsgroups: (.*)$/i && do { X split(/,/, $1); X foreach $i (@_) { X $newact{$i}=1 unless $active{$i}; X } X last; X }; X } X close A; X } else { X print STDOUT "?"; X } } X # Ask for generation of new groups $havenew=0; if (!$opt_n) { X @g = (sort keys %newact); X $tstr = sprintf("%d %s@%s (mvjunk)", time, $logname, $hostname); X if ($#g>=0) { X print STDOUT "\nEnter newsgroup flag for group or empty:\n"; X foreach $i (@g) { X Input: while (1) { X print STDOUT "$i "; X $_=; y/\r\n//d; X /^=(.*)$/ && do { last Input if ($active{$1}); }; X last Input if (/^[ymn]?$/); X } X if ($_) { X &addgroup($i,$_); X ++$havenew; X } X } X } } X # Second pass: Repost and remove $progress=50; $count=0; &open_batch; Article: foreach $art (@arts) { X if (++$progress>50) { X print STDOUT "."; $progress=0; X } X if (open(A, $art)) { X $inhdr=1; $article=""; $dothis=0; X Line: while() { X y/\n//d; X if ($inhdr) { X /^Newsgroups:\s+(.*)$/i && do { X split(/,/, $1); X foreach $i (@_) { X next Article if ($i eq "junk"); X # don't repost to "junk" X if ($active{$i}) { X $dothis=1; X } X } X }; X /^Message-ID:\s+<([^>]+)>$/i && do { X $_="Message-ID: "; X }; X /^Distribution:/i && next Line; X /^$/ && do { X next Article unless $dothis; X $inhdr=0; X $_="Distribution: local\n"; X }; X } X $article .= "$_\n"; X } X close A; X ++$count; X $s=length($article); X $batchsize+=$s; X if ($opt_r) { X unlink($art); X } X if ($batchsize>$opt_b) { X &close_batch; &open_batch; X } X print BATCH "#! rnews $s\n$article"; X } else { X print STDOUT "?"; X } } &close_batch; X print LOG "$count articles reposted.\n"; &auxexit($havenew); unlink("LOCK"); Xexit 0; SHAR_EOF $shar_touch -am 1203195795 'psl/xmvjunk' && chmod 0555 'psl/xmvjunk' || echo 'restore of psl/xmvjunk failed' shar_count="`wc -c < 'psl/xmvjunk'`" test 3289 -eq "$shar_count" || echo "psl/xmvjunk: original size 3289, current size $shar_count" fi # ============= psl/docheckg ============== if test -f 'psl/docheckg' && test X"$1" != X"-c"; then echo 'x - skipping psl/docheckg (file already exists)' else echo 'x - extracting psl/docheckg (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/docheckg' && #!/usr/bin/perl # checkgroups backend -*- perl -*- X # Process a checkgroups report, as given by C News Cleanup Release. X # Assembled from spare parts of the psl project by Olaf Titz, Nov.95. # $Id: docheckg,v 1.3 1996/02/23 00:21:38 olaf Exp $ X # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ---------------------------------------------------------------------------- X require "psl-aux.pl"; &auxinit("docheckgroups"); X $tstr = sprintf("%d %s@%s (checkgroups)", time, $logname, $hostname); $stat=""; $havenew=$havedel=$havechg=0; while(<>) { X chop; X /^$/ && next; X /^obsolete groups/ && do { X $stat="del"; next; X }; X /^new groups/ && do { X $stat="new"; next; X }; X /^groups needing moderation/ && do { X $stat="sta"; next; X }; X ($g, $f) = split; X if ($stat eq "new") { X if (!$active{$g}) { X &addgroup($g,$f); X ++$havenew; X } X } elsif ($stat eq "del") { X ++$havedel; X &delgroup($g); X } elsif ($stat eq "sta") { X if ($active{$g}) { X ++$havechg; X print LOG "changed status $g $f\n"; X split(/ /,$active{$g}); X $_[2] = $f; X $active{$g} = join(" ", @_); X } X } } X close TBD; print LOG "added $havenew, deleted $havedel, changed $havechg groups.\n"; &auxexit($havenew+$havedel+$havechg); unlink("LOCK"); Xexit 0; SHAR_EOF $shar_touch -am 0223012396 'psl/docheckg' && chmod 0555 'psl/docheckg' || echo 'restore of psl/docheckg failed' shar_count="`wc -c < 'psl/docheckg'`" test 1758 -eq "$shar_count" || echo "psl/docheckg: original size 1758, current size $shar_count" fi # ============= psl/psl-parm.proto ============== if test -f 'psl/psl-parm.proto' && test X"$1" != X"-c"; then echo 'x - skipping psl/psl-parm.proto (file already exists)' else echo 'x - extracting psl/psl-parm.proto (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/psl-parm.proto' && # psl-parm -*- perl -*- X # Global parameters common to the psl programs, system constants etc. # $Id: psl-parm.proto,v 1.1 1995/11/05 13:10:52 olaf Exp $ X # Where the server control directories live $workdir = "/var/lib/news/nntp"; X # Global files and directories $newslib = "/var/lib/news"; $newsarts = "/var/spool/news"; $inputdir = "/var/spool/news/in.coming"; X # How to get free size of a filesystem. # Command that produces a line like this, measured in kilobytes: # /dev/hda6 308951 278677 14827 95% /var # where the free size is at field $dffield ^^^ (here this is field 3) # This command has to take an argument and output the rating of the # given filesystem in the only line. Undef if you don't have such a beast. $dfcommand = "/bin/df -kP %s | tail -1"; # this is for gnu df $dffield = 3; X # Should be set in environment, but... $hostname = `hostname`; chop $hostname; $logname = getpwuid($<); X # Standard news files $history = "$newslib/history"; $active = "$newslib/active"; X # Files under $workdir/$server. No need to change these $logfile = "log"; $artfile = "articles"; $subfile = "subscriptions"; $sactfile = "sactive"; # ...and under $newslib $globlog = "psl.log"; X # constants from system headers etc., check if they are correct $AF_INET = 2; $SOCK_STREAM = 1; $WNOHANG = 1; $SEEK_SET = 0; $SEEK_CUR = 1; $SEEK_END = 2; # these are "Assigned Numbers" $IPPROTO_TCP = 6; $PORT_NNTP = 119; X umask 002; select(STDOUT); $|=1; 1; X SHAR_EOF $shar_touch -am 1105141295 'psl/psl-parm.proto' && chmod 0444 'psl/psl-parm.proto' || echo 'restore of psl/psl-parm.proto failed' shar_count="`wc -c < 'psl/psl-parm.proto'`" test 1538 -eq "$shar_count" || echo "psl/psl-parm.proto: original size 1538, current size $shar_count" fi # ============= psl/perl.patch ============== if test -f 'psl/perl.patch' && test X"$1" != X"-c"; then echo 'x - skipping psl/perl.patch (file already exists)' else echo 'x - extracting psl/perl.patch (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/perl.patch' && The following is a patch I had to apply to perl 4.036 to get it working with dbz. perl has the necessary hooks but apparently contains an off-by-one error. (Or dbz contains this error; I'm using the dbz from my C News: 43779 Jul 23 1993 dbz.c) The different handling of the dbz vs. dbm fetch return value does not matter for psl. X Apply this patch in the perl source directory, then run "make dbzperl". Non-dbz perl is not affected by the patch. X --- perl.h.orig Sun Dec 11 19:27:39 1994 +++ perl.h Sun Dec 11 15:04:59 1994 @@ -302,12 +302,12 @@ X #ifdef WANT_DBZ X #include X #define SOME_DBM -#define dbm_fetch(db,dkey) fetch(dkey) +#define dbm_fetch(db,dkey) dbzfetch(dkey) X #define dbm_delete(db,dkey) fatal("dbz doesn't implement delete") -#define dbm_store(db,dkey,dcontent,flags) store(dkey,dcontent) +#define dbm_store(db,dkey,dcontent,flags) dbzstore(dkey,dcontent) X #define dbm_close(db) dbmclose() X #define dbm_firstkey(db) (fatal("dbz doesn't implement traversal"),fetch()) -#define nextkey() (fatal("dbz doesn't implement traversal"),fetch()) +#define nextkey(db) (fatal("dbz doesn't implement traversal"),fetch()) X #define dbm_nextkey(db) (fatal("dbz doesn't implement traversal"),fetch()) X #ifdef HAS_NDBM X #undef HAS_NDBM --- hash.c.orig Sun Dec 11 19:28:12 1994 +++ hash.c Sun Dec 11 19:25:59 1994 @@ -100,6 +100,9 @@ X if (tb->tbl_dbm) { X dkey.dptr = key; X dkey.dsize = klen; +#ifdef WANT_DBZ + dkey.dsize++; /* broken in dbz? -ot */ +#endif X #ifdef HAS_GDBM X dcontent = gdbm_fetch(tb->tbl_dbm,dkey); X #else @@ -612,6 +615,9 @@ X char *fname; X int mode; X { +#ifdef WANT_DBZ + dbzincore(1); +#endif X if (!tb) X return FALSE; X #ifdef HAS_ODBM @@ -694,6 +700,9 @@ X dkey.dsize = klen; X dcontent.dptr = str_get(str); X dcontent.dsize = str->str_cur; +#ifdef WANT_DBZ + dkey.dsize++; /* broken in dbz? -ot */ +#endif X #ifdef HAS_GDBM X error = gdbm_store(tb->tbl_dbm, dkey, dcontent, GDBM_REPLACE); X #else X $Id: perl.patch,v 2.1 1995/03/26 19:18:49 olaf Exp $ SHAR_EOF $shar_touch -am 1105141195 'psl/perl.patch' && chmod 0444 'psl/perl.patch' || echo 'restore of psl/perl.patch failed' shar_count="`wc -c < 'psl/perl.patch'`" test 2007 -eq "$shar_count" || echo "psl/perl.patch: original size 2007, current size $shar_count" fi # ============= psl/substman.pl ============== if test -f 'psl/substman.pl' && test X"$1" != X"-c"; then echo 'x - skipping psl/substman.pl (file already exists)' else echo 'x - extracting psl/substman.pl (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/substman.pl' && #!/usr/bin/perl require "./psl-parm.pl"; while (<>) { X s:__WORKDIR:$workdir:go; X s:__NEWSLIB:$newslib:go; X s:__INPUTDIR:$inputdir:go; } SHAR_EOF $shar_touch -am 0222235796 'psl/substman.pl' && chmod 0444 'psl/substman.pl' || echo 'restore of psl/substman.pl failed' shar_count="`wc -c < 'psl/substman.pl'`" test 139 -eq "$shar_count" || echo "psl/substman.pl: original size 139, current size $shar_count" fi # ============= psl/Makefile ============== if test -f 'psl/Makefile' && test X"$1" != X"-c"; then echo 'x - skipping psl/Makefile (file already exists)' else echo 'x - extracting psl/Makefile (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/Makefile' && # Makefile for psl # This does little more than provide a convenient installation X # Customize here X BINDIR=/var/lib/news/psl MANDIR=/usr/local/man/man1 PERL=/usr/bin/perl DBZPERL=/usr/local/bin/dbzperl X # End of user-serviceable part X FILES= psl.README psl.1 psl-tools.1 \ X psl-init psl-salvage psl-subscribe psl-run \ X psl-aux.pl lockfiles.pl mkid.pl addgrps xmvjunk docheckg \ X psl-parm.proto perl.patch substman.pl Makefile COPYING GFILES= getnews/README \ X getnews/getnews getnews/getnews-setup getnews/setup.mesg \ X getnews/setup.german.mesg getnews/messages getnews/mbc \ X getnews/Makefile X PROGSP=psl-init psl-salvage psl-subscribe addgrps xmvjunk docheckg PROGSD=psl-run PROGSN=psl-parm.pl psl-aux.pl lockfiles.pl PROGS=$(PROGSP) $(PROGSD) $(PROGSN) MANS=psl.1 psl-tools.1 X all: $(PROGS) X getnews: X cd getnews && $(MAKE) all PERL=$(PERL) X install: all X [ -d $(BINDIR) ] || mkdir $(BINDIR) X for i in $(PROGSP); do \ X echo "#!$(PERL) -I$(BINDIR)" > $(BINDIR)/$$i; \ X grep -v '^\#!' $$i >> $(BINDIR)/$$i; \ X chmod +rx $(BINDIR)/$$i; \ X done X for i in $(PROGSD); do \ X echo "#!$(DBZPERL) -I$(BINDIR)" > $(BINDIR)/$$i; \ X grep -v '^\#!' $$i >> $(BINDIR)/$$i; \ X chmod +rx $(BINDIR)/$$i; \ X done X for i in $(PROGSN); do cp $$i $(BINDIR); chmod +r $(BINDIR)/$$i; done X for i in $(MANS); do \ X $(PERL) substman.pl $$i > $(MANDIR)/$$i; \ X chmod a+r $(MANDIR)/$$i; \ X done X install.getnews: X cd getnews; \ X $(MAKE) install BINDIR=$(BINDIR) MANDIR=$(MANDIR) PERL=$(PERL) X psl-parm.pl: psl-parm.proto X cp psl-parm.proto psl-parm.pl X chmod u+w psl-parm.pl X @echo "First configure psl-parm.pl!" X @exit 1 X psl.tar: $(FILES) X tar cvf psl.tar $(FILES) X shar: $(FILES) $(GFILES) X cd ..; shar $(patsubst %,psl/%, $(FILES) $(GFILES)) > psl.shar X post: shar X cat Blurb ../psl.shar > psl.post X clean: X rm -f psl-parm.pl psl.post psl.tar *~ *.msg X cd getnews; $(MAKE) clean X co.l: X co -l $(FILES) X ci: $(FILES) X ci -u $(FILES) X ci.f: $(FILES) .VERSION .STATE X ci -n `cat .VERSION` -s `cat .STATE` -u $(FILES) SHAR_EOF $shar_touch -am 0402180697 'psl/Makefile' && chmod 0444 'psl/Makefile' || echo 'restore of psl/Makefile failed' shar_count="`wc -c < 'psl/Makefile'`" test 2012 -eq "$shar_count" || echo "psl/Makefile: original size 2012, current size $shar_count" fi # ============= psl/COPYING ============== if test -f 'psl/COPYING' && test X"$1" != X"-c"; then echo 'x - skipping psl/COPYING (file already exists)' else echo 'x - extracting psl/COPYING (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/COPYING' && X GNU GENERAL PUBLIC LICENSE X Version 2, June 1991 X X Copyright (C) 1989, 1991 Free Software Foundation, Inc. X 675 Mass Ave, Cambridge, MA 02139, USA X Everyone is permitted to copy and distribute verbatim copies X of this license document, but changing it is not allowed. X X Preamble X X The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. X X When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. X X To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. X X For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. X X We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. X X Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. X X Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. X X The precise terms and conditions for copying, distribution and modification follow. X X GNU GENERAL PUBLIC LICENSE X TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION X X 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". X Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. X X 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. X You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. X X 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: X X a) You must cause the modified files to carry prominent notices X stating that you changed the files and the date of any change. X X b) You must cause any work that you distribute or publish, that in X whole or in part contains or is derived from the Program or any X part thereof, to be licensed as a whole at no charge to all third X parties under the terms of this License. X X c) If the modified program normally reads commands interactively X when run, you must cause it, when started running for such X interactive use in the most ordinary way, to print or display an X announcement including an appropriate copyright notice and a X notice that there is no warranty (or else, saying that you provide X a warranty) and that users may redistribute the program under X these conditions, and telling the user how to view a copy of this X License. (Exception: if the Program itself is interactive but X does not normally print such an announcement, your work based on X the Program is not required to print an announcement.) X These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. X Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. X In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. X X 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: X X a) Accompany it with the complete corresponding machine-readable X source code, which must be distributed under the terms of Sections X 1 and 2 above on a medium customarily used for software interchange; or, X X b) Accompany it with a written offer, valid for at least three X years, to give any third party, for a charge no more than your X cost of physically performing source distribution, a complete X machine-readable copy of the corresponding source code, to be X distributed under the terms of Sections 1 and 2 above on a medium X customarily used for software interchange; or, X X c) Accompany it with the information you received as to the offer X to distribute corresponding source code. (This alternative is X allowed only for noncommercial distribution and only if you X received the program in object code or executable form with such X an offer, in accord with Subsection b above.) X The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. X If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. X X 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. X X 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. X X 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. X X 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. X If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. X It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. X This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. X X 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. X X 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. X Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. X X 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. X X NO WARRANTY X X 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. X X 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. X X END OF TERMS AND CONDITIONS X X Appendix: How to Apply These Terms to Your New Programs X X If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. X X To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. X X X Copyright (C) 19yy X X This program is free software; you can redistribute it and/or modify X it under the terms of the GNU General Public License as published by X the Free Software Foundation; either version 2 of the License, or X (at your option) any later version. X X This program is distributed in the hope that it will be useful, X but WITHOUT ANY WARRANTY; without even the implied warranty of X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X GNU General Public License for more details. X X You should have received a copy of the GNU General Public License X along with this program; if not, write to the Free Software X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X Also add information on how to contact you by electronic and paper mail. X If the program is interactive, make it output a short notice like this when it starts in an interactive mode: X X Gnomovision version 69, Copyright (C) 19yy name of author X Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. X This is free software, and you are welcome to redistribute it X under certain conditions; type `show c' for details. X The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. X You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: X X Yoyodyne, Inc., hereby disclaims all copyright interest in the program X `Gnomovision' (which makes passes at compilers) written by James Hacker. X X , 1 April 1989 X Ty Coon, President of Vice X This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. SHAR_EOF $shar_touch -am 0609233095 'psl/COPYING' && chmod 0444 'psl/COPYING' || echo 'restore of psl/COPYING failed' shar_count="`wc -c < 'psl/COPYING'`" test 17982 -eq "$shar_count" || echo "psl/COPYING: original size 17982, current size $shar_count" fi # ============= psl/getnews/README ============== if test ! -d 'psl/getnews'; then echo 'x - creating directory psl/getnews' mkdir 'psl/getnews' fi if test -f 'psl/getnews/README' && test X"$1" != X"-c"; then echo 'x - skipping psl/getnews/README (file already exists)' else echo 'x - extracting psl/getnews/README (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/getnews/README' && A simple-to-use front end for psl, used to implement offline reading on a multi-user system. Assumes that psl-parm.pl has $workdir = "$ENV{HOME}/News"; $inputdir = "$workdir/in.coming"; Works without access to a news system and without any privileges. X getnews and getnews-setup have to be customized! X To make this boring stuff interesting from a software engineering point of view, I've added a scheme for multilingual messages in shell scripts. Rip this off if you want, this directory is public domain. :-) SHAR_EOF $shar_touch -am 1127232995 'psl/getnews/README' && chmod 0444 'psl/getnews/README' || echo 'restore of psl/getnews/README failed' shar_count="`wc -c < 'psl/getnews/README'`" test 513 -eq "$shar_count" || echo "psl/getnews/README: original size 513, current size $shar_count" fi # ============= psl/getnews/getnews ============== if test -f 'psl/getnews/getnews' && test X"$1" != X"-c"; then echo 'x - skipping psl/getnews/getnews (file already exists)' else echo 'x - extracting psl/getnews/getnews (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/getnews/getnews' && #!/bin/sh X # Customize this! psldir="/user/uknf/bin" serv="my.server" X cmpr=$1 PATH=/bin:/usr/bin:$psldir:/usr/local/bin; export PATH cd $HOME/News ob=`find in.coming -name '[0-9]*' -mtime +10 -print` [ "$ob" ] && { X echo "Removing old batches: $ob" X rm -f $ob } X psl-run -s -d0 -x3 -b300000 $serv >/dev/null X f=`find in.coming -name '[0-9]*.t' -print` [ "$f" ] && { X seqfil=in.coming/.SEQ X seq=`cat $seqfil 2>/dev/null` X [ "$seq" ] || seq=0 X seq=`expr $seq + 1` X echo $seq > $seqfil X for i in $f; do X s=in.coming/$seq.b X case $cmpr in X gzip) X echo "#! gunbatch" > $s X gzip -cf $i >> $s && rm -f $i X ;; X compress) X echo "#! cunbatch" > $s X compress -c $i >> $s && rm -f $i X ;; X *) X mv -f $i $s X ;; X esac X done } X lf=`ls -l $serv/log 2>/dev/null | awk '{print $5}'` [ "$lf" ] || lf=0 [ "$lf" -gt 10000 ] && { X echo "Warning: $serv/log is getting big" } Xexit 0 SHAR_EOF $shar_touch -am 1127232195 'psl/getnews/getnews' && chmod 0444 'psl/getnews/getnews' || echo 'restore of psl/getnews/getnews failed' shar_count="`wc -c < 'psl/getnews/getnews'`" test 881 -eq "$shar_count" || echo "psl/getnews/getnews: original size 881, current size $shar_count" fi # ============= psl/getnews/getnews-setup ============== if test -f 'psl/getnews/getnews-setup' && test X"$1" != X"-c"; then echo 'x - skipping psl/getnews/getnews-setup (file already exists)' else echo 'x - extracting psl/getnews/getnews-setup (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/getnews/getnews-setup' && #!/bin/sh X # Customize this! msgdir="/user/uknf/bin" serv="my.server" X msgname="setup" msgvers="2" source $msgdir/messages MSG 1 MSG 2 read cd $HOME X md () { X [ -d $1 ] || { X MSG 3 "$1" X mkdir $1 || { MSG 4; exit 1 } X } } X md News md News/in.coming md News/$serv sf="News/$serv/articles" MSG 5 "$sf" [ -f $sf ] || { X MSG 6 "$sf" X echo "news.announce.important: 1" > $sf } MSG 2 read MSG 7 "News/$serv" SHAR_EOF $shar_touch -am 1116191995 'psl/getnews/getnews-setup' && chmod 0555 'psl/getnews/getnews-setup' || echo 'restore of psl/getnews/getnews-setup failed' shar_count="`wc -c < 'psl/getnews/getnews-setup'`" test 404 -eq "$shar_count" || echo "psl/getnews/getnews-setup: original size 404, current size $shar_count" fi # ============= psl/getnews/setup.mesg ============== if test -f 'psl/getnews/setup.mesg' && test X"$1" != X"-c"; then echo 'x - skipping psl/getnews/setup.mesg (file already exists)' else echo 'x - extracting psl/getnews/setup.mesg (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/getnews/setup.mesg' && X.:setup:2: X.1 X X getnews - not copyright 1995 Olaf Titz X "getnews" is a psl front-end that fetches news articles from a NNTP server and makes batches out of them. It can be run by cron or similar; the batches can be transferred to your home machine and read with your favourite offline reader that understands "rnews" batches. X "getnews" is set up to work the following way: (Every file name is relative to your home directory) News/in.coming contains the batches. News/(servername) contains the psl working files. Thus, this setup program creates these directories and leaves instructions on how to proceed. X.2 X Continue - press Return X.3 Setting up directory %1... X.4 mkdir failed - something's wrong! X.5 X You need a .newsrc-formatted file as %1. This file contains the groups you want to read via "getnews". Copy over your .newsrc or old getnewsrc (where applicable) and edit out all the groups you don't want. X.6 X This setup program leaves a file %1 containing just one line to show you how it works. X.7 X Okay. All worked well. Now you can run "getnews". X You need to know the following: - The batch files have names ending in '.b'. Every such file in X News/in.coming is a batch file. The names are unique and will not be X repeated. X If you run automatic file transfer processes, set them up to X recognize these file names. Remove the batch files after transfer X or your quota will fill up. :-) - But getnews will remove batches older than 10 days. - The %1 dir contains a log file. X "getnews" will complain if this grows too much. You can look at it X and/or delete it as you like. - You don't need to fiddle with psl-subscribe etc. X SHAR_EOF $shar_touch -am 1116185595 'psl/getnews/setup.mesg' && chmod 0444 'psl/getnews/setup.mesg' || echo 'restore of psl/getnews/setup.mesg failed' shar_count="`wc -c < 'psl/getnews/setup.mesg'`" test 1671 -eq "$shar_count" || echo "psl/getnews/setup.mesg: original size 1671, current size $shar_count" fi # ============= psl/getnews/setup.german.mesg ============== if test -f 'psl/getnews/setup.german.mesg' && test X"$1" != X"-c"; then echo 'x - skipping psl/getnews/setup.german.mesg (file already exists)' else echo 'x - extracting psl/getnews/setup.german.mesg (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/getnews/setup.german.mesg' && X.:setup:2: X.1 X X getnews - not copyright 1995 Olaf Titz X "getnews" liest Newsartikel vom NNTP-Server und erzeugt daraus Batches. Es kann aus der crontab etc. gestartet werden. Die Batches koennen auf einen anderen Rechner uebertragen und dort offline mit einem Programm gelesen werden, das "rnews"-Batches versteht (z.B. Crosspoint). X "getnews" verwendet folgende Dateien und Verzeichnisse unter dem Home-Verzeichnis: News/in.coming enthaelt die Batches. News/(servername) enthaelt die psl-Arbeitsdateien. Dieses Setup-Programm erzeugt die notwendigen Verzeichnisse und Dateien. X.2 X Weiter - Return druecken X.3 setup legt Verzeichnis %1 an... X.4 mkdir fehlgeschlagen - irgendwas stimmt nicht! X.5 X "getnews" braucht eine Datei %1 im .newsrc-Format. Diese Datei enthaelt die Newsgruppen, die mit getnews uebertragen werden sollen. X Kopiere die .newsrc oder getnewsrc-Datei (falls vorhanden) und editiere sie entsprechend! X.6 X Dieses Setup-Programm erzeugt die Datei %1 mit einer Zeile, die zeigt, wie es aussehen soll. X.7 X Okay. Alles in Ordnung. Jetzt kann "getnews" laufen. X Wissenswertes ueber Dateien: - Die Batchdateien haben Namen, die auf '.b' enden. Jede solche X Datei in News/in.coming ist ein Batch. Die Dateinamen sind eindeutig X und werden nicht wiederholt. X Automatische Dateitransfer-Prozesse aller Art muessen diese Namen X beachten. Regelmaessiges Entfernen der Batchdateien ist notwendig X gegen Quotaueberlauf. :-) - getnews entfernt Batches, die aelter als 10 Tage sind. - In %1 liegt ein Logfile. X getnews prueft, ob dieses zu gross wird. Es kann ohne weiteres X geloescht werden. - psl-subscribe, etc. ist unnoetig. X SHAR_EOF $shar_touch -am 1116185695 'psl/getnews/setup.german.mesg' && chmod 0444 'psl/getnews/setup.german.mesg' || echo 'restore of psl/getnews/setup.german.mesg failed' shar_count="`wc -c < 'psl/getnews/setup.german.mesg'`" test 1666 -eq "$shar_count" || echo "psl/getnews/setup.german.mesg: original size 1666, current size $shar_count" fi # ============= psl/getnews/messages ============== if test -f 'psl/getnews/messages' && test X"$1" != X"-c"; then echo 'x - skipping psl/getnews/messages (file already exists)' else echo 'x - extracting psl/getnews/messages (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/getnews/messages' && # (This really has to be written in perl, but...) # # A small hack to show the power of shell scripts. -ot.95-11 Publ.Dom. X # Given the parameters $msgdir, $msgname, $msgvers, this loads the # required message base file (for $LANG if available). Then the # shell function MSG will be available: # MSG $1 $2 - display message no. $1 from the message base. # Any "%1" in the message will be replaced by $2. # The format of the message base files is documented in "mbc". # The name is ${msgname}.${LANG}.msg or ${msgname}.msg. X # The displaying function MSG () { X n=$1 X a=`echo $msglines | sed -n -e 's/^.*\.'$n'\.\([0-9,]*\).*$/\1/p'` X sed -n "$a p" $msgfile | sed "s:%1:$2:g" } X # Find the right message base file msgfile=$msgdir/$msgname.$LANG.msg [ -r $msgfile ] || { X msgfile=$msgdir/$msgname.msg X [ -r $msgfile ] || { X echo "No message file for $msgname" X exit 1 X } } # Check if the file has the right format set `grep '^\.:' $msgfile | tr ':' ' '` [ "$2" = "$msgname" -a "$3" = "$msgvers" -a "$4" = "C" ] || { X echo "$msgfile: Format error" X exit 1 } # Load the index line msglines=`grep '^\.[0-9]' $msgfile` X SHAR_EOF $shar_touch -am 1116062895 'psl/getnews/messages' && chmod 0444 'psl/getnews/messages' || echo 'restore of psl/getnews/messages failed' shar_count="`wc -c < 'psl/getnews/messages'`" test 1116 -eq "$shar_count" || echo "psl/getnews/messages: original size 1116, current size $shar_count" fi # ============= psl/getnews/mbc ============== if test -f 'psl/getnews/mbc' && test X"$1" != X"-c"; then echo 'x - skipping psl/getnews/mbc (file already exists)' else echo 'x - extracting psl/getnews/mbc (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/getnews/mbc' && #!/usr/bin/perl # Message base compiler. -*-perl-*- X # The input has the following format: # .:name:version: # .1 # (lines of text for message 1) # .2 # (lines of text for message 2) # (etc.) X # A compiled message base file has the format: # .:name:version:C # (lines of text) # .n.range .n.range (...) # where each range gives the lines which message n occupies in this file. X # "Name" and "version" are used to identify the file. X $_=<>; chop; /^\.:([^:]*):([^:]*):([^:]*)$/ || die "Unrecognized format"; ($nam,$ver,$fl) = ($1,$2,$3); $fl eq "" || die "Compiled or invalid format"; printf STDERR "Name: %s Version: %s\n", $nam, $ver; printf ".:%s:%s:C\n", $nam, $ver; $l=2; X while(<>) { X /^\.(.*)/ && do { X $no=$1; X $no>0 || die "Invalid message no: $no"; X $ll{$on}=$l-1; X $fl{$no}=$l; X $on=$no; X next; X }; X print; X ++$l; } $ll{$on}=$l-1; X foreach $i (sort keys %fl) { X $i>0 || next; X printf ".%d.%d,%d ", $i, $fl{$i}, $ll{$i}; } print "\n"; Xexit 0; SHAR_EOF $shar_touch -am 1116061795 'psl/getnews/mbc' && chmod 0555 'psl/getnews/mbc' || echo 'restore of psl/getnews/mbc failed' shar_count="`wc -c < 'psl/getnews/mbc'`" test 1014 -eq "$shar_count" || echo "psl/getnews/mbc: original size 1014, current size $shar_count" fi # ============= psl/getnews/Makefile ============== if test -f 'psl/getnews/Makefile' && test X"$1" != X"-c"; then echo 'x - skipping psl/getnews/Makefile (file already exists)' else echo 'x - extracting psl/getnews/Makefile (text)' sed 's/^X//' << 'SHAR_EOF' > 'psl/getnews/Makefile' && PROGSN=getnews getnews-setup messages setup.msg setup.german.msg X X.SUFFIXES: .mesg .msg X.mesg.msg: X $(PERL) ./mbc $< > $@ X all: $(PROGSN) X install: all X for i in $(PROGSN); do cp $$i $(BINDIR); chmod +rx $(BINDIR)/$$i; done X clean: X rm -f *~ *.msg SHAR_EOF $shar_touch -am 0402180697 'psl/getnews/Makefile' && chmod 0444 'psl/getnews/Makefile' || echo 'restore of psl/getnews/Makefile failed' shar_count="`wc -c < 'psl/getnews/Makefile'`" test 248 -eq "$shar_count" || echo "psl/getnews/Makefile: original size 248, current size $shar_count" fi exit 0