rkive patch01 - part 1 of 2
Kent Landfield
kent at ssbell.UUCP
Mon Jul 17 07:50:56 AEST 1989
This is the first of a two (*2*) part patch to rkive, the "Usenet Sources
Archiver". Rkive now supports archiving of non-sources groups better by the
addition of Chronological archiving. It fixes some portability problems
and few irratating "oops". :-)
**** WARNING ******* WARNING ****
The format of the .patchlog changed in this patch. The reason being, the
Patch-To: auxiliary header line was finally "nailed down" and now includes
the package name in it. See the README for additional info about Patch-To:
and the .patchlog.
*Please* assure that you have both parts since they constitute one
(*1*) complete patch.
Credits:
I want to thank the following people for their help with ideas
and supplied bug fixes.
Brandon Allbery - allbery at uunet
Scott Anderson - scott at Questar.MN.ORG
Jonathan Bayer - jbayer at ispi
Mathieu Federspiel - mcf at statware
Axel Fischer - fischer at utower
Steven Grady - grady at ucbvax
Michael R. Johnston - mikej at lilink
Michael Kent - mike at aleytys
Jens Kjerte - jk at dde.dk
Chris Lewis - clewis at eci386
Robert Nelson - robert at sysint
Mark Peek - mark at imagen
Dave Rand - dlr at daver
Bill Randle - billr at saab.CNA.TEK.COM
Wayne Schlitt - wayne at dsndata
Jos Vos - jos at idca.tds.PHILIPS.nl
Andrew Worsley - worsley at ditmela.oz.au
Thanks again!
As always, if you have problems or ideas, I'll
be here... :-)
-Kent+
------------ Cut, snip, rip, tear, slice, dice ... -------------
#! /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 1 (of 2)."
# Contents: Patch1-p2of2
# Wrapped by kent at ssbell on Sun Jul 16 16:20:26 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Patch1-p2of2' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Patch1-p2of2'\"
else
echo shar: Extracting \"'Patch1-p2of2'\" \(44988 characters\)
sed "s/^X//" >'Patch1-p2of2' <<'END_OF_FILE'
X*** O.news_arc.c Fri Jul 14 21:30:34 1989
X--- news_arc.c Sun Jul 16 13:42:51 1989
X***************
X*** 11,19 ****
X ** History:
X ** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X **
X */
X #ifndef lint
X! static char SID[] = "@(#)news_arc.c 1.1 6/1/89";
X #endif
X
X #include <sys/types.h>
X--- 11,34 ----
X ** History:
X ** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X **
X+ ** Patch #1:
X+ ** Added the code to support Chronological archiving in
X+ ** save_article and the new function chronpath. Added
X+ ** more forgiveness for multiple blank lines between the
X+ ** News header and the Auxiliary headers. Added the ifdef
X+ ** NO_MONTH_DIR to decide which way the Chronological archiving
X+ ** is to be done. Copy_article was written to reduce the
X+ ** duplicated code at the end of save_article and do_problem.
X+ ** Split out compression suffix manipulation routines suffix,
X+ ** remove_suffix, and expand_name and check_archive_name for
X+ ** use in other related software. Added DIR_MODE define for
X+ ** the modes of the directories when created. Moved write_patch_log
X+ ** into a new function, copy_article. Reformated the .patchlog
X+ ** to meet the finally stable Patch-To: format. Cleaned up a
X+ ** typo that was duplicated in Problem error messages.
X */
X #ifndef lint
X! static char SID[] = "@(#)news_arc.c 1.4 7/16/89";
X #endif
X
X #include <sys/types.h>
X***************
X*** 20,25 ****
X--- 35,41 ----
X #include <sys/stat.h>
X #include <dirent.h>
X #include <stdio.h>
X+ #include <time.h>
X #include <ctype.h>
X #include "article.h"
X #include "cfg.h"
X***************
X*** 34,39 ****
X--- 50,56 ----
X #define TYPE_PROB 3
X
X int test = 0;
X+ int inum = 0;
X int problem_article;
X
X extern struct group_archive *newsgrp;
X***************
X*** 45,50 ****
X--- 62,69 ----
X char *do_problem();
X char *basename();
X char *suffix();
X+ char *expand_name();
X+ char *copy_article();
X FILE *efopen();
X void exit();
X
X***************
X*** 53,58 ****
X--- 72,78 ----
X {
X char *dp;
X int header_ok = 0;
X+ int last = TEXT;
X FILE *gfp;
X
X init_article();
X***************
X*** 67,77 ****
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--- 87,105 ----
X
X if (!isalpha(*s) || (strchr(s,':') == NULL)) {
X header_ok++;
X! if (header_ok >= 2) {
X! if (*s == '\n' && last == BLANK)
X! continue;
X break;
X+ }
X+ if (*s == '\n')
X+ last = BLANK;
X+ else
X+ last = TEXT;
X continue;
X }
X
X+ last = TEXT;
X dp = s;
X while (*++dp)
X if (*dp == '\n')
X***************
X*** 85,227 ****
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-
X- check_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-
X- struct compress_tab {
X- char *com_name;
X- char *com_suffix;
X- };
X-
X- struct compress_tab cprgs[] = {
X- { "compress", ".Z" },
X- { "pack", ".z" },
X- { NULL, 0 },
X- };
X-
X- char *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-
X- int remove_suffix(path_str)
X- char *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-
X- char *expand_name(filename,ng)
X- char *filename;
X- struct 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
X struct hdrstokeep {
X--- 113,118 ----
X***************
X*** 235,240 ****
X--- 126,132 ----
X { "Subject:", (sizeof "Subject:") },
X { "Message-ID:", (sizeof "Message-ID:") },
X { "Date:", (sizeof "Date:") },
X+ { "Approved:", (sizeof "Approved:") },
X { NULL, 0 },
X };
X
X***************
X*** 380,391 ****
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
X char *save_article (filename,ng)
X char *filename;
X--- 272,315 ----
X
X (void) mkparents(b);
X
X! if ((rc = makedir(b, DIR_MODE, newsgrp->owner, newsgrp->group)) != 0)
X error("makedir failed attempting to make", b);
X
X return(rc);
X }
X
X+ char *copy_article(ng, filename,path)
X+ struct group_archive *ng;
X+ char *filename;
X+ char *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+ /*
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+ ** 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+ /*
X+ ** Return the path to the archived file.
X+ */
X+ return(path);
X+ }
X
X char *save_article (filename,ng)
X char *filename;
X***************
X*** 466,471 ****
X--- 390,412 ----
X */
X (void) sprintf(path,"%s/%s", ng->location, filename);
X break;
X+ case CHRONOLOGICAL:
X+ /*
X+ ** The chronpath() is called to create a path for an article
X+ ** to be stored in chronological ordering. We need to be sure
X+ ** that the issue number is not in use. This is necessary to
X+ ** handle multiple runs of the program on the same day.
X+ **
X+ ** The idea here is to have the program check to see if the
X+ ** issue number to be used is available.
X+ ** There should be no duplicates here ever... :-)
X+ ** [ just don't blow away your .archived file... :-( ]
X+ */
X+ do {
X+ ++inum;
X+ chronpath(ng->location, path, inum);
X+ } while (stat(path ,&sb) == 0);
X+ break;
X default:
X /*
X ** We have got problems....
X***************
X*** 473,489 ****
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--- 414,419 ----
X***************
X*** 532,555 ****
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-
X char *do_problem(type_of_problem, ng, file, path)
X int type_of_problem;
X struct group_archive *ng;
X--- 462,471 ----
X if ((stat(final_path ,&sb) == 0) && !overwrite) /* duplicate found */
X return(do_problem(DUP_PROB,ng, filename, final_path));
X
X! return(copy_article(ng, filename, path));
X }
X
X
X char *do_problem(type_of_problem, ng, file, path)
X int type_of_problem;
X struct group_archive *ng;
X***************
X*** 556,562 ****
X char *file;
X char *path;
X {
X-
X #ifdef MV_ORIGINAL
X char crnt_path[MAXNAMLEN];
X #endif /*MV_ORIGINAL */
X--- 472,477 ----
X***************
X*** 578,590 ****
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--- 493,505 ----
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***************
X*** 652,658 ****
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--- 567,573 ----
X return(NULL);
X }
X
X! set_ownership(path, path, ng);
X
X /* restore the destination path for inbound article */
X (void) strcpy(path,crnt_path);
X***************
X*** 688,759 ****
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-
X write_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 }
X--- 603,713 ----
X /* Make any necessary directories along the way. */
X (void) mkparents(path);
X
X! return(copy_article(ng, file, path));
X! }
X
X
X+ #ifndef NO_MONTH_DIR
X+ static char *month[] = {
X+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
X+ };
X+ #endif /* NO_MONTH_DIR */
X+
X+ chronpath(dirloc, path, seqnum)
X+ char *dirloc;
X+ char *path;
X+ int seqnum;
X+ {
X+ long time();
X+ struct tm *localtime();
X+
X+ long clk;
X+ struct tm *crnt;
X+ static struct tm now;
X+ static int no_time = 1;
X+
X+ if (no_time) {
X+ clk = time((long *)0);
X+ crnt = localtime(&clk);
X+ no_time = 0;
X+ now = *crnt;
X+ }
X+ #ifdef NO_MONTH_DIR
X+ /*
X+ ** Format:
X+ ** /usenet/alt/sources/volume89/890629.01
X+ */
X+ (void) sprintf(path,"%s/%s%d/%.02d%.02d%.02d.%.02d",dirloc,
X+ VOLUME, now.tm_year,
X+ now.tm_year,now.tm_mon+1,now.tm_mday,seqnum);
X+ #else
X /*
X! ** Format:
X! ** /usenet/alt/sources/volume89/Jun/890629.01
X */
X! (void) sprintf(path,"%s/%s%d/%s/%.02d%.02d%.02d.%.02d",dirloc,
X! VOLUME, now.tm_year, month[now.tm_mon],
X! now.tm_year,now.tm_mon+1,now.tm_mday,seqnum);
X! #endif /* NO_MONTH_DIR */
X }
X write_patch_log(ng, path)
X! struct group_archive *ng;
X! char *path;
X {
X! char *sp;
X! FILE *plfp;
X! struct stat sb;
X
X! if (test)
X! return;
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! ** # Patch log for comp.sources.whatever
X! ** #
X! ** # Path To Patch Package Initial
X! ** # Patchfile Volume Issue Name Volume Issue
X! ** #
X! ** volume4/conquer4/Part04 6 86 conquer4 4 42-49
X! ** volume4/conquer4/Part06 6 88 tests3 4 42,47,51
X! ** volume6/conquer4/Part07 6 89 Unknown 6 89
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... Output in the patch log is not intended
X! ** to be centered under these columns... :-) I'm laaaazy.
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", ng->ng_name);
X!
X! (void) fprintf(plfp,"# %-27s%-12s%-15s%-s\n",
X! "Path To", "Patch", "Package", "Initial");
X!
X! (void) fprintf(plfp,"# %-22s%-18s%-10s%-15s\n#\n",
X! "Patchfile", "Volume Issue", "Name",
X! "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,"%-25s %3d %5d %-14s %3d %s\n", sp,
X! article.volume, article.issue, article.package_name,
X! article.patch_volume, article.patch_issue);
X! (void) fclose(plfp);
X! return;
X }
X*** O.patchlevel.h Fri Jul 14 21:29:26 1989
X--- patchlevel.h Sun Jul 16 13:42:52 1989
X***************
X*** 1,4 ****
X /*
X! ** @(#)patchlevel.h 1.1 6/1/89
X */
X! #define PATCHLEVEL 0
X--- 1,4 ----
X /*
X! ** @(#)patchlevel.h 1.2 7/15/89
X */
X! #define PATCHLEVEL 1
X*** O.rkive.1 Fri Jul 14 21:29:31 1989
X--- rkive.1 Sun Jul 16 13:42:56 1989
X***************
X*** 1,4 ****
X! 'br "@(#)rkive.1 1.1 6/1/89"
X .TH RKIVE 1
X .SH NAME
X rkive \- archive USENET source groups
X--- 1,4 ----
X! 'br "@(#)rkive.1 1.2 7/15/89"
X .TH RKIVE 1
X .SH NAME
X rkive \- archive USENET source groups
X***************
X*** 9,15 ****
X .I rkive
X is used to archive the USENET sources groups to an alternate
X location as specified in an rkive configuration file. Archives can
X! be maintained in one of three ways:
X .PP
X .I Archive-Name -
X The moderators of most sources groups assign an official Archive-Name
X--- 9,15 ----
X .I rkive
X is used to archive the USENET sources groups to an alternate
X location as specified in an rkive configuration file. Archives can
X! be maintained in one of four ways:
X .PP
X .I Archive-Name -
X The moderators of most sources groups assign an official Archive-Name
X***************
X*** 32,37 ****
X--- 32,50 ----
X recommended for any site that will be doing massive searches of the
X individual volumes since it keeps the quadratic nature of directory searches
X from making your life miserable.
X+ .PP
X+ .I Chronological -
X+ This type of archiving can be used to store articles that do not have the
X+ auxiliary headers. The articles are stored by the date the article is
X+ archived. The articles are stored in a format of
X+ .PP
X+ newsgroup/volume89/Jun/890627.01 or
X+ newsgroup/volumeYY/MOY/YYMMDD.II
X+ .PP
X+ where YY is the year, MM is the month, DD is the day, and II is the
X+ issue number archived that day. Depending on how the archive administrator
X+ has configured the rkive software, the MOY (month of year) may or may
X+ not be used.
X .PP
X .I Article Number -
X The news software stores the articles locally by naming the news article
X*** O.rkive.5 Fri Jul 14 21:30:02 1989
X--- rkive.5 Sun Jul 16 13:42:58 1989
X***************
X*** 1,4 ****
X! 'br "@(#)rkive.5 1.1 6/1/89"
X .TH RKIVE 5
X .SH NAME
X rkive.cf \- USENET Source Archiver Configuration File.
X--- 1,4 ----
X! 'br "@(#)rkive.5 1.2 7/15/89"
X .TH RKIVE 5
X .SH NAME
X rkive.cf \- USENET Source Archiver Configuration File.
X***************
X*** 65,71 ****
X in which to archive USENET sources,
X .RS
X .IP
X! Volume-Issue, Archive-Name or Article Number.
X .RE
X .IP
X These are used to determine if you wish the articles archived in a
X--- 65,71 ----
X in which to archive USENET sources,
X .RS
X .IP
X! Volume-Issue, Archive-Name, Chronological, or Article Number.
X .RE
X .IP
X These are used to determine if you wish the articles archived in a
X***************
X*** 72,78 ****
X .RS
X .IP
X /basedir/amiga/Volume1/v001i22 or /basedir/amiga/Volume1/sitonit or
X! /basedir/amiga/Volume1/44 format.
X .RE
X .IP "PATCHES ="
X This variable determines the way in which patches are installed into
X--- 72,78 ----
X .RS
X .IP
X /basedir/amiga/Volume1/v001i22 or /basedir/amiga/Volume1/sitonit or
X! /basedir/amiga/Volume89/Jun/890628.01 or /basedir/amiga/Volume1/44 format.
X .RE
X .IP "PATCHES ="
X This variable determines the way in which patches are installed into
X***************
X*** 164,170 ****
X in which to archive USENET sources,
X .RS
X .IP
X! Volume-Issue, Archive-Name or Article Number.
X .RE
X .IP "PATCHES ="
X This variable determines the way in which patches are installed into
X--- 164,170 ----
X in which to archive USENET sources,
X .RS
X .IP
X! Volume-Issue, Archive-Name, Chronological, or Article Number.
X .RE
X .IP "PATCHES ="
X This variable determines the way in which patches are installed into
X*** O.rkive.c Fri Jul 14 21:30:39 1989
X--- rkive.c Sun Jul 16 13:43:01 1989
X***************
X*** 31,38 ****
X ** History:
X ** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X **
X */
X! char sccsid[] = "@(#)rkive.c 1.1 6/1/89";
X
X #include <sys/types.h>
X #include <sys/stat.h>
X--- 31,49 ----
X ** History:
X ** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X **
X+ ** Patch #1:
X+ ** Added a check to verify the file is not zero bytes in length.
X+ ** Added Chronological archiving support. Added check for "test"
X+ ** in logit. In set_ownership, added a "CYA" in the event that
X+ ** compression fails because no saving is produced. Set_ownership
X+ ** attempts the filename without the suffix added when the attempt
X+ ** on the filename with the suffix failed. In this manner, file
X+ ** permissions get set even if the compression routine fails.
X+ ** Also in set_ownership, moved the chmod before the chown/chgrp so
X+ ** that you need not be root to run rkive and give away files.
X+ ** Changed index to mindex so as to make the BSD getopt happy.
X */
X! char sccsid[] = "@(#)rkive.c 1.3 7/16/89";
X
X #include <sys/types.h>
X #include <sys/stat.h>
X***************
X*** 75,80 ****
X--- 86,92 ----
X extern int debug;
X extern int verbose;
X extern int test;
X+ extern int inum;
X extern int problem_article;
X
X main(argc, argv)
X***************
X*** 190,195 ****
X--- 202,209 ----
X }
X #endif
X
X+ inum = 0; /* initialize chronological issue counter */
X+
X /* Remove any existing temporary mail file */
X
X (void) unlink(tmp_mailfile);
X***************
X*** 283,289 ****
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--- 297,313 ----
X
X else if ((sbuf.st_mode & S_IFMT) != S_IFREG)
X continue;
X!
X! /* Check to assure that the file has a size greater that 0 */
X! /* Maybe I'm nuts but I don't see any reason to archive */
X! /* files unless they contain something.. :-) */
X!
X! else if (sbuf.st_size == 0) {
X! (void) fprintf(errfp, "%s/%s is a Zero length file - SKIPPING\n",
X! newsgroup_directory, dp->d_name);
X! continue;
X! }
X!
X /*
X ** If the user has specified that a quick status
X ** listing should be produced then hop to it....
X***************
X*** 311,317 ****
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--- 335,341 ----
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,new_member,newsgrp);
X
X /*
X ** If a problem has been encountered,
X***************
X*** 437,472 ****
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
X! set_ownership(filename,ng)
X! char *filename;
X struct 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
X--- 461,516 ----
X {
X FILE *fp, *fopen();
X
X! if (!test) {
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
X+ /*
X+ ** Set_ownership
X+ **
X+ ** This functions is responsible for setting the owner, group
X+ ** and modes of a file just put into the archive. Two file names
X+ ** are passed to this function. "filename" contains the compression
X+ ** suffix if the file is to be compressed. "flname" is the name of
X+ ** the file without the compression suffix. If the ownership or
X+ ** modes can not be set on the target "filename", this function
X+ ** then tries to set the original file, "flname". In this manner,
X+ ** the file permissions get set even if the compression routine fails.
X+ */
X
X! set_ownership(filename,flname,ng)
X! char *filename; /* filename with compression suffix */
X! char *flname; /* filename without compression suffix */
X struct group_archive *ng;
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! if (!test) { /* change the file modes to the specified modes */
X! if (chmod(filename,ng->modes) != 0) {
X! /* Assume the compress failed and try the original... */
X! if (chmod(flname,ng->modes) != 0)
X! error("Can't change modes of", filename);
X! }
X }
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! /* Assume the compress failed and try the original... */
X! if (chown(flname, ng->owner, ng->group) != 0)
X! error("Can't change ownership of", filename);
X! }
X }
X }
X
X***************
X*** 524,532 ****
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--- 568,576 ----
X logit(ng->index, DEFAULT_INDEX_FORMAT, filename);
X }
X
X! if (*mindex) { /* Is there a global index file ? */
X if (*index_format) /* Yes, Is there a global file format ? */
X! logit(mindex, index_format, filename);
X else /* No, so use the default index format */
X logit(ng->index, DEFAULT_INDEX_FORMAT , filename);
X }
X*** O.rkive.cf Fri Jul 14 21:30:20 1989
X--- rkive.cf Sun Jul 16 13:43:05 1989
X***************
X*** 1,6 ****
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--- 1,6 ----
X #
X #
X! # @(#)rkive.cf 1.2 7/15/89
X #
X # An rkive.cf template.
X # Copy and edit this to reflect the local archive conditions.
X***************
X*** 18,29 ****
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--- 18,31 ----
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 4 possible keys:
X! # Volume-Issue, Archive-Name
X! # Chronological 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/Volume89/890619001 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***************
X*** 92,103 ****
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--- 94,107 ----
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 4 possible keys:
X! # Volume-Issue, Archive-Name
X! # Chronological 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/Volume89/890619001 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***************
X*** 167,173 ****
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--- 171,177 ----
X
X $$comp.sources.mac
X BASEDIR: /usenet/mac
X! TYPE: Chronological
X LOG: /usenet/mac/log
X INDEX: /usenet/mac/index
X INDEX_FORMAT: "%O %a %T"
X***************
X*** 201,207 ****
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--- 205,211 ----
X
X $$alt.sources
X BASEDIR: /usenet/alt/sources
X! TYPE: Chronological
X LOG: /usenet/alt/sources/log
X LOG_FORMAT: "%O %S"
X
X*** O.rkive.h Fri Jul 14 21:30:08 1989
X--- rkive.h Sun Jul 16 13:43:06 1989
X***************
X*** 1,5 ****
X /*
X! ** @(#)rkive.h 1.1 6/1/89
X **
X ** This is the rkive source configuration header file.
X ** Please examine and change to suite your own site's needs..
X--- 1,5 ----
X /*
X! ** @(#)rkive.h 1.3 7/15/89
X **
X ** This is the rkive source configuration header file.
X ** Please examine and change to suite your own site's needs..
X***************
X*** 15,20 ****
X--- 15,21 ----
X #define OWNER 0
X #define GROUP 3
X #define MODES 0444
X+ #define DIR_MODE 0755 /* directory creation modes */
X
X /*
X ** If you have a smart mailer that supports a "-s subject" command
X***************
X*** 113,119 ****
X ** If you wish to have the headers "trimmed" when the file is archived,
X ** assure that REDUCE_HEADERS is defined. Currenlty all header lines that
X ** are not either;
X! ** From:, Newsgroups:, Subject:, Message-ID:, and Date:
X ** will be removed. See news_arc.c if you wish to add or subtract header
X ** lines to keep. This can produce a savings of between 200 to 500 bytes
X ** per archived article.
X--- 114,120 ----
X ** If you wish to have the headers "trimmed" when the file is archived,
X ** assure that REDUCE_HEADERS is defined. Currenlty all header lines that
X ** are not either;
X! ** From:, Newsgroups:, Subject:, Message-ID: Approved:, and Date:
X ** will be removed. See news_arc.c if you wish to add or subtract header
X ** lines to keep. This can produce a savings of between 200 to 500 bytes
X ** per archived article.
X***************
X*** 181,186 ****
X--- 182,189 ----
X #define VOLUME_ISSUE 1 /* Archive as "v16i003" */
X #define ARTICLE_NUMBER 2 /* Archive with same name */
X /* as the file to archive */
X+ #define CHRONOLOGICAL 3 /* Archive as "890619002" */
X+ /* or YYMMDDIII format */
X
X /*
X ** patch handling type defines
X***************
X*** 203,208 ****
X--- 206,212 ----
X /* 0 = Archive-Name */
X /* 1 = Volume-Issue */
X /* 2 = Article-Number */
X+ /* 3 = Chronological */
X int patch_type; /* Method of handling patches. */
X /* 0 = Historical */
X /* 1 = Package */
X*** O.setup.c Fri Jul 14 21:30:25 1989
X--- setup.c Sun Jul 16 13:43:09 1989
X***************
X*** 10,19 ****
X **
X ** History:
X ** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X! **
X */
X #ifndef lint
X! static char SID[] = "@(#)setup.c 1.1 6/1/89";
X #endif
X
X #include <sys/types.h>
X--- 10,26 ----
X **
X ** History:
X ** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
X! **
X! ** Patch #1:
X! ** Added Chronological archiving support. Changed the default
X! ** archive type to Chronological. Corrected error message for
X! ** invalid PATCHES type. Added CHECK_LOGNAME ifdef around getpwnam
X! ** so as to all for remote mail addresses. Changed index to mindex
X! ** so as to make the BSD getopt happy. Corrected pointer check
X! ** in get_users.
X */
X #ifndef lint
X! static char SID[] = "@(#)setup.c 1.3 7/16/89";
X #endif
X
X #include <sys/types.h>
X***************
X*** 33,39 ****
X int default_owner = OWNER;
X int default_group = GROUP;
X int default_modes = MODES;
X! int default_type = ARTICLE_NUMBER;
X int default_patch_type = HISTORICAL;
X
X /*
X--- 40,46 ----
X int default_owner = OWNER;
X int default_group = GROUP;
X int default_modes = MODES;
X! int default_type = CHRONOLOGICAL;
X int default_patch_type = HISTORICAL;
X
X /*
X***************
X*** 65,74 ****
X char log_format[BUFSIZ] = { '\0' };
X
X /*
X! ** index -
X ** The location of the master index.
X */
X! char index[MAXNAMLEN] = { '\0' };
X
X /*
X ** index_format -
X--- 72,81 ----
X char log_format[BUFSIZ] = { '\0' };
X
X /*
X! ** mindex -
X ** The location of the master index.
X */
X! char mindex[MAXNAMLEN] = { '\0' };
X
X /*
X ** index_format -
X***************
X*** 183,189 ****
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--- 190,196 ----
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(mindex, strstrip(sp));
X else
X GAG(buf);
X break;
X***************
X*** 455,467 ****
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--- 462,477 ----
X return_type = ARCHIVE_NAME;
X else if (strcmp(s, "Volume-Issue") == 0)
X return_type = VOLUME_ISSUE;
X+ else if (strcmp(s, "Chronological") == 0)
X+ return_type = CHRONOLOGICAL;
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, %s or %s\n",
X! "Archive-Name", "Volume-Issue",
X! "Chronological", "Article-Number");
X exit(1);
X }
X
X***************
X*** 528,538 ****
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--- 538,548 ----
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 = '\0';
X
X name = tmp_users;
X
X***************
X*** 543,559 ****
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--- 553,570 ----
X *(list-1) = '\0';
X }
X
X+ #ifdef CHECK_LOGNAME
X /* check if user is found in passwd file */
X! if ((pwent = getpwnam(name)) == NULL)
X! error("Invalid user:",name);
X! #endif /* CHECK_LOGNAME */
X!
X! if (*users != '\0') {
X! (void) strcat(users, ",");
X! (void) strcat(users, name);
X }
X else
X! (void) strcpy(users, name);
X name = list;
X }
X return(users);
X***************
X*** 596,602 ****
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--- 607,613 ----
X else {
X (void) fprintf(errfp,"%s: %s: %s %s\n", progname,
X ngname, "Invalid Patches Type:", s);
X! (void) fprintf(errfp,"\tPATCHES Must be %s, or %s\n",
X "Historical", "Package");
X exit(1);
X }
X*** O.suffix.c Sat Jul 15 00:40:12 1989
X--- suffix.c Sun Jul 16 13:43:11 1989
X***************
X*** 0 ****
X--- 1,95 ----
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: Thu Jun 22 18:44:35 CST 1989 due to necessity.
X+ **
X+ */
X+ #ifndef lint
X+ static char SID[] = "@(#)suffix.c 1.1 7/15/89";
X+ #endif
X+
X+ #include <stdio.h>
X+ #include <sys/types.h>
X+ #include <dirent.h>
X+ #include "rkive.h"
X+ #include "suffix.h"
X+
X+ char *basename();
X+ extern char compress[];
X+
X+ char *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+
X+ int remove_suffix(path_str)
X+ char *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+
X+ char *expand_name(filename,ng)
X+ char *filename;
X+ struct group_archive *ng;
X+ {
X+ char *comp_cmd;
X+ char *strcpy();
X+ char *strcat();
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*** O.suffix.h Sat Jul 15 00:40:17 1989
X--- suffix.h Sun Jul 16 13:43:12 1989
X***************
X*** 0 ****
X--- 1,22 ----
X+ /*
X+ ** @(#)suffix.h 1.1 7/15/89
X+ **
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 BELOW......
X+ */
X+
X+ struct compress_tab {
X+ char *com_name;
X+ char *com_suffix;
X+ };
X+
X+ struct compress_tab cprgs[] = {
X+ { "compress", ".Z" },
X+ { "pack", ".z" },
X+ { NULL, 0 },
X+ };
X+
END_OF_FILE
if test 44988 -ne `wc -c <'Patch1-p2of2'`; then
echo shar: \"'Patch1-p2of2'\" unpacked with wrong size!
fi
# end of 'Patch1-p2of2'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked both 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
More information about the Comp.sources.bugs
mailing list