v19i101: Usenet sources archiver, Part04/04
Rich Salz
rsalz at uunet.uu.net
Sat Jul 1 00:47:13 AEST 1989
Submitted-by: Kent Landfield <ssbell!kent>
Posting-number: Volume 19, Issue 101
Archive-name: rkive/part04
#! /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 4 (of 4)."
# Contents: news_arc.c rkive.c
# Wrapped by kent at ssbell on Thu Jun 1 16:19:18 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'news_arc.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'news_arc.c'\"
else
echo shar: Extracting \"'news_arc.c'\" \(20172 characters\)
sed "s/^X//" >'news_arc.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[] = "@(#)news_arc.c 1.1 6/1/89";
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>
X#include <stdio.h>
X#include <ctype.h>
X#include "article.h"
X#include "cfg.h"
X
X/*
X** Defines for the type of "problems"
X** encountered in saving the articles.
X*/
X#define DUP_PROB 0
X#define NAME_PROB 1
X#define VOL_PROB 2
X#define TYPE_PROB 3
X
Xint test = 0;
Xint problem_article;
X
Xextern struct group_archive *newsgrp;
Xextern int overwrite;
X
Xchar *strchr();
Xchar *strcpy();
Xchar *strcat();
Xchar *do_problem();
Xchar *basename();
Xchar *suffix();
XFILE *efopen();
Xvoid exit();
X
Xget_header(filename)
X char *filename;
X{
X char *dp;
X int header_ok = 0;
X FILE *gfp;
X
X init_article();
X
X gfp = efopen(filename,"r");
X
X (void) strcpy(article.newsarticle, filename);
X
X while (fgets(s,sizeof s,gfp) != NULL) {
X if (debug)
X (void) fprintf(logfp, "BUF = [%s]",s);
X
X if (!isalpha(*s) || (strchr(s,':') == NULL)) {
X header_ok++;
X if (header_ok == 2)
X break;
X continue;
X }
X
X dp = s;
X while (*++dp)
X if (*dp == '\n')
X *dp = '\0';
X
X store_line();
X }
X (void) fclose(gfp);
X
X if (debug)
X dump_article();
X}
X
X/*
X** check_archive_name
X**
X** Assure the path specified is within the base directory
X** specified by the archive administrator by assuring that
X** a prankster could not have an article archived at a
X** basedir/../../../etc/passwd
X** location.
X**
X** If an absoulte path is specified in the Archive-name, it
X** is of no concern since a "checked" base directory and
X** volume directory are prefixed.
X*/
X
Xcheck_archive_name(argstr)
X char *argstr;
X {
X char *substr();
X register char *rp;
X register char *dp;
X
X /*
X ** check to assure that the path specified
X ** does not contain the '..' sequence.
X */
X
X while ((rp = substr(argstr, "..")) != NULL) {
X dp = rp+2;
X while(*dp)
X *rp++ = *dp++;
X *rp = '\0';
X }
X
X /* I know this is not necessary but what the heck.. */
X
X while ((rp = substr(argstr, "//")) != NULL) {
X dp = rp+2;
X ++rp;
X while(*dp)
X *rp++ = *dp++;
X *rp = '\0';
X }
X
X /*
X ** strip the string of trailing '/'s
X */
X
X dp = argstr+(strlen(argstr)-1);
X while(*dp == '/' && dp > argstr)
X *dp = '\0';
X}
X
X/*
X** IF YOU USE A COMPRESSION ROUTINE OTHER THAN COMPRESS
X** OR PACK, ADD YOUR COMPRESSION SPECIFIC INFORMATION
X** TO THE cprgs COMPRESS_TABLE ......
X*/
X
Xstruct compress_tab {
X char *com_name;
X char *com_suffix;
X};
X
Xstruct compress_tab cprgs[] = {
X{ "compress", ".Z" },
X{ "pack", ".z" },
X{ NULL, 0 },
X};
X
Xchar *suffix(compression)
X char *compression;
X {
X struct compress_tab *ct;
X
X ct = &cprgs[0];
X while ((ct->com_name) != NULL) {
X if (strcmp(compression, ct->com_name) == 0)
X return(ct->com_suffix);
X ct++;
X }
X return("");
X}
X
Xint remove_suffix(path_str)
Xchar *path_str;
X {
X char *ss;
X struct compress_tab *ct;
X
X /*
X ** need to compare the filename passed in to
X ** the compression suffix table in order to
X ** determine if the file has a recognized,
X ** compression suffix attached.
X */
X
X ss = path_str + (strlen(path_str) -2);
X
X ct = &cprgs[0];
X while ((ct->com_name) != NULL) {
X if (strcmp(ss, ct->com_suffix) == 0) {
X *ss = '\0';
X return(TRUE);
X }
X ct++;
X }
X return(FALSE);
X}
X
Xchar *expand_name(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X char *comp_cmd;
X static char compress_path[MAXNAMLEN];
X
X (void) strcpy(compress_path, filename);
X
X /*
X ** Check to see if a group specific compress was specified.
X ** If so, then attach the suffix and return.
X ** Else check to see if a global compress was specified. If so,
X ** then attach the suffix and return.
X ** If both are NULL, return filename.
X */
X
X if (*(ng->compress)) {
X comp_cmd = basename(ng->compress);
X (void) strcat(compress_path, suffix(comp_cmd));
X }
X else if (*compress) {
X comp_cmd = basename(compress);
X (void) strcat(compress_path, suffix(comp_cmd));
X }
X return(compress_path);
X}
X
X#ifdef REDUCE_HEADERS
X
Xstruct hdrstokeep {
X char *ststr;
X int stbytes;
X};
X
Xstruct hdrstokeep hdrs[] = {
X{ "From:", (sizeof "From:") },
X{ "Newsgroups:", (sizeof "Newsgroups:") },
X{ "Subject:", (sizeof "Subject:") },
X{ "Message-ID:", (sizeof "Message-ID:") },
X{ "Date:", (sizeof "Date:") },
X{ NULL, 0 },
X};
X
Xint keep_line(argstr)
X char *argstr;
X {
X struct hdrstokeep *pt;
X
X pt = &hdrs[0];
X while ((pt->ststr) != NULL) {
X if (strncmp(argstr, pt->ststr, (pt->stbytes-1)) == 0)
X return(TRUE);
X pt++;
X }
X return(FALSE);
X}
X
Xint copy(source, target)
X char *source, *target;
X{
X char *strchr();
X FILE *from, *to;
X char fbuf[BUFSIZ];
X int inheader;
X
X inheader = TRUE;
X
X if (verbose) {
X (void) fprintf(logfp,"archive <%s> to <%s>\n",source,target);
X if (test)
X return(0);
X }
X if ((from = fopen(source, "r")) == NULL) {
X (void) fprintf(errfp,"%s: cannot open %s\n",progname,source);
X return (-1);
X }
X if ((to = fopen(target, "w")) == NULL) {
X (void) fclose(from);
X (void) fprintf(errfp,"%s: cannot create %s\n",progname,target);
X return (-1);
X }
X /*
X ** Read the source and do not print any headers
X ** unless specified in the "keep" headers table.
X */
X
X while (fgets(fbuf, BUFSIZ, from) != NULL) {
X if (inheader) {
X /*
X ** Have I encountered a line without a line type ?
X */
X if (!isalpha(*fbuf) || (strchr(fbuf,':') == NULL))
X inheader = FALSE;
X
X else {
X /*
X ** Determine the type of the header line and
X ** decide if this is a line to be kept or pitched.
X */
X if (!keep_line(fbuf))
X continue;
X }
X }
X if (fputs(fbuf, to) == EOF) {
X (void) unlink(target);
X (void) fclose(from);
X (void) fclose(to);
X (void) fprintf(errfp,"%s: bad copy to %s\n",progname,target);
X return (-1);
X }
X }
X (void) fclose(from);
X (void) fclose(to);
X return(0);
X}
X
X#else
X
Xcopy(source, target)
X char *source, *target;
X{
X int from, to, ct;
X char fbuf[BUFSIZ];
X
X if (verbose) {
X (void) fprintf(logfp,"archive <%s> to <%s>\n",source,target);
X if (test)
X return(0);
X }
X if ((from = open(source, 0)) < 0) {
X (void) fprintf(errfp,"%s: cannot open %s\n",progname,source);
X return (-1);
X }
X if ((to = creat (target, 0644)) < 0) {
X (void) close(from);
X (void) fprintf(errfp,"%s: cannot create %s\n",progname,target);
X return (-1);
X }
X while ((ct = read(from, fbuf, BUFSIZ)) != 0) {
X if(ct < 0 || write(to, fbuf, (unsigned) ct) != ct) {
X (void) unlink(target);
X (void) close(from);
X (void) close(to);
X (void) fprintf(errfp,"%s: bad copy to %s\n",progname,target);
X return (-1);
X }
X }
X (void) close(from);
X (void) close(to);
X return(0);
X}
X
X#endif /* REDUCE_HEADERS */
X
X/*
X** mkparents:
X**
X** If any parent directories in
X** fullname don't exist, create them.
X*/
X
Xint mkparents(fullname)
Xchar *fullname;
X{
X char *strrchr();
X
X register char *p;
X char b[MAXNAMLEN];
X int rc;
X
X (void) strcpy(b, fullname);
X
X if ((p = strrchr(b, '/')) != NULL)
X *p = '\0';
X else /* no directories in fullname */
X return(0);
X
X if (*b == '\0') /* are we at the root ? */
X return(0);
X
X if (access(b, 0) == 0)
X return(0);
X
X (void) mkparents(b);
X
X if ((rc = makedir(b, 0755, newsgrp->owner, newsgrp->group)) != 0)
X error("makedir failed attempting to make", b);
X
X return(rc);
X}
X
X
Xchar *save_article (filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X char *final_path;
X static char path[MAXNAMLEN];
X struct stat sb;
X
X problem_article = FALSE;
X path[0] = '\0';
X
X /*
X ** Read the news article file to extract the
X ** header information and fill appropriate
X ** data structures.
X */
X get_header(filename);
X
X /*
X ** Build the path string for the final resting spot
X ** for the new archive member.
X */
X switch(ng->type) {
X case ARCHIVE_NAME:
X /*
X ** The header's archive_name contains the filename in
X ** an "elm/part06" format.
X */
X
X if ((article.volume == -1) || (!header.archive_name[0]))
X return(do_problem(NAME_PROB, ng,filename,path));
X
X /*
X ** Assure the address is relative and
X ** that some prankster can not do nasty
X ** things to your system files by having
X ** an Archive-name line like:
X ** ../../../../../etc/passwd
X */
X
X check_archive_name(header.archive_name);
X
X /*
X ** Check to see if the article is a patch. If so,
X ** check to see if the administrator wishes to
X ** store the patch with the initially posted
X ** articles. This really relys on the archive name
X ** being correct.
X */
X
X if (article.rectype == PATCH && ng->patch_type == PACKAGE)
X /*
X ** Store the patch in the volume specified with the
X ** Archive-name: specified file name.
X */
X (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
X article.patch_volume, header.archive_name);
X
X else
X (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
X article.volume, header.archive_name);
X break;
X case VOLUME_ISSUE:
X /*
X ** The article filename contains the filename in
X ** a "v01i001" format.
X */
X if ((article.volume == -1) || (!article.filename[0]))
X return(do_problem(VOL_PROB,ng,filename,path));
X
X (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
X article.volume, article.filename);
X break;
X case ARTICLE_NUMBER:
X /*
X ** Store in same filename - thanks news...
X */
X (void) sprintf(path,"%s/%s", ng->location, filename);
X break;
X default:
X /*
X ** We have got problems....
X */
X return(do_problem(TYPE_PROB,ng,filename,path));
X }
X
X /*
X ** Check if the file is a patch. If so, log
X ** the patch information into the patch log
X ** in a *non-configurable* format so that
X ** applications can be written to access the
X ** file's "known format".
X */
X
X if (article.rectype == PATCH)
X write_patch_log(ng,path);
X
X#ifdef ADD_REPOST_SUFFIX
X if (article.repost == TRUE)
X /*
X ** The ADD_REPOST_SUFFIX code adds the REPOST_SUFFIX
X ** to any file that has been indicated as a repost
X ** by the moderator. This should not be used with
X ** Archive-Name archiving on a filesystem with 14
X ** character filename limits or filename truncation
X ** can occur. You have been warned... :-(
X **
X ** After adding the REPOST_SUFFIX, the filename is
X ** treated as any other file with the duplication
X ** checks and all...
X */
X (void) strcat(path,REPOST_SUFFIX);
X#endif /* ADD_REPOST_SUFFIX */
X
X /*
X ** expand the path to the file to include the
X ** compression suffix if necessary.
X */
X
X final_path = expand_name(path, ng);
X
X /*
X ** Make any necessary directories
X ** along the way.
X */
X (void) mkparents(path);
X
X /*
X ** Check to assure that there is not already
X ** a file with the same file name. If so
X ** copy (or archive) the file to the problems
X ** directory.
X **
X ** This works for REPOSTS as well.
X ** If the REPOST arrives and there is
X ** no file currently at the archive location, the
X ** REPOST is installed in the correct archive
X ** location.
X ** If there is a file that exists when a REPOST
X ** arrives, the REPOST is then handled in do_problem().
X */
X
X if ((stat(final_path ,&sb) == 0) && !overwrite) /* duplicate found */
X return(do_problem(DUP_PROB,ng, filename, final_path));
X
X if (copy(filename,path) != 0) {
X (void) fprintf(errfp,"copy failed for %s to %s\n",filename,path);
X return(NULL);
X }
X /*
X ** Write the filename to the .archived file in the newsgroup's
X ** BASEDIR directory since we do not want it rearchived tomorrow.
X */
X write_archived(filename, path);
X
X /*
X ** Return the path to the archived file.
X */
X return(path);
X}
X
X
X
Xchar *do_problem(type_of_problem, ng, file, path)
Xint type_of_problem;
Xstruct group_archive *ng;
Xchar *file;
Xchar *path;
X{
X
X#ifdef MV_ORIGINAL
X char crnt_path[MAXNAMLEN];
X#endif /*MV_ORIGINAL */
X
X char pmess[BUFSIZ];
X
X problem_article = TRUE;
X
X /* ALERT THE ADMINISTRATOR THAT A PROBLEM WAS ENCOUNTERED
X **
X ** A problem has been encountered. It could be that there is an
X ** format mismatch or there is already a file with the same
X ** issue/archive/msg-id name.
X ** Copy the problem file to the problems directory.
X ** Alert the Administrator that a problem was received.
X */
X
X (void) sprintf(pmess,"PROBLEM: Article %s in %s ",file,ng->ng_name);
X
X switch( type_of_problem ) {
X case NAME_PROB:
X (void) strcat(pmess,"does not support Archive-Name Archiving\n.");
X break;
X case VOL_PROB:
X (void) strcat(pmess,"does not support Volume-Issue Archiving\n.");
X break;
X case TYPE_PROB:
X (void) strcat(pmess,"has an invalid archive TYPE specified\n.");
X break;
X case DUP_PROB:
X if (article.repost != TRUE)
X (void) strcat(pmess,"is a Duplicate article.\n");
X else
X (void) strcat(pmess,"is a Reposted article.\n");
X (void) sprintf(pmess,"%s\tExisting Archived path - %s", pmess,path);
X break;
X }
X
X /* print the message out to the screen, crontab output, etc */
X
X (void) fprintf(errfp,"%s\n",pmess);
X
X /* log the initial detection message. */
X
X record_problem(pmess, file, ng);
X
X /* Handling Repostings.
X **
X ** MV_ORIGINAL
X ** The original article is placed into a "original" directory in
X ** the problems directory (if duplicated). The inbound reposted
X ** article is placed into the archive in the correct position.
X **
X ** ADD_REPOST_SUFFIX
X ** If ADD_REPOST_SUFFIX is defined, all reposts will have the
X ** string specified in REPOST_SUFFIX appended to the archive
X ** filename so that a repost of elm/part07 would appear in
X ** the archive as elm/part07-repost prior to any compression.
X ** The addition of the suffix was done in save_article().
X ** Handle this as the true duplicated article that it is.
X **
X ** No Reposting Defines specified:
X ** The inbound article would be placed into the archive in the
X ** correct position only if the initial article is not in the archive.
X ** Otherwise the reposted article is placed in the problems directory
X ** as a normal duplicate article as it is now.
X */
X
X#ifdef MV_ORIGINAL
X if (article.repost == TRUE) {
X /*
X ** save the duplicated path
X ** Caution: may have compression suffix attached
X */
X (void) strcpy(crnt_path, path);
X
X /* create the storage path for original copy */
X /* no slash needed between Originals and crnt_path below.. */
X
X (void) sprintf(path,"%s/%s%s",problems_dir,"Originals",crnt_path);
X
X /* Display and record the actions */
X (void) sprintf(pmess,"\tMoving %s (original)\n\tto %s",crnt_path,path);
X (void) fprintf(errfp,"%s\n",pmess);
X record_problem(pmess, file, ng);
X
X /* Make any necessary directories along the way. */
X (void) mkparents(path);
X
X /* copy the original out of the way */
X if (copy(crnt_path,path) != 0) {
X (void) fprintf(errfp,"copy failed for %s to %s\n", crnt_path, path);
X return(NULL);
X }
X
X set_ownership(path, ng);
X
X /* restore the destination path for inbound article */
X (void) strcpy(path,crnt_path);
X
X /* remove the existing file */
X (void) unlink(path);
X /*
X ** Must assure that "path" does not have a .Z type
X ** of suffix used in compression. If it does, it must
X ** be removed before continuing. This is cheating and
X ** will probably break but what the hell.
X */
X (void) remove_suffix(path);
X }
X else
X
X#endif /* MV_ORIGINAL */
X
X /*
X ** Build the path string for the location of the article in
X ** the problems directory. Place the file in the appropriate
X ** directory in Article-Number format. In this manner, multiple
X ** problems will be stored as separate files.
X */
X
X (void) sprintf(path,"%s/%s/%s",problems_dir,ng->ng_name,file);
X
X /* Display and record the actions */
X (void) sprintf(pmess,"\tStoring Article %s at %s\n", file, path);
X (void) fprintf(errfp,"%s\n",pmess);
X record_problem(pmess, file, ng);
X
X /* Make any necessary directories along the way. */
X (void) mkparents(path);
X
X if (copy(file,path) != 0) {
X (void) fprintf(errfp,"copy failed for %s to %s\n", file, path);
X return(NULL);
X }
X
X /*
X ** Write the filename to the .archived file in the newsgroup's
X ** BASEDIR directory since we do not want it rearchived tomorrow.
X */
X write_archived(file, path);
X
X /*
X ** Return the path to the stored problem file.
X */
X return(path);
X}
X
Xwrite_patch_log(ng, path)
X struct group_archive *ng;
X char *path;
X{
X char *sp;
X FILE *plfp;
X struct stat sb;
X int hn;
X
X /*
X ** The .patchlog file is used to record the
X ** information specific to patches that come
X ** through the newsgroup.
X **
X ** The format of the .patchlog file is:
X **
X ** path-to-patch initial-volume initial-issue volume issue
X ** bb/patch01 22 105 23 77
X ** v47i022 22 105 23 77
X */
X
X /*
X ** If this is the first time that an entry is written to the
X ** patch log, add a header on top of the file for informational
X ** purposes only...
X */
X if ((stat(ng->patchlog ,&sb) != 0)) {
X plfp = efopen(ng->patchlog,"a+");
X
X (void) fprintf(plfp,"#\n#\tPatch log for %s\n#\n",
X ng->ng_name);
X
X (void) fprintf(plfp,"# %-30s%-11s%-13s%-6s%10s\n",
X "Path To", "Initial", "Initial",
X "Current", "Current");
X
X (void) fprintf(plfp,"# %-30s%-11s%6s%13s%10s\n#\n",
X "Patchfile", "Volume", "Issue", "Volume", "Issue");
X (void) fclose(plfp);
X }
X
X /*
X ** Get rid of the base directory.
X */
X sp = path + (strlen(ng->location)+1);
X
X plfp = efopen(ng->patchlog,"a+");
X (void) fprintf(plfp,"%-24s%12d%12d%12d%11d\n", sp,
X article.patch_volume, article.patch_issue,
X article.volume, article.issue);
X (void) fclose(plfp);
X}
END_OF_FILE
if test 20172 -ne `wc -c <'news_arc.c'`; then
echo shar: \"'news_arc.c'\" unpacked with wrong size!
fi
# end of 'news_arc.c'
fi
if test -f 'rkive.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'rkive.c'\"
else
echo shar: Extracting \"'rkive.c'\" \(17099 characters\)
sed "s/^X//" >'rkive.c' <<'END_OF_FILE'
X/*
X**
X** Subsystem: USENET Sources Archiver
X** File Name: rkive.c
X**
X** usage: rkive [ -dgstuvV ] [ -f config_file ] [-n newsgroup ]
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** Use of this software constitutes acceptance for use in an AS IS
X** condition. There are NO warranties with regard to this software.
X** In no event shall the author be liable for any damages whatsoever
X** arising out of or in connection with the use or performance of this
X** software. Any use of this software is at the user's own risk.
X**
X** If you make modifications to this software that you feel
X** increases it usefulness for the rest of the community, please
X** email the changes, enhancements, bug fixes as well as any and
X** all ideas to me. This software is going to be maintained and
X** enhanced as deemed necessary by the community.
X**
X** Kent Landfield
X** uunet!ssbell!kent
X**
X** History:
X** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X**
X*/
Xchar sccsid[] = "@(#)rkive.c 1.1 6/1/89";
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <dirent.h>
X#include <stdio.h>
X#include "article.h"
X#include "cfg.h"
X
X/*
X** This is necessary since the builtin makedir call uses
X** mknod which is a superuser only call for directories.
X*/
X#if (!HAVE_MKDIR && !USE_SYSMKDIR)
X#define ROOT_ONLY
X#endif
X
X#define UFMT "usage: %s [ -dgstuvV ] [ -f config_file ] [ -n newsgroup ]\n"
X
Xint overwrite;
Xint status_only;
Xstruct stat sbuf;
Xstruct group_archive *newsgrp;
X
Xchar tmp_mailfile[] = "/tmp/rkive.mail";
Xchar global_mailfile[] = "/tmp/gbl.mail";
X
Xchar *save_article();
Xchar *compress_file();
Xchar *do_compress();
Xchar *basename();
Xchar *suffix();
Xvoid archive();
X
Xchar *strcpy();
Xchar *strcat();
Xchar *strchr();
XFILE *efopen();
Xvoid exit();
X
Xextern int debug;
Xextern int verbose;
Xextern int test;
Xextern int problem_article;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X int c;
X extern char *optarg;
X char *nwsg = NULL;
X
X progname = argv[0];
X errfp = stderr;
X logfp = stdout;
X
X status_only = debug = verbose = 0;
X test = overwrite = fill_in_defaults = 0;
X
X /*
X ** Setup the default config file to be used
X ** unless the user specifies otherwise.
X */
X config_file = LOCATION;
X
X if (argc > 1) {
X while ((c = getopt(argc, argv, "dgstuvVn:f:")) != EOF) {
X switch (c) {
X case 'f':
X config_file = optarg;
X break;
X case 'd':
X debug++;
X verbose++;
X break;
X case 'g':
X fill_in_defaults++;
X break;
X case 'n':
X nwsg = optarg;
X break;
X case 's':
X status_only++;
X break;
X case 't':
X test++;
X verbose++;
X break;
X case 'u':
X overwrite++;
X break;
X case 'v':
X verbose++;
X break;
X case 'V':
X version();
X default:
X (void) fprintf(errfp, UFMT, progname);
X return(1);
X }
X }
X }
X
X setup_defaults();
X
X init_article();
X
X for (c = 0; c <= num; c++) {
X newsgrp = &group[c];
X /*
X ** Was a newsgroup specified on the command line ?
X */
X if (nwsg != NULL) {
X if (strcmp(nwsg, newsgrp->ng_name) != 0)
X continue;
X }
X archive();
X }
X
X if (!status_only) {
X /*
X ** Mail notification of the archived members to the
X ** list of users specified in the configuration file
X ** and remove the file containing the archived info.
X */
X mail_file(mail, global_mailfile, "Complete Archive Results ");
X (void) unlink(global_mailfile);
X }
X return(0);
X}
X
Xvoid archive()
X{
X struct dirent *dp;
X int cct;
X DIR *dfd;
X char *rp, *rec;
X char *dir = ".";
X char *new_member;
X char *archived_file;
X char *get_archived_rec();
X char newsgroup_directory[MAXNAMLEN];
X
X#ifdef ROOT_ONLY
X /*
X ** check to assure that the user is root if
X ** actual archiving is to take place. This is necessary
X ** if there is no mkdir system call.
X */
X
X if (!status_only && (getuid() != 0)) {
X (void) fprintf(errfp, "%s: Sorry, Must be root to rkive.\n",
X progname);
X exit(1);
X }
X#endif
X
X /* Remove any existing temporary mail file */
X
X (void) unlink(tmp_mailfile);
X cct = 0; /* counter for newsgroup message in global mail */
X
X /*
X ** Assure that there something specified in the
X ** archive location variable...
X */
X if (!*newsgrp->location) {
X (void) fprintf(errfp, "SKIPPING %s: No archive location specified..\n",
X newsgrp->ng_name);
X return;
X }
X
X /*
X ** print out the appropriate
X ** header for the newsgroup.
X */
X
X if (debug || (verbose && status_only)) {
X (void) fprintf(logfp,"\n\n");
X display_group_info(newsgrp);
X (void) fprintf(logfp,"\n");
X }
X else if (status_only)
X (void) fprintf(logfp, "%s\n",newsgrp->ng_name);
X
X /* convert newsgroup name into a disk path */
X
X rp = newsgrp->ng_name;
X
X /*
X ** convert all '.' to '/' to generate a path to the
X ** newsgroup directory relative from the specified SPOOLDIR.
X */
X
X while (*rp) { /* convert all */
X if (*rp == '.') /* '.'s to '/' */
X *rp = '/'; /* to create */
X rp++; /* the disk */
X } /* location */
X
X (void) sprintf(newsgroup_directory,"%s/%s", spooldir,newsgrp->ng_name);
X
X if (chdir(newsgroup_directory) != 0) {
X (void) fprintf(errfp,"Can't change directory to %s, %s not archived\n",
X newsgroup_directory, newsgrp->ng_name);
X return;
X }
X
X /*
X ** Create a path to the .archived file for the newsgroup's archive.
X ** This file is used to determine if an article has already been
X ** archived.
X */
X (void) sprintf(newsgrp->arc_done,"%s/.archived",newsgrp->location);
X
X /*
X ** Create a path to the .patchlog file for the newsgroup's archive.
X ** This file is used to record patches to posted software so that
X ** it can easily be determined what the full set of software is.
X */
X (void) sprintf(newsgrp->patchlog,"%s/.patchlog",newsgrp->location);
X
X /*
X ** locate a file that needs to be archived. This is done by
X ** a linear search of the directory with a linear search of
X ** of the contents of the .archived file. If the file is not
X ** specified in the .archived file, it has not been archived
X ** before and we can proceed with the archiving.
X */
X if ((dfd = opendir(dir)) == NULL) {
X (void) fprintf(errfp, "can't open %s\n", newsgroup_directory);
X return;
X }
X while ((dp = readdir(dfd)) != NULL) {
X if (strcmp(dp->d_name,".") == 0
X || strcmp(dp->d_name,"..") == 0)
X continue;
X
X if (stat(dp->d_name, &sbuf) != 0) {
X (void) fprintf(errfp, "can't stat %s/%s\n",
X newsgroup_directory, dp->d_name);
X continue;
X }
X
X /*
X ** If its not a regular file, we cannot archive it.
X */
X
X else if ((sbuf.st_mode & S_IFMT) != S_IFREG)
X continue;
X
X /*
X ** If the user has specified that a quick status
X ** listing should be produced then hop to it....
X */
X
X if (status_only) {
X if ((rec = get_archived_rec(dp->d_name)) == NULL)
X (void) fprintf(logfp,"\t<%s> Awaiting Archiving\n",dp->d_name);
X else if ((rp = strchr(rec,' ')) == NULL)
X (void) fprintf(logfp,"\t<%s> Archived\n",dp->d_name);
X else {
X rp++;
X *(rp-1) = '\0';
X (void) fprintf(logfp,"\t<%s> Archived as <%s>\n",rec,rp);
X }
X continue;
X }
X
X /*
X ** Archiving from here on out.
X */
X
X if (!needs_to_be_archived(dp->d_name))
X continue;
X
X if ((new_member = save_article(dp->d_name,newsgrp)) != NULL) {
X archived_file = compress_file(new_member,newsgrp);
X set_ownership(archived_file,newsgrp);
X
X /*
X ** If a problem has been encountered,
X ** the function do_problem handles
X ** the logging, and notifying.
X */
X
X if (!problem_article) {
X log_activities(archived_file,newsgrp);
X build_index(new_member,newsgrp);
X notify_users(archived_file,newsgrp,cct++);
X }
X }
X else
X (void) fprintf(logfp,"Unable to archive %s/%s!!!\n",
X newsgrp->ng_name, dp->d_name);
X }
X (void) closedir(dfd);
X
X if (!status_only) {
X /* Remove the expired entries from the .archived file */
X /* stored in the newsgroup's BASEDIR directory. */
X
X remove_expired();
X
X /* Mail notification of the archived members to the */
X /* list of users specified in the configuration file */
X /* and remove the file containing the archived info. */
X
X mail_file(newsgrp->mail_list, tmp_mailfile, newsgrp->ng_name);
X (void) unlink(tmp_mailfile);
X }
X return;
X}
X
X/*
X** Notify Users of Archiving.
X** If users have been specified to be informed, check to see
X** if they have requested a specific logging format. If so
X** use the specified format to notify the user. If not, use
X** "file archived at path" message.
X*/
Xnotify_users(filename,ng,num_msgs)
Xchar *filename;
Xstruct group_archive *ng;
Xint num_msgs;
X{
X /*
X ** Are there users specified in the
X ** newsgroup section ?
X */
X if ( *(ng->mail_list) ) {
X if ( *(ng->logformat) )
X logit(tmp_mailfile, ng->logformat, filename);
X else
X logit(tmp_mailfile, DEFAULT_LOG_FORMAT, filename);
X }
X
X /*
X ** Are there users specified in the
X ** global section ?
X */
X if ( *mail ) {
X if (num_msgs == 0) /* print the newsgroup name out */
X logit(global_mailfile, "\n\t\t:%G:\n",filename);
X if (*log_format)
X logit(global_mailfile, log_format,filename);
X else
X logit(global_mailfile, DEFAULT_LOG_FORMAT, filename);
X }
X}
X
X/*
X** Log_activities
X**
X** There are two possible logfiles that need to be written.
X** The group specific logfile (ng->logfile) and the global
X** log. If it has been configured to use a specific format
X** for the logging, do so. Else, just record the fact the
X** file was sucessfully archived and the date.
X*/
Xlog_activities(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X long clock;
X long time();
X char *ctime();
X
X char logbuf[BUFSIZ];
X char dms_date[30];
X
X if ( !*(ng->logformat) || !*log_format) {
X clock = time((long *)0);
X (void) strcpy(dms_date, ctime(&clock));
X *(dms_date+(strlen(dms_date)-1)) = '\0';
X (void) sprintf(logbuf,"%s archived %s",filename, dms_date);
X }
X
X if ( *(ng->logformat) )
X logit(ng->logfile, ng->logformat, filename);
X else
X logit(ng->logfile, logbuf, filename);
X
X if ( *log_format )
X logit(log, log_format, filename);
X else
X logit(log, logbuf, filename);
X}
X
X/*
X** logit
X**
X** This function is used to append a logfile record
X** if there is a logfile name specified.
X**
X*/
X
Xlogit(filename, format_of_log, arch_file)
Xchar *filename;
Xchar *format_of_log;
Xchar *arch_file;
X{
X FILE *fp, *fopen();
X
X if ( *(filename) ) { /* Is a logfile specified ? */
X if ((fp = fopen(filename,"a")) != NULL) {
X format_output(fp, format_of_log, arch_file, ARCHIVE);
X (void) fclose(fp);
X }
X }
X}
X
X
Xset_ownership(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X if (verbose) { /* Print out the actions about to be preformed */
X (void) fprintf(logfp,"chown\t<%d> <%s>\n", ng->owner, filename);
X (void) fprintf(logfp,"chgrp\t<%d> <%s>\n", ng->group, filename);
X }
X
X if (!test) { /* chown the owner/group to the desired values */
X if (chown(filename,ng->owner, ng->group) != 0)
X error("Can't change ownership of", filename);
X }
X
X if (verbose) { /* Print out the actions about to be preformed */
X (void) fprintf(logfp,"chmod\t<%o> <%s>\n", ng->modes, filename);
X }
X
X if (!test) { /* change the file modes to the specified modes */
X if (chmod(filename,ng->modes) != 0)
X error("Can't change modes of", filename);
X }
X}
X
Xmail_file(user_list, file_to_mail, nwsgrp)
Xchar *user_list;
Xchar *file_to_mail;
Xchar *nwsgrp;
X{
X char *list, *name;
X char cmdstr[80];
X
X /* Is there a list of users to mail to ? */
X if ( !*user_list || (strlen(user_list) == 0))
X return;
X
X /* Was there a notification file created ? */
X if (stat(file_to_mail, &sbuf) != 0)
X return;
X
X name = user_list;
X do {
X if ((list = strchr(name,',')) != NULL) {
X list++;
X *(list-1) = '\0';
X }
X
X#ifdef SUBJECT_LINE
X (void) sprintf(cmdstr, "%s -s '%s' %s < %s",
X MAIL, nwsgrp, name, file_to_mail);
X#else
X (void) sprintf(cmdstr, "%s %s < %s", MAIL, name, file_to_mail);
X#endif
X if (verbose)
X (void) fprintf(logfp,"Mailing %s Archived results to %s\n",
X nwsgrp, name);
X if (!test)
X (void) system(cmdstr);
X
X name = list;
X
X } while (name != NULL);
X return;
X}
X
Xbuild_index(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X if (*(ng->index)) { /* Is there a newsgroup index file ? */
X if (*(ng->indformat)) /* Yes, Is there a index file format? */
X logit(ng->index, ng->indformat, filename);
X else if (*index_format) /* No, is there a global format ? */
X logit(ng->index, index_format, filename);
X else /* No, use the default index format */
X logit(ng->index, DEFAULT_INDEX_FORMAT, filename);
X }
X
X if (*index) { /* Is there a global index file ? */
X if (*index_format) /* Yes, Is there a global file format ? */
X logit(index, index_format, filename);
X else /* No, so use the default index format */
X logit(ng->index, DEFAULT_INDEX_FORMAT , filename);
X }
X}
X
X
Xchar *compress_file(filename,ng)
Xchar *filename;
Xstruct group_archive *ng;
X{
X static char compressed[MAXNAMLEN];
X
X (void) strcpy(compressed, filename); /* store the filename */
X
X /* Check to see if a group specific compress was specified. */
X /* If so, then execute the command with the filename passed in. */
X /* Else check to see if a global compress was specified. If so, */
X /* then execute the command with the filename passed in. */
X /* If both are NULL, no compression is done. */
X
X if (*(ng->compress))
X (void) strcat(compressed, do_compress(ng->compress, filename));
X else if (*compress)
X (void) strcat(compressed, do_compress(compress, filename));
X
X return(compressed);
X}
X
Xchar *do_compress(packit,filename)
Xchar *packit;
Xchar *filename;
X{
X char *comp_cmd;
X char cmd[BUFSIZ];
X
X (void) sprintf(cmd,"%s %s", packit, filename);
X
X /*
X ** get the basename of the command to use.
X */
X comp_cmd = basename(packit);
X
X if (verbose)
X (void) fprintf(logfp,"%s %s\n", comp_cmd, filename);
X
X if (!test)
X (void) system(cmd);
X
X return(suffix(comp_cmd));
X}
X
X
X/*
X** Record_problem()
X** This function is used to log problems encountered
X** to the designated parties.
X*/
Xrecord_problem(msg_fmt,filename,ng)
Xchar *msg_fmt;
Xchar *filename;
Xstruct group_archive *ng;
X{
X /*
X ** This function is used in the event that a problem
X ** has occurred during archiving. It mails a message
X ** to the newsgroup speecified list and it mails a
X ** message to the globally specified users.
X **
X ** It then logs the fact into both the newsgroup
X ** and the global logfiles if they have been specified.
X */
X
X if ( *(ng->mail_list) )
X logit(tmp_mailfile, msg_fmt, filename);
X
X if ( *mail )
X logit(global_mailfile, msg_fmt,filename);
X
X logit(ng->logfile, msg_fmt, filename);
X logit(log, msg_fmt, filename);
X}
END_OF_FILE
if test 17099 -ne `wc -c <'rkive.c'`; then
echo shar: \"'rkive.c'\" unpacked with wrong size!
fi
# end of 'rkive.c'
fi
echo shar: End of archive 4 \(of 4\).
cp /dev/null ark4isdone
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