[comp.sources.misc] v07i060: A news archiver

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (08/08/89)

Posting-number: Volume 7, Issue 60
Submitted-by: tony@ajfcal.UUCP (Tony Field)
Archive-name: arcnews

[I wonder if SEA wants to make noise about the name?  :-(  ++bsa]

Arcnews is a simple programme to conveniently archive comp.sources.* (and
other) newsgroups from the network. Essentially, it is a special purpose
"cp" command that knows about news headers. Archive directories are
constructed based on the contents of the "Newsgroup", "Subject", and
"Archive-name" headers. 

Arcnews can archive from "news" or "lbgm" directories or as part of the
news forwarding system.

   usage:      arcnews [ -v ] [ file file...] archive.path
   eg:         arcnews /usr/spool/lbgm/* /u/archive
               arcnews /usr/spool/comp/sources/unix/* /u/archive
               cat news.article | arcnews -v /u/archive 2>>arc.log
              
A posting:      Newsgroups: comp.sources.unix
                Subject: v19i001:  A reimplementation of the System V shell
                Archive-name: ash/part01

archives to:    /<archive path>/comp/sources/unix/ash/part01
 - - - - - - - - - - - - - - - - -
#! /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 shell archive."
# Contents:  Readme Makefile arcnews.1 arcnews.c arccron.SH arcauto.SH
# Wrapped by ajf@ajfcal on Sun Jun 11 11:20:08 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Readme' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Readme'\"
else
echo shar: Extracting \"'Readme'\" \(7331 characters\)
sed "s/^X//" >'Readme' <<'END_OF_FILE'
X                            a r c n e w s
X                            ~~~~~~~~~~~~~
X   Arcnews is a simple program to conveniently archive (in particular)
X   the comp.sources.* newsgroups from the network.  Other groups may
X   be archived if desired.
X   
X   Essentially, it is a special purpose "cp" command that recognizes
X   news headers. Archive directories are constructed based on the
X   contents of the Newsgroup:, Subject:, and Archive-name: headers.
X
X	usage:	   arcnews [ -v ] [ file file...] archive.path
X
X	   eg:     arcnews -v /usr/spool/lbgm/[A-Z]* /u/archive
X		   arcnews /usr/spool/news/comp/sources/unix/* /u/archive
X		   cat this.article | arcnews -v /u/archive 2>>log.file
X
X   Error messages and -v)erbose output are written to stderr.
X   
X   Arcnews may be run as part of the news forwarding system, or may be
X   run as a utility (from cron?) than can archive "news" or "lbgm"
X   directories.
X
XHOW IT WORKS
X~~~~~~~~~~~~
X   Arcnews locates the Newsgroups:, Subject:, and Archive-name: lines 
X   in the news headers. It attempts to build a path that looks like:
X
X   	/<archive dir>/<news group>/<archive name>
X
XFor groups that use the Archive-name header such as:
X
X        Newsgroups: comp.sources.unix
X        Subject: v19i001:  A reimplementation of the System V shell, Part01/08
X        Archive-name: ash/part01
X
X   it will archive the posting into:
X
X        /<archive path>/comp/sources/unix/ash/part01
X
X   If the Archive-name: is "invalid", it will try to construct a file
X   name from the second word in the Subject: line.  Only the first
X   newsgroup name is used for archive directory construction.
X
X   For:    Newsgroups: comp.sources.unix,comp.sources.d
X           Subject: v19INF1:  Start of Volume 19
X           Archive-name: /dev/null
X
X   the file will be sent to:
X
X            /<archive path>/comp/sources/unix/v19INF1
X
XFor groups determining the file name from the Subject header:
X   
X   For:    Newsgroups: comp.doc
X	   Subject: RFC1097 - TELNET SUBLIMINAL-MESSAGE Option
X
X   the file will be sent to:
X   
X   	   /<archive path>/comp/doc/RFC1097
X   
XFor other non-Archive-name and non-Subject groups
X
X   If the group is not comp.sources.unix/misc or other group using the
X   Archive-name header, (e.g. comp.sources.bugs) then the archived file
X   name created from YYMmmDD.PID:
X
X	    /<archive path>/comp/sources/bugs/89Jan01.1234
X
XNEWS FORWARDED TO ARCNEWS
X~~~~~~~~~~~~~~~~~~~~~~~~~
X   If arcnews is to be directly used as part of the news forwarding
X   system, the news "sys" file should contain something like:
X
X        SOURCES:world,comp.sources.unix,comp.sources.misc,
X              	comp.sources.bugs,comp.doc
X                ::/usr/lib/news/arcauto /u/archive newsadmin
X
X   where "/u/archive" is the path to the archive directory and
X   "newsadmin" is the user to whom mail is sent if a file cannot
X   be archived.
X
X   Each forwarded article is archived to the /u/archive directory.
X   The "arcauto" is a shell script that will call arcnews for the
X   actual archive process and mails error messages the the newsadmin.
X
X   Arcnews should be setuid to root (on Xenix) if it is part of the news
X   forward system.  It does a setuid(seteuid()) to ensure that all
X   files can be created in the archive directory.
X
XWITH LBGM
X~~~~~~~~~
X   If you have already installed lbgm as a basic news archiver, then
X   you can use arcnews with the simple addition of a cron'ed script.
X   
X   Lbgm directories are a convenient collection bin. If they are kept
X   clean, they only contain recent postings. The contents of the
X   directory to be archived may be any mixture of files: e.g.:
X   comp.sources.misc, comp.sources.unix, comp.sources.bugs.
X
X   Archives can be initially set up using lbgm as the news forwarding
X   system. A news "sys" file might contain:
X
X        SOURCES:world,comp.sources.unix,comp.sources.misc,
X              	comp.sources.bugs,comp.doc,to.ajfcal
X                ::/usr/lib/news/lbgm /usr/spool/lbgm/src notify
X
X   After news forwards files to "lbgm", a cron'ed script can move the
X   files from the "lbgm" directory to the tidy archive. After the move,
X   the contents of the "lbgm" directory are deleted - ready for the next
X   day's "fantastic sources". The following is an extract from a cron'ed
X   news cleanup script:
X
X		cd /usr/spool/lbgm/src
X		/usr/lib/news/arcnews [A-Z]* /u/archive
X		case $? in
X		0)	rm [A-Z]*
X			/local/bin/empty save.log;;   # make zero length file
X		esac
X
XCOMPILE
X~~~~~~~
X   You may wish to modify the list of moderated groups that can use the
X   Subject: header first word to construct a file name. An example of
X   this is comp.doc used for the posting of a few RFC's. The first word
X   after the Subject: header is the "RFCnnnn".
X
X   Edit Makefile for any configuration changes needed.
X   
X   then:	make all
X   
X   as root:	make install
X
X   After "make all", a sample entry for your news sys file is placed
X   in sys.sample.  Modify this to reflect the news groups that you wish
X   to have archived and then include the entry into your sys file.
X   The arcauto script file is also created with "make all".
X
XTESTING
X~~~~~~~
X   If arcnews is part of the news forwarding system, it may be tested
X   with "local" posting. A local posting is not forwarded to other
X   sites. Create a news forward entry in your sys file that looks
X   something like:
X
X        aTest:world,misc.test,to.yoursitename
X                  ::/usr/lib/news/arcauto /usr/tmp news
X   
X   Post local news to either "misc.test" or "to.yoursitename".
X   The posted article will be archived under the /usr/tmp directory.
X   Since it is a local posting only, it will not leave your system.
X   After the test, you can cancel the articles.
X   
X   If arcnews is run as a utility (from cron?), then it may be tested
X   with the following:
X   
X   		cd /usr/spool/news/comp/sources/misc
X   		arcnews -v * /usr/tmp
X
X
XConfusion
X~~~~~~~~~
X   Sometimes, you may find that you have archives that you did not
X   expect. This may happen with cross-postings to multiple groups (i.e.
X   there are multiple groups specified in the Newsgroups: header).
X   Arcnews chooses only the FIRST news group for the construction of the
X   destination file name. This generally does not happen in moderated
X   source groups, but it is not impossible.
X
X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
XNOTES:	Arcnews was written for Xenix 2.2.3 / 386 using the MicroSoft
X~~~~~~  C compiler.  Hopefully, it is portable.
X
X  1.	A number of moderated source/binary groups follow the
X	"Archive-name:" conventions and will archive nicely with
X	arcnews.  At least these do (I scanned my current news
X	to come up with this list - however i only keep 3 days
X	of news; this is incomplete):
X
X		comp.sources.unix
X		comp.sources.misc
X		comp.binaries.atari.st
X		comp.binaries.ibm.pc
X
X  2.	Arcnews does not compress the received files.  A programme
X	change would be necessary.  E.g.  have arcnews do a
X
X		system ("compress filename");
X	
X	after the received file is closed.  It might be better to use
X	something like zoo rather than compress....
X	
X  3.	"lbgm" is the "Little Bird Gave Me" archiver that comes with
X	the news distribution in the ~/misc/lbgm directory.
X
X  4.	Is the -s "subject" option avaiable on all mail systems?
X	It is used by arcauto to send mail.
X~~~~~~
Xtony field      (tony@ajfcal)
END_OF_FILE
if test 7331 -ne `wc -c <'Readme'`; then
    echo shar: \"'Readme'\" unpacked with wrong size!
fi
# end of 'Readme'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(1853 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#	makefile for     a r c n e w s
X#
X#	NOTE:   arcnews should be setuid root if running as part
X#		of the news forward system.  (it tries to setuid(seteuid())).
X#	You should be "root" when you "make install".
X#
X# installation steps:
X#
X#	1. edit the configuration section to match you system requirements
X#	2. make all
X#	3. examine the "arcauto" shell script
X#	4. edit the generated "sys.sample" file to identify all groups
X#		that are to be archived.  Add it to your sys file.
X#	5. create the archive directory
X#	6. as root,  make install
X#		
X#   ------------ configuration -------------
X#
X#	   Where do we put arcnews  BINDIR
X#	         archive directory  ARCDIR
X#	          log file Arc.Log  LOGDIR
X#		          news uid  NEWSID
X#		        news group  NEWSGROUP
X#	User id to notify if error  NOTIFY
X
XBINDIR=/usr/lib/news
XARCDIR=/u/archive
XLOGDIR=/u/archive
XNEWSID=news
XNEWSGROUP=news
XNOTIFY=news
X
X#    --------- end configuration -----------
X
XCFLAGS = -O
XOBJECTS = arcnews.o
X
Xall:	arcnews auto sys
X
Xarcnews:	$(OBJECTS)
X	cc $(CFLAGS) $(OBJECTS) -o arcnews
X
Xauto:
X	rm -f arcauto
X	sed 's@BINdir@$(BINDIR)@g' arcauto.SH \
X		| sed 's@ARCdir@$(ARCDIR)@g' \
X		| sed 's@LOGdir@$(LOGDIR)@g' > arcauto
X	chmod +x arcauto
X
Xsys:
X	rm -f sys.sample
X	echo 'ArcSrc:world,comp.sources.unix,comp.sources.misc,comp.sources.bugs::$(BINDIR)/arcauto $(NOTIFY)' > sys.sample
X
Xinstall:
X	cp arcauto $(BINDIR)
X	cp arcnews $(BINDIR)
X	chmod 4711 $(BINDIR)/arcnews
X	chgrp $(NEWSGROUP) $(BINDIR)/arcnews
X	chgrp $(NEWSGROUP) $(BINDIR)/arcauto
X	chown $(NEWSID)    $(BINDIR)/arcauto
X
Xshar:
X	rm -f arcnews.shar
X	shar Readme Makefile arcnews.1 arcnews.c arccron.SH \
X		arcauto.SH > arcnews.shar
X
Xtar:
X	rm -f arcnews.tar
X	tar cvAf arcnews.tar Readme Makefile arcnews.1 arcnews.c \
X		arccron.SH arcauto.SH
X
Xclean:
X	rm -f arcnews.o
X	rm -f arcnews
X	rm -f arcauto
X	rm -f sys.sample
X	rm -f *.B	
END_OF_FILE
if test 1853 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'arcnews.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'arcnews.1'\"
else
echo shar: Extracting \"'arcnews.1'\" \(1239 characters\)
sed "s/^X//" >'arcnews.1' <<'END_OF_FILE'
X.TH ARCNEWS 1 ""
X.SH NAME
Xarcnews \- archive news files
X.SH SYNOPSIS
X.B arcnews
X[
X.B -v
X] [
X.I file file ...
X]
X.I archive.path
X.nf
X
X   where           -v = verbose (display file names)
X                 file = list of files to be archived (or stdin)
X         archive.path = directory path to archive
X
Xe.g.:
X       arcnews /u/lbgm/src/[A-Z]* /u/archive
X.fi
X
X.SH DESCRIPTION
X.B Arcnews
Xis used to archive
X.I news
Xfiles to an archive.  It is primarily intended to archive
X.I comp.sources.unix
Xand
X.I comp.sources.misc,
Xhowever it will also archive any other news group.
X
XEach 
X.I news 
Xarticle is examined for the following headers:
X
X.nf
X       Newsgroups:
X       Subject:
X       Archive-name:
X.fi
X
XA suitable file name is determined.  The final destination path
Xis a concatenation of the
X.I archive.path
Xand the resolved file name.  It generally looks like:
X
X    /<
X.I archive.path
X>/<
X.I newsgroup
X>/<
X.I archive-name
X>
X
XFor example, if the 
X.I news 
Xarticle contained:
X
X.nf
X        Newsgroups: comp.sources.unix
X        Subject: v19i001:  programme description
X        Archive-name: this/part01
X.fi
X
X.B arcnews
Xwill archive the posting to:
X
X        /<
X.I archive.path
X>/comp/sources/unix/this/part01
X
X.SH AUTHOR
XTony Field    (tony@ajfcal)
END_OF_FILE
if test 1239 -ne `wc -c <'arcnews.1'`; then
    echo shar: \"'arcnews.1'\" unpacked with wrong size!
fi
# end of 'arcnews.1'
fi
if test -f 'arcnews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'arcnews.c'\"
else
echo shar: Extracting \"'arcnews.c'\" \(13454 characters\)
sed "s/^X//" >'arcnews.c' <<'END_OF_FILE'
X/* ta=4 */
X/****************************************************************************
X*					a r c n e w s . c										*
X*																			*
X*	Archive news files.														*
X*																			*
X*	Tony Field     (tony@ajfcal)											*
X****************************************************************************/
X
X/*	Archive news articles (comp.sources.*).  Construct a file name 
X	based on the "Newsgroups:", "Subject:" and "Archive-name:" headers.
X
X	usage		arcnews [ -v ] [ file file ...]  archive.path
X						  -v = verbose
X						  
X				input could be from stdin
X
X		eg:		arcnews /usr/spool/lbgm/[A-Z]* /u/archive
X
X	MUST BE SETUID ROOT to work in news forward environment.
X				(i.e.  chmod 4711 arcnews)
X				
X	EXIT CODES:		0 = successful operation
X				   >0 = error of some form has been noted  (usually 2)
X
XNOTE:	Modify the list of "subject_line_groups[]" for those moderated
X		groups that use an archive file name as the first word of the
X		Subject: line.
X
XBUGS:	maybe arcnews should quit when it finds the first error.
X*/
X
X#include <stdio.h>
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#define		NLINES		20		/*	number of lines to search for
X									Newsgroups:, Subject: Archive-name:
X								*/
X
X#define IS_FILE		0			/*	file type from ftype()	*/
X#define	IS_DIR		1
X#define	IS_SPECIAL	2
X#define	IS_ERROR	-1
X
X
X/*	construct the list of groups that use the first word of Subject:
X											  ~~~~~~~~~~~~~~~~~~~~~~
X*/
Xchar	*subject_line_groups[] =
X		{	"comp.doc",
X			"comp.doc.techreports",
X			(char *) NULL						/*	<--- end of list	*/
X		} ;
X
Xchar *first_lines[NLINES];
Xint		nlines;
Xint		nfiles;
Xchar	pgm_name[200];
X
Xextern int errno;
Xint		rc = 0;
X
Xmain (argc, argv)
Xint		argc;
Xchar	*argv[];
X{	int		i, j, c;
X	FILE	*fp;
X	int		verbose = 0;
X	int		nfiles = 0;
X	char	arc_dir[200];
X	extern char *optarg;
X	extern int	optind;
X	char 	amon[10], alt_fname[50], t[50];
X	int		day, year, pid;
X	unsigned short	guid, geuid, getuid(), geteuid(), getegid();
X
X
X	strcpy (pgm_name, argv[0]);
X	fname_only (pgm_name);
X
X	if (argc == 1)
X		usage ();
X
X	/*	we really want to say:
X				setuid(geteuid());
X				setgid(getegid());
X		but Xenix get upset unless getuid() == root
X	*/
X	guid = getuid();
X	geuid = geteuid();
X	if (guid != geuid)
X	{
X		setuid(geuid);
X		setgid(getegid());
X		if (geuid != getuid())
X		{	fprintf (stderr, "%s: must be setuid root\n", pgm_name);
X			exit (1);
X		}
X	}
X
X	while ((c = getopt(argc, argv, "v")) != -1)
X	{	switch (c)
X		{
X		case 'v':
X			verbose = 1;
X			break;
X		
X		default:
X			usage ();
X		}
X	}
X
X	strcpy (arc_dir, argv[--argc]);
X
X	if (ftype (arc_dir) != IS_DIR)
X	{	fprintf (stderr, "%s: %s is not an valid directory\n", pgm_name, arc_dir);
X		exit (2);
X	}
X	if (optind >= argc)
X	{	/* make a file name as  YYMMMDD.PID.  eg.  89Jan01.1234 */
X		get_time (t);
X		sscanf (t, "%*s%s%d%*s%d", amon, &day, &year);
X		pid = getpid ();
X		sprintf (alt_fname, "%d%s%02d.%d",year % 100, amon, day, pid);
X		arc_file (arc_dir, alt_fname, verbose, "stdin");
X	}
X	else
X	{	for (i = optind;  i < argc;  i++)
X		{	arc_file (arc_dir, argv[i], verbose, NULL);
X		}
X	}
X	exit (rc);
X}
X
X
X/****************************************************************************
X*	arc_file()																*
X*	archive the given file name												*
X****************************************************************************/
X
Xarc_file (arc_dir, fn, verbose, alt_fname)
Xchar	*fn;			/*	name of file to archive							*/
Xchar	*arc_dir;		/*	name of archive directory						*/
Xchar	*alt_fname;		/*	NULL if file name specified or "stdin"			*/
Xint		verbose;		/*	1 = display operation,  0 = quiet mode			*/
X{	int		i, j, c;
X	FILE	*fp;
X	char	line[1000], cline[1000];
X	char	*words[200];
X	int		nwords, trc;
X	char	arc_name[200], archive[200];
X	char	filename[200], dirname[200], subject[200], group[200];
X	int		nblanks;
X	int		have_group, have_subject, have_archive, sub_line;
X
X
X	trc = 0;
X
X	if (alt_fname == NULL  &&  ftype (fn) != IS_FILE)
X		return (-1);
X
X	strcpy (archive, fn);
X	fname_only (archive);
X	if (strcmp (archive, "save.log") == 0)		/*	ignore save.log	*/
X		return (-1);
X	
X	if (alt_fname)
X		fp = stdin;
X	else if ((fp = fopen (fn, "r")) == NULL)
X	{	rc |= 2;
X		return (-1); 
X	}
X
X	archive[0] = '\0';
X	*subject = '\0';
X	nlines = nblanks = 0;
X	have_group = have_subject = have_archive = 0;
X	while (nlines < NLINES  &&  fgets (line, 999, fp))			/*	get header lines	*/
X	{
X		first_lines[nlines] = (char *) malloc (strlen (line) + 1);
X		strcpy (first_lines[nlines], line);
X		nlines++;
X
X		strcpy (cline, line);
X		nwords = getwords (cline, words);
X		if (nwords)
X		{	if (strcmp (words[0], "Newsgroups:") == 0)
X			{	have_group = 1;
X				strcpy (archive, arc_dir);
X				strcat (archive, "/");
X				strcat (archive, words[1]);
X				strcpy (group,   words[1]);
X			}
X			else if (strcmp (words[0], "Subject:") == 0)
X			{	if (nwords >= 2)
X				{	strcpy (subject, words[1]);
X					trim (subject);					/* remove ":" */
X					have_subject = 1;
X				}
X				else *subject = '\0';
X			}
X			else if (strcmp (words[0], "Archive-name:") == 0)
X			{	have_archive = 1;
X				strcpy (arc_name, words[1]);
X			}
X		}
X		else if (++nblanks == 2)			/*	quit if 2 blank lines	*/
X			break;
X	}
X
X	if (have_group == 0)					/* invalid news file		*/
X	{	if (fp != stdin)
X			fclose (fp);
X		return (-1);
X	}
X
X	if (have_archive)
X	{	/*	have an Archive-name: header specified.
X			sometimes R$alz uses /dev/null for notes.
X			if so, use the Subject: line for the file name.
X 		*/
X		if (arc_name[0] ==  '/')
X		{	if (have_subject  &&  *subject)
X				strcpy (arc_name, subject);	/*	use subject as file name */
X			else
X				strcpy (arc_name, fn);	/* 	no subject, use original file name */
X			fname_only (arc_name);
X		}
X	}
X	else
X	{	/*	subject_line_groups[] get file name from Subject:	*/
X	
X		for (j = sub_line = 0;  subject_line_groups[j] != NULL;  j++)
X		{	if (sub_line = (strcmp (group, subject_line_groups[j]) == 0))
X				break;
X		}
X		if (sub_line   &&  have_subject  &&  *subject)
X			strcpy (arc_name, subject);
X		else
X		{	/*	all other groups use original file name	*/
X			strcpy (arc_name, fn);	
X			fname_only (arc_name);
X		}
X	}
X
X	/*	now make the path to the file and actually do a copy */
X
X	names (archive, arc_name, filename, dirname);
X	if (makepath (dirname) == 0)
X	{
X		trc = copyfile (fp, filename);
X		if (verbose  ||  trc)
X		{
X			fprintf (stderr, "%s %s -> %s\n", pgm_name, fn, filename);
X			if (trc)
X				fprintf (stderr, "--------^^^-- copy failed\n");
X		}
X		rc |= trc;
X		nfiles++;
X	}
X	else
X	{	fprintf (stderr, "%s: cannot create directory %s\n", pgm_name, dirname);
X		rc |= 2;
X		trc = 1;
X	}
X	if (trc)
X	{	fputs ("---> header of offending file:\n", stderr);
X		for (j = 0;  j < nlines;  j++)
X			if (first_lines[j][0] > ' ')
X				fprintf (stderr, "\t%s", first_lines[j]);
X			else
X				break;
X	}
X	if (fp != stdin)
X		fclose (fp);
X	for (j = 0;  j < nlines;  j++)
X		free (first_lines[j]);
X	return (trc);
X}
X
X
X/****************************************************************************
X*	name()																	*
X*	given a archive name and program id, return the full file name and		*
X*	the path only.															*
X****************************************************************************/
X
Xnames (archive, arc_name, filename, dirname)
Xchar	*archive;		/*	in: such as /u/archive/comp.sources.unix		*/
Xchar	*arc_name;		/*	in: such as program/part01						*/
Xchar	*filename;		/*	ret: /u/archive/comp/sources/unix/program/part01*/
Xchar	*dirname;		/*	ret: /u/archive/comp/sources/unix/program		*/
X{	char	*c, *strrchr();
X
X	strcpy (filename, archive);
X	dots (filename);				/*	change a.b.c to a/b/c  */
X	strcat (filename, "/");
X	strcat (filename, arc_name);	/*	arc_name may be a path */
X
X	strcpy (dirname, filename);
X	c = strrchr (dirname, '/');
X	*c = '\0';
X}
X
X/****************************************************************************
X*	dots()																	*
X*	translate . to /														*
X****************************************************************************/
X
Xdots (c)
Xchar	*c;
X{
X	while (*c)
X	{	if (*c == '.')
X			*c = '/';
X		c++;
X	}
X}
X
X/****************************************************************************
X*	fname_only ()															*
X*	replace the path with just the file name (right end of the path)		*
X****************************************************************************/
X
Xfname_only (fname)
Xchar	 *fname;
X{	char	c[500], *strrchr(), *tc;
X
X	if ((tc = strrchr (fname, '/')) != NULL)
X	{	strcpy (c, tc + 1);
X		strcpy (fname, c);
X	}
X}
X
X/****************************************************************************
X*	getwords ()																*
X*	given a string 's', return pointers in 'words' that point to the		*
X*	beginning of each word.  's' is modified with null word terminators.	*
X*	Function return is the number of words found							*
X****************************************************************************/
X
Xgetwords(s, words)
Xchar	*s;				/*	string to scan									*/
Xchar	*words[];		/*	vector of words found.							*/
X{	int	nwords = 0;
X
X	while (*s)
X	{	if (*s > ' ')
X		{	words[nwords++] = s;
X			while (*s  &&  *s > ' '  &&  *s != ',')
X				s++;
X			if (*s == '\0')
X				return (nwords);
X			*s++ = '\0';
X		}
X		else
X			s++;
X	}
X	return (nwords);
X}
X
X/****************************************************************************
X*	trim()																	*
X*	remove trailing ':' marks.  (eg subject:)								*
X****************************************************************************/
X
Xtrim (s)
Xchar	*s;
X{
X	int	n;
X	
X	n = strlen (s);
X	if (s[n-1] == ':')
X		s[n-1] = '\0';
X}
X
X/****************************************************************************
X*	makepath ()																*
X*	for a given path specification, make all directores needed to ensure	*
X*	that the path is valid.													*
X*	If the path is valid, then return 0.  If the path cannot be made,		*
X*	then return 2.															*
X****************************************************************************/
X
Xmakepath (path_name)
Xchar	*path_name;
X{	char	path[500], p[500], *w[50], cmd[500];
X	char	*c, *strrchr();
X	int		i, j, n;
X	
X	strcpy (path, path_name);
X	if ((c = strrchr (path, '/')) == NULL)
X		return (-1);
X	
X	/*	if path exists, the return now */
X
X	if (access (path, 7) == 0)
X		return (0);
X
X	/*	path does not exist.  Start from left side of path and
X		build towards the right as necessary
X	*/
X	c = path;
X	if (*c != '/')				/* get each path directory */
X	{	n = 0;
X		w[n++] = path;
X	}
X	else
X		n = 0;
X	while (*c)
X	{	if (*c == '/')
X		{	*c++ = '\0';
X			w[n++] = c;
X		}
X		else
X			c++;
X	}
X
X	for (i = 0;  i < n;  i++)		/* try /a, /a/b, /a/b/c, etc. in order */
X	{	p[0] = '\0';
X		if (*path_name == '/')
X			strcat (p, "/");
X		for (j = 0;  j <= i;  j++)
X		{	strcat (p, w[j]);
X			if (j == i)
X				break;
X			strcat (p, "/");
X		}
X		switch (ftype (p))
X		{
X		case IS_ERROR:
X			   	if (errno == ENOENT)				/*	no entry, build it	*/
X				{	sprintf (cmd, "mkdir %s", p);
X					j = system (cmd);
X					chmod (p, 0775);
X					rc |= j;
X					if (j)
X						return (j);
X				}
X				else
X				{	rc |= 2;
X					return (2);
X				}
X				break;
X
X		case IS_DIR: 					/*	directory already exists, continue*/
X				break;
X		
X		default:						/*	file with same name. bad.		*/
X				rc |= 2;
X				return (2);
X				break;
X		}
X	}
X	return (0);
X}
X
X/****************************************************************************
X*	copyfile()																*
X*	If a copy problem happens, return -1,  otherwise return 0				*
X****************************************************************************/
X
Xcopyfile (fp_in, out_file)
XFILE	*fp_in;
Xchar	*out_file;
X{
X	FILE	*fp_out;
X	int		r_many, w_many, rc, i;
X	char	buf[2000];
X
X	if ((fp_out = fopen (out_file, "w")) == NULL)
X		return (2);
X
X	for (i = 0;  i < nlines;  i++)
X		fputs (first_lines[i], fp_out);
X
X	rc = 0;
X	while (r_many = fread (buf, 1, 2000, fp_in))
X	{	w_many = fwrite (buf, 1, r_many, fp_out);
X		if (w_many != r_many)
X		{	rc = 2;
X			break;
X		}
X		if (r_many < 2000)
X			break;
X	}
X	fclose (fp_out);
X	chmod (out_file, 0664);
X	
X#ifdef COMPRESSION
X	/*	compress the file just received.  should use zoo and 
X		create an library file
X	*/
X	if (rc == 0)
X	{	sprintf (buf, "/local/bin/compress %s", out_file);
X		system (buf);
X	}
X#endif
X	return (rc);
X}
X
X
X/****************************************************************************
X*	ftype (name)															*
X*																			*
X*	What is "name"?															*
X*		0 = file,  1 = directory,   2 = special file,   -1 = error			*
X****************************************************************************/
X
Xftype (name)
Xchar	*name;
X{
X	struct stat buf;
X
X	if (stat (name, &buf) != 0)
X		return (IS_ERROR);
X	if ((buf.st_mode & S_IFMT) == S_IFDIR)
X		return (IS_DIR);
X	if ((buf.st_mode & S_IFMT) == S_IFREG)
X		return (IS_FILE);
X	return (IS_SPECIAL);
X}
X
X
X/****************************************************************************
X*	time_name()																*
X****************************************************************************/
X
Xget_time (t)
Xchar	*t;
X{
X	long	n_time, time ();
X	char	*x_time, *cc, *strchr(), *ctime();
X	
X	n_time = time ((long *) 0L);		/* get time 		*/
X	x_time = (char *) ctime (&n_time);	/* convert ascii 	*/
X	if ((cc = strchr (x_time, '\n')) != NULL)
X		*cc = '\0';
X	strcpy (t, x_time);
X}
X
X/****************************************************************************
X*	usage()																	*
X****************************************************************************/
X
Xusage ()
X{	fprintf (stderr, "usage:   %s [ -v ] [ file file ...]  archive.path\n\n", pgm_name);
X	fprintf (stderr, "e.g.:    %s /usr/spool/lbgm/src/[A-Z]* /u/archive\n\n",pgm_name);
X	exit (2);
X}
END_OF_FILE
if test 13454 -ne `wc -c <'arcnews.c'`; then
    echo shar: \"'arcnews.c'\" unpacked with wrong size!
fi
# end of 'arcnews.c'
fi
if test -f 'arccron.SH' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'arccron.SH'\"
else
echo shar: Extracting \"'arccron.SH'\" \(652 characters\)
sed "s/^X//" >'arccron.SH' <<'END_OF_FILE'
X:
X#	A variation of this can be included to execute from cron
X#	to automatically take all files from a chosen directory
X#	and archive them to a tidy directory
X#
X#	In this case, all files in "/usr/spool/lbgm/src" are archived
X#	into the "/u/archive" directory.
X#
X#	Since all lbgm-generated file name start with an upper case
X#	letter, select [A-Z]* (this ignores the save.log file).
X#
X#	After the files are archived, the lbgm directory is cleaned.
X#	("empty" makes a zero-length file).
X
X#	Any errors are reported by mail from cron.
X
Xcd /usr/spool/lbgm/src
X/usr/lib/news/arcnews [A-Z]* /u/archive
Xcase $? in
X0)	rm [A-Z]*
X	/local/bin/empty save.log;;
Xesac
END_OF_FILE
if test 652 -ne `wc -c <'arccron.SH'`; then
    echo shar: \"'arccron.SH'\" unpacked with wrong size!
fi
# end of 'arccron.SH'
fi
if test -f 'arcauto.SH' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'arcauto.SH'\"
else
echo shar: Extracting \"'arcauto.SH'\" \(697 characters\)
sed "s/^X//" >'arcauto.SH' <<'END_OF_FILE'
X:
X#	arcauto		automatic archive with news forward
X#
X#	usage:	arcauto archive.path notify
X#
X#	where:	archive.path = directory under which news articles
X#			       are to be archived.
X#		      notify = user to notify if arcnews detects an error
X
X#	If an archive error happens, a small message is mailed to "notify"
X
X#	use the -v option to record the names of all files archived.
X#	remove the -v option to record only errors.
X#	Arc.Log is the error/copy log file on stderr.
X#	arcnews is expecting the news article on stdin.
X
XBINdir/arcnews -v "$1" 2>> ARCdir/Arc.Log
Xcase $? in
X0)	exit 0;;
Xesac
X
X(echo 'News archive file creation problem: see LOGdir/Arc.Log') | mail -s "News archive error" $2
Xexit 1
END_OF_FILE
if test 697 -ne `wc -c <'arcauto.SH'`; then
    echo shar: \"'arcauto.SH'\" unpacked with wrong size!
fi
# end of 'arcauto.SH'
fi
echo shar: End of shell archive.
exit 0