[comp.sources.misc] v17i080: newsbreak - unpack source and binary group postings, Part01/01

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.