rsalz@uunet.uu.net (Rich Salz) (06/30/89)
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@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@ssbell.uucp X ** kent@ssbell.uucp (Kent Landfield) X ** Kent Landfield <kent@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@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.