grwalter@watfun.waterloo.edu (Fred Walter) (04/04/91)
Submitted-by: Fred Walter <grwalter@watfun.waterloo.edu> Posting-number: Volume 17, Issue 80 Archive-name: newsbreak/part01 Supersedes: newsbreak1.11: Volume 15, Issue 107 Here is the latest version of my program to automatically take postings to comp.{sources|binaries}.* and put them together, unshar them and uudecode them. The change from version 1.11 to 1.12 is that newsbreak has been ported to Xenix386. The change from version 1.10 to 1.11 is that special case handling for postings to comp.binaries.ibm.pc was added. (These aren't shar files.) fred -----cut here-or-use-"newsbreak -a"----------------------------------------- #! /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: newsbreak.c # Wrapped by grwalter@watfun on Wed Apr 3 07:52:37 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'newsbreak.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'newsbreak.c'\" else echo shar: Extracting \"'newsbreak.c'\" \(17991 characters\) sed "s/^X//" >'newsbreak.c' <<'END_OF_FILE' X#define VERSION "newsbreak 1.12 by G. R. Walter" X X/* X * newsbreak.c X * X * by G. R. Walter (Fred) December 30, 1988 X * X * USAGE: X * newsbreak [-a] X * Default action is to only process files that contain the line X * "Archive-name:" - this is used to create the subdirectory that X * the shar files are unshared/etc into. X * X * If -a is used then all files will be processed. This is needed X * for postings to groups such as alt.sources, or when dealing with X * entries at some archive sites (the program is stored as lots X * of shar files). X * X * DESCRIPTION: X * Takes a series of files which are shar files (strips any X * garbage at the start of the shar file) that have been posted to X * comp.{sources|binaries}.* and feeds them through sh. X * After they have been fed through sh the original files are X * deleted. Then any uuencoded files are uudecoded, after which X * the uuencoded files are also deleted. Special care is taken X * with files posted to comp.binaries.ibm.pc which use a non-standard X * format. X * X * TO COMPILE: X * Under BSD 4.3 X * cc newsbreak.c -o newsbreak X * Under System V X * cc -DSYSTEM_V newsbreak.c -lbsd -o newsbreak X * Under Xenix X * cc -DXENIX newsbreak.c -lx -o newsbreak X * X * NOTES: X * 1) This program assumes that all necessary shar files are in the X * current directory. It attempts to not delete stuff if it can't X * handle it (like when not all the parts of a multi-part uuencoded X * file are available). X * 2) When there are multiple parts to a uuencoded file, a maximum X * of 99 parts are currently handled. X * X * HISTORY: X * 1.00 - original version X * 1.01 - small enhancements/bug fixes X * 1.02 - now handle .zu's with > 9 parts correctly X * 1.03 - now check for ":\tshar\:Shell Archiver" when checking if a file X * is a shar archive X * - now handles files ending in .uu# X * 1.04 - now check for ": run sh on this file to unbundle" when checking X * if a file is a shar archive X * 1.05 - now check for "# This is a shell archive." when checking X * if a file is a shar archive X * - now prints out the version (and author name) when run X * 1.06 - now check for "Archive-name:" to see what directory the X * resulting files should be put in. NOTE: any parts after X * a "part*" section in the path are not mkdir'ed X * - now handles files ending in .zuu# X * - now handles files ending in .uu# properly X * - now doesn't attempt to process files starting in "." X * - now prints some useful info (so you know what it is doing) X * - now check for "# After you unpack everything" when checking X * if a file is a shar archive X * - make sure I don't try to uudecode directories X * - recursively descend directories when uudecoding X * 1.07 - added ifdef's around code needed so this compiles under System V X * - changes by ames!uts.amdahl.com!dwl10@mailrus (Dave Lowrey) X * 1.08 - now check for ": This is a shar archive." when checking X * if a file is a shar archive X * - now check for "# This is the first line of a \"shell archive\"" X * when checking if a file is a shar archive X * - build up a list of files in the current directory before unshar'ing X * - scan these files to see which ones should be unshar'ed and try X * to determine the best ordering for unshar'ing (using the secondary X * header "Archive-name:" if it exists, otherwise using file name) X * - print what directory is being searched for uuencoded files X * - print what is being uudecoded X * 1.09 - added code to force creation of all necessary subdirectories X * - based on code supplied by michel@etl.go.jp (Michel Pasquier) X * 1.10 - now check for "# type sh " when checking X * if a file is a shar archive X * - now check for "# To recover, type \"sh archive\"" when checking X * if a file is a shar archive X * - now handle .puu# X * - effectively do a caseless strncmp() on "part" in unshar() so that X * things like "Archive-name: nethack3p9/Part01" are properly handled X * 1.11 - added special case handling for postings to comp.binaries.ibm.pc X * (these aren't shar files) X * 1.12 - ported to XENIX X */ X X#include <stdio.h> X#include <ctype.h> X#include <sys/types.h> X#include <sys/file.h> X#include <sys/stat.h> X#ifdef XENIX X# include <sys/ndir.h> X# include <sys/dirent.h> X# include <errno.h> Xchar *getcwd(); X#endif X#ifdef SYSTEM_V X# include <sys/dir.h> X# include <dirent.h> Xchar *getcwd(); X#endif X#ifndef XENIX X# ifndef SYSTEM_V X# define OTHER X# include <sys/dir.h> Xchar *getwd(); X# endif X#endif X X#ifdef XENIX X# define F_OK (0) X#endif X Xchar *malloc(); Xchar *strcpy(); Xchar *sprintf(); X Xtypedef struct { X char *filename; X char *archivename; X} Name; X Xchar ArchiveName[200]; X X#define AN_ARCHIVE(BUF) \ X( \ X (!strncmp(BUF, "#!/bin/sh", 9)) \ X|| (!strncmp(BUF, "#! /bin/sh", 10)) \ X|| (!strncmp(BUF, "# This is a shell archive.", 26)) \ X|| (!strncmp(BUF, ": This is a shar archive.", 25)) \ X|| (!strncmp(BUF, ":\tshar:\tShell Archiver", 22)) \ X|| (!strncmp(BUF, ": run sh on this file to unbundle", 33)) \ X|| (!strncmp(BUF, "# After you unpack everything", 29)) \ X|| (!strncmp(BUF, "# This is the first line of a \"shell archive\"", 45)) \ X|| (!strncmp(BUF, "# type sh ", 13)) \ X|| (!strncmp(BUF, "# To recover, type \"sh archive\"", 30)) \ X) X X#define COMBINE "~~" X Xmain(argc, argv) X int argc; X char **argv; X{ X#ifdef SYSTEM_V X struct dirent **dp; X#else X struct direct **dp; X#endif X struct stat stat_buf; X int size; X int i; X int j; X Name *array; X int all_flag = 0; X X int Select(); X int alphasort(); X int scandir(); X int compare(); X void unshar(); X void uudecode(); X X fprintf(stderr, "%s\n", VERSION); X X if (argc > 1) X if (!strcmp(argv[1], "-a")) X all_flag = -1; X X /* X * Count the sharfiles in the current directory. If there are any, put X * the filenames and archive-names (if any) into an array and sort it. X * Then unshar. (This code assumes that the current directory contents X * don't change underneath you.) X */ X size = scandir(".", &dp, Select, alphasort); X if (size > 0) { X array = (Name *) malloc((unsigned) (sizeof(Name) * size)); X for (i = 0, j = 0; i < size; i++) { X if (stat(dp[i]->d_name, &stat_buf)) /* can't stat !?!?!? */ X continue; X X if ((stat_buf.st_mode & S_IFDIR)) /* a directory */ X continue; X X if (has_an_archive_name(dp[i]->d_name) || all_flag) { X array[j].filename = X malloc((unsigned) (strlen(dp[i]->d_name) + 1)); X strcpy(array[j].filename, dp[i]->d_name); X array[j].archivename = X malloc((unsigned) (strlen(ArchiveName) + 1)); X strcpy(array[j].archivename, ArchiveName); X j++; X } X } X size = j; X if (size > 0) { X fprintf(stderr, "\nNow performing the unshar pass.\n"); X X qsort((char *) array, size, (int) sizeof(Name), compare); X X /* now unshar everything */ X for (i = 0; i < size; i++) X unshar(array[i].filename, array[i].archivename); X } X fprintf(stderr, "\nNow performing the uudecode pass.\n"); X X uudecode(".", 0); X } X /* X * In theory I should free all allocated memory, but it will be free'd X * upon exitting. X */ X exit(0); X} X X/* X * create_subpath - recursively create subpath X */ X Xvoid Xcreate_subpath(dir, subpath) X char *dir; X char *subpath; X{ X char *p; X char *newdir; X X for (p = subpath; *p != '\0' && *p != '/'; p++); X X if (*p == '/') { /* was a sub-directory and not a filename */ X *p++ = '\0'; X newdir = malloc((unsigned) (strlen(dir) + 1 + strlen(subpath) + 1)); X sprintf(newdir, "%s/%s", dir, subpath); X /* X * If it doesn't exist then create it. X */ X if (access(newdir, F_OK) < 0) { X if (mkdir(newdir, 0777) < 0) { X fprintf(stderr, "Couldn't mkdir %s\n", newdir); X return; X } X } X create_subpath(newdir, p); X free(newdir); X } X} X X/* X * ensure_existance_of_subdirs - ensure existance of necessary sub-directories X * X * Search for destination file or path and extract its full name X * then create necessary sub-directories. X */ X X#define NOT_END(P) \ X(*P != ' ' && *P != '\'' && *P != '\"' && *P != '\n' && *P != '&' && *P != '\0') X Xvoid Xensure_existance_of_subdirs(p, dir) X char *p; X char *dir; X{ X char *subdirs; X X for (; *p != '>' && *p != '\0'; p++); /* Get to start of path. */ X if (*p == '\0') X return; X for (p++; (*p == ' ' || *p == '\'' || *p == '\"') && *p != '\0'; p++); X if (*p == '\0') X return; X X subdirs = p; /* Get to end of path. */ X for (; NOT_END(p); p++); X *p = '\0'; X X create_subpath(dir, subdirs); X} X Xvoid Xunshar(name, archivename) X char *name; X char *archivename; X{ X FILE *fin; X FILE *fout; X char buf[200]; X char dir[200]; X char *part = NULL; X char *p; X char tmp[4]; X int i; X X fprintf(stderr, "Attempting to unshar %s\n", name); X fin = fopen(name, "r"); X if (fin == (FILE *) NULL) /* file doesn't exist !? */ X return; X X strcpy(dir, "."); /* setup directory to use */ X if (archivename[0] != '\0') { X strcpy(dir, archivename); X for (p = dir; *p != '\0'; p++) { X if (*p == '/') { X *p = '\0'; X for (i = 0; i < 4; i++) { X if (isascii(p[i + 1]) && isupper(p[i + 1])) X tmp[i] = tolower(p[i + 1]); X else X tmp[i] = p[i + 1]; X if (tmp[i] == '\0') X break; X } X if (!strncmp(tmp, "part", 4)) { X part = p + 1; X break; X } X if (access(dir, F_OK) < 0) X if (mkdir(dir, 0777) < 0) X goto ABORT_ATTEMPT; X *p = '/'; X } X } X if (access(dir, F_OK) < 0) { X if (mkdir(dir, 0777) < 0) { X ABORT_ATTEMPT: X fprintf(stderr, "Couldn't mkdir %s\n", dir); X fprintf(stderr, "Aborting this attempt\n"); X fclose(fin); X return; X } X } X } X fprintf(stderr, "unsharing into directory \"%s\"\n", dir); X X for (;;) { X if (fgets(buf, 200, fin) == NULL) { /* not a shar file !? */ X fclose(fin); X return; X } X if (!strncmp(buf, "Newsgroups: comp.binaries.ibm.pc", 32)) { X if (part != NULL) { X fclose(fin); X sprintf(buf, "mv %s %s/%s%s", name, dir, part, COMBINE); X (void) system(buf); X return; X } X } X if (AN_ARCHIVE(buf)) X break; X } X X sprintf(buf, "%s/.unshar.temp.file", dir); X fout = fopen(buf, "w"); X while (fgets(buf, 200, fin) != NULL) { X fprintf(fout, "%s", buf); X /* X * For each source archived ensure existance of necessary X * sub-directories. X */ X if (!strncmp(buf, "sed", 3) || !strncmp(buf, "cat", 3)) X ensure_existance_of_subdirs(buf, dir); X } X fclose(fout); X fclose(fin); X X sprintf(buf, "cd %s; sh .unshar.temp.file", dir); X if (system(buf) == 0) { X (void) unlink(name); X } else { X fprintf(stderr, "exit status non-zero, not deleting %s\n", name); X } X X sprintf(buf, "rm %s/.unshar.temp.file", dir); X (void) system(buf); X} X Xvoid Xuudecode(name, level) X char *name; X int level; X{ X#ifdef SYSTEM_V X struct dirent **dp; X#else X struct direct **dp; X#endif X FILE *file; X char buf[200]; X char name_buf[200]; X char path[200]; X char *p; X struct stat stat_buf; X char digit; X int i; X int size; X X int scandir(); X int Select(); X int alphasort(); X X if (stat(name, &stat_buf)) /* can't stat !?!?!?! */ X return; X X if ((stat_buf.st_mode & S_IFDIR)) { X /* uudecode everything in this directory */ X#ifdef OTHER X if (!getwd(path)) X return; X#else X if (!getcwd(path, 200)) X return; X#endif X size = scandir(name, &dp, Select, alphasort); X if (size <= 0) X return; X X if (chdir(name)) X return; X X level++; X if (level == 1) X fprintf(stderr, "uudecoding in directory \"%s\"\n", path); X else X fprintf(stderr, "uudecoding in directory \"%s/%s\"\n", path, name); X for (i = 0; i < size; i++) X uudecode(dp[i]->d_name, level); X (void) chdir(path); X if (level > 1) X fprintf(stderr, "uudecoding in directory \"%s\"\n", path); X return; X } X /* X * First combine any files that end in COMBINE. X */ X p = name + strlen(name) - strlen(COMBINE); X if (p >= name) { X if (!strcmp(p, COMBINE)) { X sprintf(buf, "cat *%s | sed '/^END/,/^BEGIN/d'| uudecode", COMBINE); X if (system(buf) == 0) { X sprintf(buf, "rm *%s", COMBINE); X (void) system(buf); X } X return; X } X } X /* X * If the file ends in ".uue" or ".zuu" ".puu" or ".uu" just uudecode it. X * Handle ".zuu#", ".puu#", ".pu#", ".zu#" and ".uu#" where # is a X * number. X */ X p = name + strlen(name) - 4; X if (((strcmp(p, ".uue") && strcmp(p, ".zuu") && X strcmp(p, ".puu") && strcmp(p + 1, ".uu")))) { X p += 3; X while (isdigit(*p)) X p--; X X digit = p[1]; X p[1] = '\0'; X p -= 2; X if (!strcmp(p, ".uu") || !strcmp(p, ".zu") || !strcmp(p, ".pu")) { X if (digit == '0') { X sprintf(buf, "cat %s* | uudecode", name); X } else { X sprintf(name_buf, "%s10", name); X file = fopen(name_buf, "r"); X if (file == (FILE *) NULL) { X sprintf(buf, "cat %s? | uudecode", name); X } else { X fclose(file); X sprintf(buf, "cat %s? %s?? | uudecode", name, name); X } X } X } else if (strcmp(p - 1, ".zuu") && strcmp(p - 1, ".puu")) { X return; X } X } X sprintf(buf, "cat %s* | uudecode", name); X fprintf(stderr, "%s\n", buf); X if (system(buf) == 0) { X sprintf(buf, "rm %s*", name); X (void) system(buf); X } else { X fprintf(stderr, "exit status non-zero, not deleting file(s)\n"); X } X} X Xint Xcompare(element1, element2) X Name *element1; X Name *element2; X{ X int result; X X result = strcmp(element1->archivename, element2->archivename); X if (result == 0) X result = strcmp(element1->filename, element2->filename); X X return (result); X} X X/* X * has_an_archive_name - return -1 if has an archive name, 0 otherwise. X * - as well, set the global variable ArchiveName X */ X Xint Xhas_an_archive_name(name) X char *name; X{ X FILE *fin; X char buf[200]; X X ArchiveName[0] = '\0'; X X fin = fopen(name, "r"); X if (fin == (FILE *) NULL) /* file doesn't exist !? */ X return (0); X X for (;;) { X if (fgets(buf, 200, fin) == NULL) { X break; X } else if (strncmp(buf, "Archive-name:", 13) == 0) { X sscanf(buf, "Archive-name: %s", ArchiveName); X fclose(fin); X return (-1); X } X } X fclose(fin); X return (0); X} X Xint XSelect(dp) X#ifdef SYSTEM_V X struct dirent *dp; X#else X struct direct *dp; X#endif X{ X if (dp->d_name[0] != '.') X return (-1); X else X return (0); X} X X#ifndef OTHER X/* X * alphasort and scandir by rsalz. X */ X X/* Initial guess at directory size. */ X#define INITIAL_SIZE 30 X X/* A convenient shorthand. */ Xtypedef struct direct ENTRY; X X/* Linked in later. */ Xextern char *malloc(); Xextern char *realloc(); Xextern char *strcpy(); X Xint Xalphasort(d1, d2) X ENTRY **d1; X ENTRY **d2; X{ X return(strcmp(d1[0]->d_name, d2[0]->d_name)); X} X Xint Xscandir(name, list, selector, sorter) X char *name; X ENTRY ***list; X int (*selector)(); X int (*sorter)(); X{ X ENTRY **names; X ENTRY *E; X DIR *Dp; X int i; X int size; X X /* Get initial list space and open directory. */ X size = INITIAL_SIZE; X names = (ENTRY **)malloc(size * sizeof names[0]); X if (names == (ENTRY **) NULL) X return(-1); X Dp = opendir(name); X if (Dp == (DIR *) NULL) X return(-1); X X /* Read entries in the directory. */ X for (i = 0; E = readdir(Dp); ) X if (selector == 0 || (*selector)(E)) { X /* User wants them all, or he wants this one. */ X if (++i >= size) { X size <<= 1; X names = (ENTRY **)realloc((char *)names, size * sizeof names[0]); X if (names == (ENTRY **) NULL) { X closedir(Dp); X return(-1); X } X } X X /* Copy the entry. */ X names[i - 1] = (ENTRY *)malloc(DIRSIZ(E)); X if (names[i - 1] == (ENTRY *) NULL) { X closedir(Dp); X return(-1); X } X names[i - 1]->d_ino = E->d_ino; X names[i - 1]->d_reclen = E->d_reclen; X names[i - 1]->d_namlen = E->d_namlen; X (void)strcpy(names[i - 1]->d_name, E->d_name); X } X X /* Close things off. */ X names[i] = (ENTRY *)NULL; X *list = names; X closedir(Dp); X X /* Sort? */ X if (i && sorter) X qsort((char *)names, i, sizeof names[0], sorter); X X return(i); X} X#endif X X#ifdef XENIX X/* X * 4.2BSD mkdir simulation from cnews X */ X X/* system call returns */ X#define SYS_OK 0 X#define SYS_ERR (-1) X X#define UMASK_MASK 0777 X X#define STRLEN(s) (sizeof (s) - 1) /* s must be a char array */ X Xint Xmkdir(dir, mode) Xchar *dir; Xint mode; X{ X char *cbuf = malloc((unsigned)STRLEN("mkdir ") + strlen(dir) + 1); X register int oldmask, ret; X X if (cbuf == NULL) { X errno = ENOMEM; /* kludge */ X return SYS_ERR; X } X oldmask = umask(0); X (void) umask(~(mode & ~oldmask) & UMASK_MASK); X X (void) sprintf(cbuf, "mkdir %s", dir); X ret = (system(cbuf) != 0? SYS_ERR: SYS_OK); X if (ret == SYS_ERR) X errno = EINVAL; /* kludge */ X X (void) umask(oldmask); X free(cbuf); X return ret; X} X#endif END_OF_FILE if test 17991 -ne `wc -c <'newsbreak.c'`; then echo shar: \"'newsbreak.c'\" unpacked with wrong size! fi # end of 'newsbreak.c' fi echo shar: End of shell archive. exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.