kent@ssbell.UUCP (Kent Landfield) (07/17/89)
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@uunet Scott Anderson - scott@Questar.MN.ORG Jonathan Bayer - jbayer@ispi Mathieu Federspiel - mcf@statware Axel Fischer - fischer@utower Steven Grady - grady@ucbvax Michael R. Johnston - mikej@lilink Michael Kent - mike@aleytys Jens Kjerte - jk@dde.dk Chris Lewis - clewis@eci386 Robert Nelson - robert@sysint Mark Peek - mark@imagen Dave Rand - dlr@daver Bill Randle - billr@saab.CNA.TEK.COM Wayne Schlitt - wayne@dsndata Jos Vos - jos@idca.tds.PHILIPS.nl Andrew Worsley - worsley@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@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