v19i100: Usenet sources archiver, Part03/04
Rich Salz
rsalz at uunet.uu.net
Sat Jul 1 00:46:57 AEST 1989
Submitted-by: Kent Landfield <ssbell!kent>
Posting-number: Volume 19, Issue 100
Archive-name: rkive/part03
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 3 (of 4)."
# Contents: header.c rkive.cf setup.c
# Wrapped by kent at ssbell on Thu Jun 1 16:19:13 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'header.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'header.c'\"
else
echo shar: Extracting \"'header.c'\" \(15302 characters\)
sed "s/^X//" >'header.c' <<'END_OF_FILE'
X/*
X**
X** This software is Copyright (c) 1989 by Kent Landfield.
X**
X** Permission is hereby granted to copy, distribute or otherwise
X** use any part of this package as long as you do not try to make
X** money from it or pretend that you wrote it. This copyright
X** notice must be maintained in any copy made.
X**
X** History:
X** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X**
X*/
X#ifndef lint
Xstatic char SID[] = "@(#)header.c 1.1 6/1/89";
X#endif
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include "article.h"
X
Xint its(s1)
Xregister char *s1;
X{
X if (strncmp(s,s1,strlen(s1)) == 0)
X return(TRUE);
X return(FALSE);
X}
X
Xint line_type()
X{
X if (its("Path: "))
X return PATH;
X if (its("From: "))
X return FROM;
X if (its("Newsgroups: "))
X return NEWSGROUP;
X if (its("Subject: "))
X return SUBJECT;
X if (its("Keywords: "))
X return KEYWORDS;
X if (its("Date: "))
X return DATE;
X if (its("Message-ID: "))
X return MSG_ID;
X if (its("Lines: "))
X return NUMLINES;
X if (its("Approved: "))
X return APPROVED;
X
X /* The following is the auxilliary headers used by */
X /* the moderators of the sources groups. In some */
X /* cases, line checks are done with "historical" */
X /* auxilliary headers. Most of the moderators have */
X /* now standardized on comp.sources.unix's format.*/
X
X /* Archive header lines for comp.sources.unix */
X /* for comp.sources.amiga, comp.sources.atari.st, */
X /* comp.sources.misc, and comp.sources.x. */
X
X if (its("Submitted-by: "))
X return SUBMITTED_BY;
X if (its("Posting-number: "))
X return POSTING_NUMBER;
X if (its("Archive-name: "))
X return ARCH_NAME;
X
X /* Archive header lines for historical purposes */
X /* once used in comp.sources.misc */
X
X if (its("Submitted-By: "))
X return SUBMITTED_BY;
X if (its("comp.sources.misc: "))
X return POSTING_NUMBER;
X if (its("Archive-Name: "))
X return ARCH_NAME;
X
X /* Archive header lines used in comp.sources.games */
X /* Archive-name is the same as comp.sources.unix */
X
X if (its("Submitted by: "))
X return SUBMITTED_BY;
X if (its("Comp.sources.games: "))
X return POSTING_NUMBER;
X
X /* Auxiliary header used as a backward reference */
X /* to the location of the initially posted sources */
X /* in the event of a patch. This line only exists */
X /* if the current article is a patch. */
X
X if (its("Patch-To: "))
X return PATCH_TO;
X
X /* Archive header lines for historical purposes */
X /* once used in mod.sources articles. */
X
X if (its("Mod.sources: "))
X return POSTING_NUMBER;
X
X /* The remainder are other types of lines included */
X /* headers and are includes for formatting output */
X /* and for potential future use. */
X
X if (its("References: "))
X return REFERENCES;
X if (its("Organization: "))
X return ORGANIZATION;
X if (its("Distribution: "))
X return DISTRIBUTION;
X if (its("Xref: "))
X return XREF;
X if (its("Expires: "))
X return EXPIRE;
X if (its("Article-I.D.: "))
X return ARTICLEID;
X if (its("Reply-To: "))
X return REPLY_TO;
X if (its("Control: "))
X return CONTROL;
X if (its("Sender: "))
X return SENDER;
X if (its("Followup-To: "))
X return FOLLOWUP_TO;
X if (its("Summary: "))
X return SUMMARY;
X if (its("Supersedes: "))
X return SUPERSEDES;
X
X return OTHER;
X}
X
Xdata(hpfield, size, fldname)
Xchar *hpfield;
Xint size;
Xchar *fldname;
X{
X register char *ptr;
X register char *p;
X char *strncpy();
X char *strchr();
X
X for (ptr = strchr(s, ':'); isspace(*++ptr); )
X ;
X if (*ptr != '\0') {
X (void) strncpy(hpfield, ptr, size - 1);
X /*
X * Strip trailing newlines, blanks, and tabs from hpfield.
X */
X for (p = hpfield; *p; ++p)
X ;
X while (--p >= hpfield && (*p == '\n' || *p == ' ' || *p == '\t'))
X ;
X *++p = '\0';
X }
X if (debug)
X (void) fprintf(logfp,"%s: %s\n",fldname, hpfield);
X}
X
Xdump_article()
X{
X char *type_str;
X
X switch(article.rectype) {
X case PATCH : type_str = "PATCH";
X break;
X case INFORMATIONAL : type_str = "INFORMATIONAL";
X break;
X default : type_str = "NORMAL";
X break;
X }
X
X (void) fprintf(logfp,"Article: [%s]\n",article.newsarticle);
X (void) fprintf(logfp," newsgroup: [%s]\n",article.newsgroup);
X (void) fprintf(logfp," filename: [%s]\n",article.filename);
X (void) fprintf(logfp," volume: [%d]\n",article.volume);
X (void) fprintf(logfp," issue: [%d]\n",article.issue);
X (void) fprintf(logfp," record type: [%s]\n", type_str);
X if (article.rectype == PATCH) {
X (void) fprintf(logfp," patch volume: [%d]\n",article.patch_volume);
X (void) fprintf(logfp," patch issue: [%d]\n",article.patch_issue);
X }
X (void) fprintf(logfp," reposted: [%s]\n",
X article.repost ? "YES": "NO");
X (void) fprintf(logfp," description: [%s]\n",article.description);
X (void) fprintf(logfp," author's name: [%s]\n",article.author_name);
X (void) fprintf(logfp," author's logon: [%s]\n\n",article.author_signon);
X}
X
Xinit_article()
X{
X article.newsgroup[0] = '\0';
X article.newsarticle[0] = '\0';
X article.filename[0] = '\0';
X article.volume = -1;
X article.issue = -1;
X article.rectype = NORMAL;
X article.repost = FALSE;
X article.patch_volume = -1;
X article.patch_issue = -1;
X article.description[0] = '\0';
X article.author_name[0] = '\0';
X article.author_signon[0] = '\0';
X
X header.from[0] = '\0'; /* From: */
X header.path[0] = '\0'; /* Path: */
X header.nbuf[0] = '\0'; /* Newsgroups: */
X header.subject[0] = '\0'; /* Subject: */
X header.ident[0] = '\0'; /* Message-ID: */
X header.replyto[0] = '\0'; /* Reply-To: */
X header.references[0] = '\0'; /* References: */
X header.subdate[0] = '\0'; /* Date: (submission) */
X header.subtime = 0; /* subdate in secs */
X header.expdate[0] = '\0'; /* Expires: */
X header.ctlmsg[0] = '\0'; /* Control: */
X header.sender[0] = '\0'; /* Sender: */
X header.followup_to[0] = '\0'; /* Followup-to: */
X header.distribution[0] = '\0'; /* Distribution: */
X header.organization[0] = '\0'; /* Organization: */
X header.numlines[0] = '\0'; /* Lines: */
X header.intnumlines = 0; /* Integer Version */
X header.keywords[0] = '\0'; /* Keywords: */
X header.summary[0] = '\0'; /* Summary: */
X header.approved[0] = '\0'; /* Approved: */
X header.xref[0] = '\0'; /* Xref: */
X header.supersedes[0] = '\0'; /* Supersedes: */
X header.submitted_by[0] = '\0'; /* Submitted_by: */
X header.posting_num[0] = '\0'; /* Posting-number: */
X header.archive_name[0] = '\0'; /* Archive-name: */
X header.patch_to[0] = '\0'; /* Patch-To: */
X}
X
Xstore_line()
X{
X char *strchr(), *strcpy(), *strstrip(), *substr();
X char *dp, *sp;
X char wrk[20];
X
X switch(line_type()) {
X
X case PATH: /*PATH REQUIRED************************/
X data(header.path, sizeof(header.path), "PATH:");
X break;
X
X case FROM: /*FROM REQUIRED************************/
X data(header.from, sizeof(header.from), "FROM:");
X break;
X
X case NEWSGROUP: /*NEWSGROUP REQUIRED**************/
X data(header.nbuf, sizeof(header.nbuf), "NEWSGROUPS:");
X if ((sp = strchr(s,':')) != NULL) {
X do {
X ++sp;
X } while(!isalpha(*sp));
X /* remove all crossposting labels */
X if ((dp = strchr(sp,',')) != NULL)
X *dp = '\0';
X (void) strcpy(article.newsgroup,sp);
X }
X break;
X
X case SUBJECT: /*SUBJECT REQUIRED******************/
X data(header.subject, sizeof(header.subject), "SUBJECT:");
X /*
X ** Save the subject as the description for articles
X ** that have no volume/issue format. Later in the
X ** code, this subject line minus the volume/issue
X ** is stored back in the .description element if
X ** the volume/issue indicator is found.
X */
X (void) strcpy(article.description, header.subject);
X
X /*
X ** Check to see if this article is a repost of
X ** a previously posted article.
X */
X if (substr(s, "REPOST") != NULL)
X article.repost = TRUE;
X
X /*
X ** Time to get the filename. Assure that it is in a
X ** volume/issue (v01INF1 or v01i001) format.
X */
X if ((sp = strchr(s,'v')) == NULL)
X return; /* no volume indicator */
X
X /*
X ** Is there a number that follows
X ** the volume indicator ?
X */
X if (*(sp+1) < '0' || *(sp+1) > '9')
X return; /* The volume number is missing */
X
X /*
X ** Is there a second ':' as well ?
X */
X (void) strcpy(article.filename,sp);
X if ((dp = strchr(article.filename,':')) == NULL)
X return; /* not in v01i001: format */
X
X /*
X ** terminate the article's filename and
X ** store the article's description
X */
X *dp = '\0';
X (void) strcpy(article.description, strstrip(++dp));
X
X /*
X ** Store the filename in a work
X ** buffer so I can stomp on it.
X */
X (void) strcpy(wrk, article.filename);
X
X /*
X ** This is an informational posting.
X */
X if ((dp = substr(wrk, "INF")) != NULL) {
X article.rectype = INFORMATIONAL;
X article.issue = atoi((dp+3));
X ++sp;
X *dp = '\0';
X article.volume = atoi(sp);
X }
X
X /*
X ** check to see if there is an issue indicator
X */
X else if ((dp = strchr(++sp,'i')) == NULL)
X return; /* proven guilty. not volume/issue format */
X
X /* parse the volume from the filename */
X *dp = '\0';
X article.volume = atoi(sp);
X
X /* parse the issue from the filename */
X sp = ++dp;
X while (isdigit(*dp)) ++dp;
X *dp = '\0';
X article.issue = atoi(sp);
X
X break;
X
X case DATE:
X data(header.subdate, sizeof(header.subdate), "DATE:");
X break;
X
X case EXPIRE:
X data(header.expdate, sizeof(header.expdate), "EXPIRES:");
X break;
X
X case MSG_ID:
X data(header.ident, sizeof(header.ident), "MESSAGE-ID:");
X break;
X
X case REPLY_TO:
X data(header.replyto, sizeof(header.replyto), "REPLY-TO:");
X break;
X
X case REFERENCES:
X data(header.references, sizeof(header.references), "REFERENCES:");
X break;
X
X case SENDER:
X data(header.sender, sizeof(header.sender), "SENDER:");
X break;
X
X case FOLLOWUP_TO:
X data(header.followup_to, sizeof(header.followup_to),"FOLLOWUP-TO:");
X break;
X
X case CONTROL:
X data(header.ctlmsg, sizeof(header.ctlmsg),"CONTROL:");
X break;
X
X case DISTRIBUTION:
X data(header.distribution, sizeof(header.distribution),"DISTRIBUTION:");
X break;
X
X case ORGANIZATION:
X data(header.organization, sizeof(header.organization),"ORGANIZATION:");
X break;
X
X case NUMLINES:
X data(header.numlines, sizeof(header.numlines),"LINES:");
X header.intnumlines = atoi(header.numlines);
X break;
X
X case KEYWORDS:
X data(header.keywords, sizeof(header.keywords), "KEYWORDS:");
X break;
X
X case APPROVED:
X data(header.approved, sizeof(header.approved), "APPROVED:");
X break;
X
X case SUPERSEDES:
X data(header.supersedes, sizeof(header.supersedes),"SUPERSEDES:");
X break;
X
X case XREF:
X data(header.xref, sizeof(header.xref),"XREF:");
X break;
X
X case SUMMARY:
X data(header.summary, sizeof(header.summary),"SUMMARY:");
X break;
X
X case POSTING_NUMBER:
X data(header.posting_num, sizeof(header.posting_num), "POSTING_NUMBER:");
X break;
X
X case SUBMITTED_BY:
X data(header.submitted_by, sizeof(header.submitted_by), "SUBMITTED_BY:");
X /*
X ** Save the author's name and sign on if specified
X ** Can be in any of the following formats:
X ** kent at ssbell.uucp
X ** kent at ssbell.uucp (Kent Landfield)
X ** Kent Landfield <kent at ssbell.uucp>
X */
X if ((sp = strchr(s,':')) != NULL) {
X (void) strcpy(article.author_signon,(sp+2));
X /*
X ** Has a name been attached to the signon ?
X */
X if ((dp = strchr(article.author_signon,'<')) != NULL) {
X *(dp-1) = '\0';
X (void) strcpy(article.author_name, article.author_signon);
X (void) strcpy(article.author_signon, ++dp);
X /*
X ** Save the name, removing the <>.
X */
X if ((dp = strchr(article.author_signon,'>')) != NULL)
X *dp = '\0';
X }
X else if ((dp = strchr(article.author_signon,'(')) != NULL) {
X *(dp-1) = '\0';
X /*
X ** Save the name, removing the ().
X */
X (void) strcpy(article.author_name, ++dp);
X if ((dp = strchr(article.author_name,')')) != NULL)
X *dp = '\0';
X }
X }
X break;
X
X case ARCH_NAME:
X data(header.archive_name,sizeof(header.archive_name),"ARCH_NAME:");
X break;
X
X case PATCH_TO:
X data(header.patch_to,sizeof(header.patch_to),"PATCH_TO:");
X article.rectype = PATCH; /* set the article type */
X
X /*
X ** Parse the initially posted article's volume and issue.
X ** The format of the auxiliary header is
X **
X ** Patch-To: Volume 5, Issue 110
X **
X ** In the case of multipart initial postings, the Patch-To:
X ** line points to the first issue.
X */
X
X /*
X ** First get the volume number.
X */
X
X dp = s;
X while (*dp && (!isdigit(*dp)))
X ++dp;
X sp = dp;
X while (*dp && (isdigit(*dp)))
X ++dp;
X *dp = '\0';
X article.patch_volume = atoi(sp);
X
X /*
X ** Now get the issue number.
X */
X
X ++dp;
X while (*dp && (!isdigit(*dp)))
X ++dp;
X sp = dp;
X while (*dp && (isdigit(*dp)))
X ++dp;
X *dp = '\0';
X article.patch_issue = atoi(sp);
X break;
X }
X return;
X}
END_OF_FILE
if test 15302 -ne `wc -c <'header.c'`; then
echo shar: \"'header.c'\" unpacked with wrong size!
fi
# end of 'header.c'
fi
if test -f 'rkive.cf' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rkive.cf'\"
else
echo shar: Extracting \"'rkive.cf'\" \(9112 characters\)
sed "s/^X//" >'rkive.cf' <<'END_OF_FILE'
X#
X#
X# @(#)rkive.cf 1.1 6/1/89
X#
X# An rkive.cf template.
X# Copy and edit this to reflect the local archive conditions.
X# After editing, run ckconfig to display how rkive will interpret
X# the local specifications.
X#
X# The format of the configuration file is as follows:
X#
X######################################################################
X#
X# Global variables
X# SPOOLDIR - the base directory for the news subsystem
X# PROBLEMS - The name of the base directory used to store any
X# duplicate or "problem" articles. All articles are
X# stored under this directory in a newsgroup/volume
X# directory.
X# TYPE - This is the archive type (or the archive key)
X# There are 3 possible keys:
X# Volume-Issue, Archive-Name or Article-Number
X# These are used to determine if you wish the
X# articles archived in a
X# /basedir/amiga/Volume1/v001i22 or
X# /basedir/amiga/Volume1/sitonit or
X# /basedir/amiga/Volume1/44 format
X# PATCHES - This variable determines the way in which patches
X# are installed into the archive. If the PATCHES
X# entry is not defined either globally or within
X# the newsgroup, the article is handled as a regular
X# article and is stored according to the specified
X# parameters of the newsgroup. The following are
X# the valid possible values for the PATCHES variable:
X# PATCHES=Historical
X# This is the same as if no PATCHES entry
X# existed at all. It is useful if Historical
X# patches archiving is the default but there
X# are certain newsgroups that have it specified
X# differently.
X# PATCHES=Package
X# Package patches archiving allows the inbound
X# patch to be placed with the directory with
X# the initially posted articles. In this manner
X# all parts and fixes of a package are together
X# making it easier for software retrieval by
X# uucp requests and mail request facilities
X# such as netlib.
X# MAIL - If specified, all actions are mailed to the
X# list of users specified. The user names are
X# a comma separated list. It is not necessary to
X# specify any users.
X# OWNER - The unix login name for the owner of the
X# archive files after they are stored in the archive.
X# GROUP - The unix group value for the group ownership of
X# the archive files after they are stored in the
X# archive.
X# MODE - The modes of the files residing in the archive.
X# LOG - The location of the master log in which all
X# actions are logged.
X# LOG_FORMAT - The format of the records of the master log file.
X# See the man page for article for a discussion of
X# the available selection format capabilities.
X# INDEX - The location of the master index (if one is kept).
X# The index can be used to interface with the netlib
X# source retrieval software facility.
X# INDEX_FORMAT - The format of the records of the master log file.
X# See the man page for article for a discussion of
X# the available selection format capabilities.
X# COMPRESS - The location of the compression utility if the
X# files are to be reduced.
X#
X######################################################################
XSPOOLDIR=/usr/spool/news
XPROBLEMS=/usenet/problems
XTYPE= Volume-Issue
XPATCHES=Historical
XMAIL=kent
XOWNER=src
XGROUP=archive
XMODE=0444
XLOG=/usenet/archive.log
XLOG_FORMAT= "%B %T"
XINDEX= /usenet/index
XINDEX_FORMAT= "%B %a %T"
X#COMPRESS=/usr/lbin/compress
X
X######################################################################
X#
X# Each Newsgroup to be archived has an entry with the following
X# possible variables. (Note that all variables are not necessary)
X#
X# BASEDIR - This is the path to the base directory of the archive
X# TYPE - This is the archive type (or the archive key)
X# There are 3 possible keys:
X# Volume-Issue, Archive-Name or Article-Number
X# These are used to determine if you wish the articles
X# archived in a
X# /basedir/amiga/Volume1/v001i22 or
X# /basedir/amiga/Volume1/sitonit or
X# /basedir/amiga/Volume1/44 format
X# PATCHES - This variable determines the way in which patches
X# are installed into the archive. If the PATCHES
X# entry is not defined either globally or within
X# the newsgroup, the article is handled as a regular
X# article and is stored according to the specified
X# parameters of the newsgroup. The following are
X# the valid possible values for the PATCHES variable:
X# PATCHES=Historical
X# This is the same as if no PATCHES entry
X# existed at all. It is useful if Historical
X# patches archiving is the default but there
X# are certain newsgroups that have it specified
X# differently.
X# PATCHES=Package
X# Package patches archiving allows the inbound
X# patch to be placed with the directory with
X# the initially posted articles. In this manner
X# all parts and fixes of a package are together
X# making it easier for software retrieval by
X# ftp, uucp requests and mail request facilities
X# such as netlib.
X# MAIL - If specified, all actions are mailed to the
X# list of users specified. The user names are
X# a comma separated list.
X# OWNER - The unix login name for the owner of the archive files
X# after they are stored in the archive.
X# GROUP - The unix group value for the group ownership of the archive
X# files after they are stored in the archive.
X# MODE - The modes of the files residing in the archive.
X# LOG - The location of the log in which all actions are logged.
X# LOG_FORMAT - The format of the records of the master log file.
X# See the man page for article for a discussion of
X# the available selection format capabilities.
X# INDEX - The location of the master index (if one is kept).
X# The index can be used to interface with the netlib
X# source retrieval software facility.
X# INDEX_FORMAT - The format of the records of the master log file.
X# See the man page for article for a discussion of
X# the available selection format capabilities.
X# COMPRESS - The location of the compression utility if the files
X# are to be reduced.
X#
X######################################################################
X$$comp.sources.amiga
X BASEDIR: /usenet/amiga
X TYPE: Volume-Issue
X MAIL: rick
X LOG: /usenet/amiga/log
X INDEX: /usenet/amiga/index
X INDEX_FORMAT: "%B %a %T"
X
X$$comp.sources.atari.st
X BASEDIR: /usenet/atari/st
X TYPE: Volume-Issue
X LOG: /usenet/atari/st/log
X INDEX: /usenet/atari/st/index
X INDEX_FORMAT: "%B %a %T"
X
X$$comp.sources.games
X BASEDIR: /usenet/games
X MAIL: rick
X TYPE: Volume-Issue
X LOG: /usenet/games/log
X INDEX: /usenet/games/index
X INDEX_FORMAT: "%B %a %T"
X
X$$comp.sources.mac
X BASEDIR: /usenet/mac
X TYPE: Article-Number
X LOG: /usenet/mac/log
X INDEX: /usenet/mac/index
X INDEX_FORMAT: "%O %a %T"
X
X$$comp.sources.misc
X BASEDIR: /usenet/misc
X TYPE: Volume-Issue
X MAIL: rick
X LOG: /usenet/misc/log
X INDEX: /usenet/misc/index
X INDEX_FORMAT: "%B %a %T"
X
X$$comp.sources.unix
X BASEDIR: /usenet/unix
X TYPE: Volume-Issue
X MAIL: rick
X LOG: /usenet/unix/log
X LOG_FORMAT: "%B %a %T"
X INDEX: /usenet/unix/index
X INDEX_FORMAT: "%B %a %T"
X
X$$comp.sources.x
X BASEDIR: /usenet/x
X TYPE: Archive-Name
X PATCHES: Package
X MAIL: rick,kent,mark
X LOG: /usenet/x/log
X LOG_FORMAT: "%B %a %T"
X INDEX: /usenet/unix/index
X INDEX_FORMAT: "%B %a %T"
X
X$$alt.sources
X BASEDIR: /usenet/alt/sources
X TYPE: Article-Number
X LOG: /usenet/alt/sources/log
X LOG_FORMAT: "%O %S"
X
X$$alt.sources.amiga
X BASEDIR: /usenet/alt/sources/amiga
X TYPE: Article-Number
X MAIL: rick
X LOG: /usenet/alt/sources/amiga/log
X LOG_FORMAT: "%O %S"
END_OF_FILE
if test 9112 -ne `wc -c <'rkive.cf'`; then
echo shar: \"'rkive.cf'\" unpacked with wrong size!
fi
# end of 'rkive.cf'
fi
if test -f 'setup.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'setup.c'\"
else
echo shar: Extracting \"'setup.c'\" \(16190 characters\)
sed "s/^X//" >'setup.c' <<'END_OF_FILE'
X/*
X**
X** This software is Copyright (c) 1989 by Kent Landfield.
X**
X** Permission is hereby granted to copy, distribute or otherwise
X** use any part of this package as long as you do not try to make
X** money from it or pretend that you wrote it. This copyright
X** notice must be maintained in any copy made.
X**
X**
X** History:
X** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X**
X*/
X#ifndef lint
Xstatic char SID[] = "@(#)setup.c 1.1 6/1/89";
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <grp.h>
X#include <dirent.h>
X#include "cfg.h"
X
X#define GAG(b) ((void) fprintf(errfp,"%s invalid variable, ignoring.\n",b))
X
Xchar spooldir[MAXNAMLEN] = { SPOOLDIR };
Xchar problems_dir[MAXNAMLEN] = { PROBLEMS_DIR };
X
Xint default_owner = OWNER;
Xint default_group = GROUP;
Xint default_modes = MODES;
Xint default_type = ARTICLE_NUMBER;
Xint default_patch_type = HISTORICAL;
X
X/*
X** compress -
X** Used to determine whether or not articles should be compressed
X** to save space. The command to execute is stored in compress.
X*/
Xchar compress[MAXNAMLEN] = { '\0' };
X
X/*
X** mail -
X** If specified, all actions logged are mailed to the list of users
X** specified. The user names are a comma seperated list.
X*/
Xchar mail[MAXNAMLEN] = { '\0' };
X
X/*
X** log -
X** The location of the master log in which all actions are logged.
X** If not specified, all logged events are printed on stdout.
X*/
Xchar log[MAXNAMLEN] = { '\0' };
X
X/*
X** log_format -
X** The format of each individual log file record. The format is
X** then filled with information contained in the headers.
X*/
Xchar log_format[BUFSIZ] = { '\0' };
X
X/*
X** index -
X** The location of the master index.
X*/
Xchar index[MAXNAMLEN] = { '\0' };
X
X/*
X** index_format -
X** The format of each individual master index record. The format
X** is then filled with information contained in the headers.
X*/
Xchar index_format[BUFSIZ] = { '\0' };
X
Xchar *config_file;
XFILE *config;
X
Xstruct stat stbuf;
Xstruct passwd *pwent;
X
Xchar *strstrip();
Xchar *strchr();
Xchar *strcpy();
Xvoid exit();
Xstruct passwd *getpwnam();
X
Xstruct restricted_dirs {
X char *dirstr; /* path of restricted directory */
X};
X
Xstatic struct restricted_dirs base_dirs[] = {
X{ "/" },
X{ "/etc" },
X{ "/dev" },
X{ "/dev/dsk" },
X{ "/dev/rdsk" },
X{ "/lib" },
X{ "/stand" },
X{ "/usr/adm" },
X{ "/usr/spool/uucp" },
X{ NULL },
X};
X
Xsetup_defaults()
X{
X char *sp;
X char *buf;
X char buffer[BUFSIZ];
X char mode_str[128];
X
X char *sav_format();
X char *get_compress();
X char *get_users();
X FILE *efopen();
X
X config = efopen(config_file,"r");
X
X num = -1; /* initialize group structure index */
X
X while (fgets(buffer, sizeof buffer, config) != NULL) {
X /* ignore comments and blank lines */
X if (*buffer == '#' || *buffer == '\n')
X continue;
X
X buf = buffer;
X
X /* strip leading spaces and tabs */
X while(*buf == ' ' || *buf == '\t')
X ++buf;
X
X *(buf+(strlen(buf)-1)) = '\0'; /* remove newline */
X
X /* if embedded comments, truncate at the comment */
X if ((sp = strchr(buf,'#')) != NULL)
X *sp = '\0';
X
X /* check to see if newsgroup entry */
X
X if (*buf == '$' && *(buf+1) == '$') {
X if (++num >= NUM_NEWSGROUPS)
X error("Maximum number of newsgroups exceeded!!\n",
X "Please increase the NUM_NEWSGROUPS define...");
X
X sp = buf+2;
X while (*sp && !isspace(*sp))
X ++sp;
X *sp = '\0';
X
X group[num].owner = default_owner;
X group[num].group = default_group;
X group[num].modes = default_modes;
X group[num].type = default_type;
X group[num].patch_type = default_patch_type;
X (void) strcpy (group[num].ng_name, strstrip(buf+2));
X group[num].location[0] = '\0';
X group[num].mail_list[0] = '\0';
X group[num].logfile[0] = '\0';
X group[num].index[0] = '\0';
X group[num].logformat[0] = '\0';
X group[num].indformat[0] = '\0';
X group[num].compress[0] = '\0';
X }
X
X else if ((sp = strchr(buf,'=')) != NULL) {
X sp++;
X /* Global assignment */
X while (*sp == ' ' || *sp == '\t')
X sp++;
X
X if (!*sp) /* is something still there ? */
X continue;
X
X switch(*buf) {
X case 'C': (void) strcpy(compress, get_compress(buf, sp));
X break;
X case 'G': default_group = get_group(buf, sp);
X break;
X case 'I': if (strncmp(buf, "INDEX_FORMAT", 12) == 0)
X (void) strcpy(index_format, sav_format(sp));
X else if (strncmp(buf, "INDEX", 3) == 0)
X (void) strcpy(index, strstrip(sp));
X else
X GAG(buf);
X break;
X case 'L': if (strncmp(buf, "LOG_FORMAT", 10) == 0)
X (void) strcpy(log_format, sav_format(sp));
X else if (strncmp(buf, "LOG", 3) == 0)
X (void) strcpy(log, strstrip(sp));
X else
X GAG(buf);
X break;
X case 'M': if (strncmp(buf, "MAIL",4) == 0)
X (void) strcpy(mail,get_users(sp));
X else if (strncmp(buf, "MODE",4) == 0)
X default_modes = correct_modes(sp, mode_str);
X else
X error(buf, "invalid global assignment");
X break;
X case 'O': default_owner = get_owner(buf, sp);
X break;
X case 'P': if (strncmp(buf, "PROBLEMS", 8) == 0)
X (void) strcpy(problems_dir, strstrip(sp));
X else
X default_patch_type = get_patch_type("Global",buf,sp);
X break;
X case 'S': get_spooldir(buf, sp);
X break;
X case 'T': default_type = get_archive_type("Global", buf, sp);
X break;
X default : error("invalid global assignment:", buf);
X }
X }
X else if ((sp = strchr(buf,':')) != NULL) {
X sp++;
X /* group variable assignment */
X while (*sp == ' ' || *sp == '\t')
X sp++;
X
X if (!*sp) /* is something still there ? */
X continue;
X
X switch(*buf) {
X case 'B': if (strncmp(buf, "BASEDIR",7) == 0)
X get_archive_basedir(sp);
X break;
X case 'C': (void)strcpy(group[num].compress,get_compress(buf,sp));
X break;
X case 'G': group[num].group = get_group(buf, sp);
X break;
X case 'I': if (strncmp(buf, "INDEX_FORMAT", 12) == 0)
X (void) strcpy(group[num].indformat,sav_format(sp));
X else if (strncmp(buf, "INDEX", 3) == 0)
X (void) strcpy(group[num].index, strstrip(sp));
X else
X GAG(buf);
X break;
X case 'L': if (strncmp(buf, "LOG_FORMAT", 10) == 0)
X (void) strcpy(group[num].logformat, sav_format(sp));
X else if (strncmp(buf, "LOG", 3) == 0)
X (void) strcpy(group[num].logfile, strstrip(sp));
X else
X GAG(buf);
X break;
X case 'M': if (strncmp(buf, "MAIL",4) == 0)
X (void) strcpy(group[num].mail_list, get_users(sp));
X else if (strncmp(buf, "MODE",4) == 0)
X group[num].modes = correct_modes(sp, mode_str);
X else
X GAG(buf);
X break;
X case 'O': group[num].owner = get_owner(buf, sp);
X break;
X case 'P': group[num].patch_type = get_patch_type(group[num].ng_name,buf,sp);
X break;
X case 'T': group[num].type = get_archive_type(group[num].ng_name, buf, sp);
X break;
X default : error("invalid group assignment:", buf);
X }
X }
X else /* no idea what it is */
X error("unknown line type", buf);
X }
X (void) fclose(config);
X}
X
Xerror(msg1,msg2)
X char *msg1;
X char *msg2;
X{
X (void) fprintf(errfp,"%s: %s %s\n",progname,msg1, msg2);
X exit(1);
X}
X
X/*
X** valid_base_directory
X**
X** Assure the directory specified in the configuration file
X** as the base directory for a newsgroup archive is not found
X** in the table of restricted base directories.
X**
X** This kind of checking is almost insulting to me as an
X** administrator but, enough people asked me to put it in
X** so "this duds for you"..
X*/
X
Xint valid_base_directory(argstr)
X char *argstr;
X {
X register char *rp;
X register char *dp;
X char wpath[MAXNAMLEN];
X char lastchar;
X struct restricted_dirs *pt;
X
X /*
X ** First check to see if the base directory is any
X ** character other than a slash. We need to assure
X ** that "../../../etc" or ./etc is not allowed. We
X ** need a valid absolute path with which to do relative
X ** path addressing. (Have I confused myself yet ?)
X */
X
X if (*argstr != '/')
X return(FALSE);
X
X /*
X ** Strip the string of duplicate '/'s.
X ** Also check to assure that the path specified
X ** does not contain the '..' sequence.
X */
X
X dp = argstr;
X rp = wpath;
X lastchar = ' ';
X
X while (*dp) {
X if (*dp != '/' || lastchar != '/') {
X lastchar = *dp;
X *rp++ = *dp;
X }
X if (*dp == '.' && lastchar == '.') {
X if ((*(dp+1) == '/') || (*(dp+1) == '\0'))
X return(FALSE);
X }
X ++dp;
X }
X *rp = '\0';
X
X /*
X ** strip the string of trailing '/'s so
X ** I can use the simple checking below.
X */
X
X dp = wpath+(strlen(wpath)-1);
X while(*dp == '/' && dp > wpath)
X *dp = '\0';
X
X /*
X ** check if they match
X */
X
X pt = &base_dirs[0];
X while ((pt->dirstr) != NULL) {
X
X if (strcmp(wpath, pt->dirstr) == 0)
X return(FALSE);
X
X pt++;
X }
X return(TRUE);
X}
X
Xget_archive_basedir(s)
Xchar *s;
X{
X (void) strcpy(group[num].location, strstrip(s));
X
X if (!valid_base_directory(group[num].location))
X error(group[num].ng_name," - Invalid archive base directory!");
X}
X
Xint correct_modes(s,mode_string)
Xchar *s;
Xchar *mode_string;
X{
X register int c;
X register int i;
X
X i = 0;
X (void) sscanf(s, "%s", mode_string);
X while ((c = *mode_string++) >= '0' && c <= '7')
X i = (i << 3) + (c - '0');
X mode_string--;
X return(i);
X}
X
Xchar *get_compress(buffer,cmd)
Xchar *buffer;
Xchar *cmd;
X{
X static char *rp;
X
X if (!valid_variable(buffer, "COMPRESS", 8))
X return(NULL);
X
X rp = strstrip(cmd);
X
X /* need to assure the user has specified */
X /* a valid executable path. */
X
X if (stat(rp, &stbuf) != 0)
X error("Can't find specified COMPRESS -", rp);
X
X return(rp);
X}
X
X
Xint get_group(buffer, valstr)
Xchar *buffer;
Xchar *valstr;
X{
X char *wp;
X struct group *grent;
X struct group *getgrnam();
X
X if (!valid_variable(buffer, "GROUP", 5))
X return(default_group);
X
X /* group specified by names but */
X /* needs to be numbers */
X
X wp = strstrip(valstr);
X
X if ((grent = getgrnam(wp)) == NULL)
X error("Invalid system group:",wp);
X return(grent->gr_gid);
X}
X
X
Xint get_owner(buffer, valstr)
Xchar *buffer;
Xchar *valstr;
X{
X char *wp;
X
X if (!valid_variable(buffer, "OWNER", 5))
X return(default_owner);
X
X /* owner specified by names but */
X /* needs to be numbers */
X
X wp = strstrip(valstr);
X
X if ((pwent = getpwnam(wp)) == NULL)
X error("Invalid user:",wp);
X return(pwent->pw_uid);
X}
X
Xint get_archive_type(ngname, buffer, s)
Xchar *ngname;
Xchar *buffer;
Xchar *s;
X{
X int return_type;
X
X if (!valid_variable(buffer, "TYPE", 4))
X return(default_type);
X
X if (strcmp(s, "Archive-Name") == 0)
X return_type = ARCHIVE_NAME;
X else if (strcmp(s, "Volume-Issue") == 0)
X return_type = VOLUME_ISSUE;
X else if (strcmp(s, "Article-Number") == 0)
X return_type = ARTICLE_NUMBER;
X else {
X (void) fprintf(errfp,"%s: %s: %s %s\n", progname,
X ngname, "Invalid Archive Type:", s);
X (void) fprintf(errfp,"\tTYPE Must be %s, %s or %s\n",
X "Archive-Name", "Volume-Issue", "Article-Number");
X exit(1);
X }
X
X return(return_type);
X}
X
Xint valid_variable(buffer, variable, length)
Xchar *buffer;
Xchar *variable;
Xint length;
X{
X if (strncmp(buffer, variable, length) != 0) {
X GAG(buffer);
X return(FALSE);
X }
X return(TRUE);
X}
X
X
Xget_spooldir(buffer,s)
Xchar *buffer;
Xchar *s;
X{
X static char *rp;
X
X if (!valid_variable(buffer, "SPOOLDIR", 8))
X (void) strcpy(spooldir, SPOOLDIR);
X
X else {
X rp = strstrip(s);
X
X /* need to assure the user has specified */
X /* a valid directory path for the base */
X /* directory for the news subsystem.. */
X
X if (stat(rp, &stbuf) != 0)
X error("Can't find SPOOLDIR -", rp);
X
X (void) strcpy(spooldir, rp);
X }
X}
X
Xchar *get_users(s)
Xchar *s;
X{
X char *strcat();
X
X static char users[512];
X char tmp_users[512];
X char *list, *name;
X char *cp, *dp;
X register int i;
X
X /* prepare the string for saving by stripping any spaces */
X
X for (i = 0; i < sizeof users; i++)
X users[i] = '\0';
X
X cp = s;
X dp = users;
X while (*cp) {
X if (*cp != ' ' && *cp != '\t')
X *dp++ = *cp;
X ++cp;
X }
X
X /* need to check the specified user list */
X /* to assure that all users are valid */
X
X (void) strcpy(tmp_users, users);
X *users = NULL;
X
X name = tmp_users;
X
X while (name != NULL) {
X /* is there additional users specified ? */
X if ((list = strchr(name,',')) != NULL) {
X list++;
X *(list-1) = '\0';
X }
X
X /* check if user is found in passwd file */
X if ((pwent = getpwnam(name)) != NULL) {
X if (*users != NULL) {
X (void) strcat(users, ",");
X (void) strcat(users, name);
X }
X else
X (void) strcpy(users, name);
X }
X else
X error("Invalid user:",name);
X name = list;
X }
X return(users);
X}
X
X/*
X** get a specified format from the buffer
X** Must allow for spaces and tabs so they
X** need to be passed intact in the format.
X*/
Xchar *sav_format(s)
X char *s;
X{
X static char *cp;
X char *dp;
X
X if ((cp = strchr(s,'"')) != NULL &&
X (dp = strchr(++cp,'"')) != NULL) {
X *dp = '\0';
X }
X else
X cp = NULL;
X return(cp);
X}
X
Xint get_patch_type(ngname,buffer, s)
Xchar *ngname;
Xchar *buffer;
Xchar *s;
X{
X int return_type;
X
X if (!valid_variable(buffer, "PATCHES", 7))
X return(default_type);
X
X if (strcmp(s, "Package") == 0)
X return_type = PACKAGE;
X else if (strcmp(s, "Historical") == 0)
X return_type = HISTORICAL;
X else {
X (void) fprintf(errfp,"%s: %s: %s %s\n", progname,
X ngname, "Invalid Patches Type:", s);
X (void) fprintf(errfp,"\tTYPE Must be %s, or %s\n",
X "Historical", "Package");
X exit(1);
X }
X return(return_type);
X}
END_OF_FILE
if test 16190 -ne `wc -c <'setup.c'`; then
echo shar: \"'setup.c'\" unpacked with wrong size!
fi
# end of 'setup.c'
fi
echo shar: End of archive 3 \(of 4\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 4 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list