Beyond shar (Re: shars and security concerns.)

Peter da Silva peter at ficc.uu.net
Thu May 3 02:56:25 AEST 1990


I also agree that shars are getting out of hand. "shar" was a good idea for
its time, but it's gotten too big, too fast. I think it's long past the
time for a standard text archive format on Usenet.

I vote for the Software Tools format:

-h- filename date true_path_name
file
-h- nextfilename date true_path_name
...

Sometimes you see things like this:
-h- filename date true_path_name
file
-t- filename date true_path_name

I think there's room to turn this into something we can all live with.
Adding the file size, and prefixing all lines with some character, should
make things a bit safer.

Martin Minow did an implementation of this. It's kind of bare bones, but
it should work as a starting place. I'm appending it to this message.

In shar format. :->

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of shell archive."
# Contents:  archc.c archx.c readme.txt
# Wrapped by peter at ficc.uu.net on Wed May  2 11:49:01 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'archc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'archc.c'\"
else
echo shar: Extracting \"'archc.c'\" \(7785 characters\)
sed "s/^X//" >'archc.c' <<'END_OF_FILE'
X/*
X *			A R C H I V E
X *
X * Create an archive
X *
X */
X
X/*)BUILD	$(TKBOPTIONS) = {
X			TASK	= ...ARC
X		}
X*/
X
X#ifdef	DOCUMENTATION
X
Xtitle	archc	text file archive creation
Xindex		text file archive creation
X
Xsynopsis
X
X	archc file[s] >archive
X
Xdescription
X
X	Archc manages archives (libraries) of source files, allowing
X	a large number of small files to be stored without using
X	excessive system resources.  It copies the set of named
X	files to standard output in archive format.
X
X	The archx program will recreate the files from an archive.
X
X	Note: there are no checks against the same file appearing
X	twice in an archive.
X
Xarchive file format
X
X	Archive files are standard text files.  Each archive element is
X	preceeded by a line of the format:
X	.s.nf
X	-h-	file.name	date	true_path_name
X	.s.f
X	Note that there is no line or byte count.  To prevent problems,
X	a '-' at the beginning of a record within a user file or embedded
X	archive will be "quoted" by doubling it.  The date and true filename
X	fields are ignored.  On Dec operating systems, file.name is
X	forced to lowercase.  Certain bytes at the beginning of a record are
X	also prefixed by '-' to prevent mailers from treating them
X	as commands.
X
Xdiagnostics
X
X	Diagnostic messages should be self-explanatory
X
Xauthor
X
X	Martin Minow
X
X#endif
X
X#include	<stdio.h>
X#include	<ctype.h>
X
X#define unix
X#undef vms
X
X#ifdef vms
X#include		<ssdef.h>
X#include		<stsdef.h>
X#define	IO_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
X#define	IO_ERROR	SS$_ABORT
X#endif
X/*
X * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
X */
X#ifndef	IO_SUCCESS
X#define	IO_SUCCESS	0
X#endif
X#ifndef	IO_ERROR
X#define	IO_ERROR	1
X#endif
X#define EOS		0
X#define	FALSE		0
X#define	TRUE		1
X
Xchar		text[513];		/* Working text			*/
Xchar		name[81];		/* Current archive member name	*/
Xchar		pathname[81];		/* Output for argetname()	*/
Xchar		*timetext;		/* Time of day text		*/
Xint		verbose		= TRUE; /* TRUE for verbosity		*/
XFILE		*infd;			/* Input file			*/
X
Xmain(argc, argv)
Xint		argc;			/* Arg count			*/
Xchar		*argv[];		/* Arg vector			*/
X{
X	register int		i;	/* Random counter		*/
X	register char		*fn;	/* File name pointer		*/
X	register char		*argp;	/* Arg pointer			*/
X	int			nfiles;
X	extern char		*ctime();
X	extern long		time();
X	long			timval;
X
X	time(&timval);
X	timetext = ctime(&timval);
X	timetext[24] = EOS;
X#ifdef vms
X	argc = getredirection(argc, argv);
X#endif
X	if (argc <= 1)
X	    fprintf(stderr, "No files to archive?\n");
X#ifdef	unix
X	for (i = 1; i < argc; i++) {
X	    if ((infd = fopen(argv[i], "r")) == NULL)
X		perror(argv[i]);
X	    else {
X		strcpy(pathname, argv[i]);
X		import();
X		fclose(infd);
X	    }
X	}
X#else
X	/*
X	 * Decus C supports fwild/fnext for explicit processing
X	 * of wild-card filenames.
X	 */
X	for (i = 1; i < argc; i++) {
X	    if ((infd = fwild(argv[i], "r")) == NULL)
X		perror(argv[i]);
X	    else {
X		for (nfiles = 0; fnext(infd) != NULL; nfiles++) {
X		    fgetname(infd, pathname);
X		    import();
X		}
X		fclose(infd);
X		if (nfiles == 0)
X		    fprintf(stderr, "No files match \"%s\"\n", argv[i]);
X	    }
X	}
X#endif
X}
X
Ximport()
X/*
X * Add the file open on infd (with file name in pathname) to
X * the archive.
X */
X{
X	unsigned int	nrecords;
X
X	fixname();
X	nrecords = 0;
X	printf("-h- %s\t%s\t%s\n", name, timetext, pathname);
X	while (fgets(text, sizeof text, infd) != NULL) {
X	    switch (text[0]) {
X	    case '-':
X	    case '.':
X	    case '~':
X		putchar('-');				/* Quote	*/
X	    }
X	    fputs(text, stdout);
X	    nrecords++;
X	}
X	if (ferror(infd)) {
X	    perror(name);
X	    fprintf(stderr, "Error when importing a file\n");
X	}
X	if (verbose) {
X	    fprintf(stderr, "%u records read from %s\n",
X		nrecords, pathname);
X	}
X}
X
Xfixname()
X/*
X * Get file name (in pathname), stripping off device:[directory]
X * and ;version.  The archive name ("file.ext") is written to name[].
X * On a dec operating system, name is forced to lowercase.
X */
X{
X	register char	*tp;
X	register char	*ip;
X	char		bracket;
X	extern char	*strrchr();
X
X#ifdef	unix
X	/*
X	 * name is after all directory information
X	 */
X	if ((tp = strrchr(pathname, '/')) != NULL)
X	    tp++;
X	else
X	    tp = pathname;
X	strcpy(name, tp);
X#else
X	strcpy(name, pathname);
X	if ((tp = strrchr(name, ';')) != NULL)
X		*tp = EOS;
X	while ((tp = strchr(name, ':')) != NULL)
X		strcpy(name, tp + 1);
X	switch (name[0]) {
X	case '[':	bracket = ']';
X			break;
X	case '<':	bracket = '>';
X			break;
X	case '(':	bracket = ')';
X			break;
X	default:	bracket = EOS;
X			break;
X	}
X	if (bracket != EOS) {
X	    if ((tp = strchr(name, bracket)) == NULL) {
X		fprintf(stderr, "? Illegal file name \"%s\"\n",
X		    pathname);
X	    }
X	    else {
X		strcpy(name, tp + 1);
X	    }
X	}
X	for (tp = name; *tp != EOS; tp++) {
X	    if (isupper(*tp))
X		*tp = tolower(*tp);
X	}
X#endif
X}
X
X#ifdef	unix
Xchar *
Xstrrchr(stng, chr)
Xregister char	*stng;
Xregister char	chr;
X/*
X * Return rightmost instance of chr in stng.
X * This has the wrong name on some Unix systems.
X */
X{
X	register char	*result;
X
X	result = NULL;
X
X	do {
X	    if (*stng == chr)
X		result = stng;
X	} while (*stng++ != EOS);
X	return (result);
X}
X#endif
X
X/*
X * getredirection() is intended to aid in porting C programs
X * to VMS (Vax-11 C) which does not support '>' and '<'
X * I/O redirection.  With suitable modification, it may
X * useful for other portability problems as well.
X */
X
Xstatic int
Xgetredirection(argc, argv)
Xint		argc;
Xchar		**argv;
X/*
X * Process vms redirection arg's.  Exit if any error is seen.
X * If getredirection() processes an argument, it is erased
X * from the vector.  getredirection() returns a new argc value.
X *
X * Warning: do not try to simplify the code for vms.  The code
X * presupposes that getredirection() is called before any data is
X * read from stdin or written to stdout.
X *
X * Normal usage is as follows:
X *
X *	main(argc, argv)
X *	int		argc;
X *	char		*argv[];
X *	{
X *		argc = getredirection(argc, argv);
X *	}
X */
X{
X#ifdef	vms
X	register char		*ap;	/* Argument pointer	*/
X	int			i;	/* argv[] index		*/
X	int			j;	/* Output index		*/
X	int			file;	/* File_descriptor 	*/
X
X	for (j = i = 1; i < argc; i++) {   /* Do all arguments	*/
X	    switch (*(ap = argv[i])) {
X	    case '<':			/* <file		*/
X		if (freopen(++ap, "r", stdin) == NULL) {
X		    perror(ap);		/* Can't find file	*/
X		    exit(IO_ERROR);	/* Is a fatal error	*/
X		}
X
X	    case '>':			/* >file or >>file	*/
X		if (*++ap == '>') {	/* >>file		*/
X		    /*
X		     * If the file exists, and is writable by us,
X		     * call freopen to append to the file (using the
X		     * file's current attributes).  Otherwise, create
X		     * a new file with "vanilla" attributes as if
X		     * the argument was given as ">filename".
X		     * access(name, 2) is TRUE if we can write on
X		     * the specified file.
X		     */
X		    if (access(++ap, 2) == 0) {
X			if (freopen(ap, "a", stdout) != NULL)
X			    break;	/* Exit case statement	*/
X			perror(ap);	/* Error, can't append	*/
X			exit(IO_ERROR);	/* After access test	*/
X		    }			/* If file accessable	*/
X		}
X		/*
X		 * On vms, we want to create the file using "standard"
X		 * record attributes.  create(...) creates the file
X		 * using the caller's default protection mask and
X		 * "variable length, implied carriage return"
X		 * attributes. dup2() associates the file with stdout.
X		 */
X		if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
X		 || dup2(file, fileno(stdout)) == -1) {
X		    perror(ap);		/* Can't create file	*/
X		    exit(IO_ERROR);	/* is a fatal error	*/
X		}			/* If '>' creation	*/
X		break;			/* Exit case test	*/
X
X	    default:
X		argv[j++] = ap;		/* Not a redirector	*/
X		break;			/* Exit case test	*/
X	    }
X	}				/* For all arguments	*/
X	return (j);
X#else
X	/*
X	 * Note: argv[] is referenced to fool the Decus C
X	 * syntax analyser, supressing an unneeded warning
X	 * message.
X	 */
X	return (argv[0], argc);		/* Just return as seen	*/
X#endif
X}
X
X
X
END_OF_FILE
if test 7785 -ne `wc -c <'archc.c'`; then
    echo shar: \"'archc.c'\" unpacked with wrong size!
fi
# end of 'archc.c'
fi
if test -f 'archx.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'archx.c'\"
else
echo shar: Extracting \"'archx.c'\" \(7837 characters\)
sed "s/^X//" >'archx.c' <<'END_OF_FILE'
X/*
X *			A R C H X
X *
X * Archive extraction
X *
X */
X
X/*
X * Note: the )BUILD comment is extracted by a Decus C tool to construct
X * system-dependent compiler command lines.
X *
X * Text inside #ifdef DOCUMENTATION is converted to runoff by a
X * Decus C tool.
X */
X
X/*)BUILD	$(TKBOPTIONS) = {
X			TASK	= ...ARX
X		}
X*/
X
X#ifdef	DOCUMENTATION
X
Xtitle	archx	text file archiver extraction
Xindex		text file archiver extraction
X
Xsynopsis
X
X	archx archive_files
X
Xdescription
X
X	Archx manages archives (libraries) of source files, allowing
X	a large number of small files to be stored without using
X	excessive system resources.  Archx extracts all files from
X	an archive.
X
X	If no archive_name file is given, the standard input is read.
X	Archive header records are echoed to the standard output.
X
Xarchive file format
X
X	Archive files are standard text files.  Each archive element is
X	preceeded by a line of the format:
X	.s.nf
X	-h-	file.name	date	true_name
X	.s.f
X	Note that there is no line or byte count.  To prevent problems,
X	a '-' at the beginning of a record within a user file or embedded
X	archive will be "quoted" by doubling it.  The date and true filename
X	fields are ignored.  On some operating systems, file.name is
X	forced to lowercase.  The archive builder (archc) may prefix
X	other characters by '-'.
X
X	If the first non-blank line of an input file does not
X	begin with "-h", the text will be appended to "archx.tmp"
X	This is needed if archives are distributed by mail
X	and arrive with initial routing and subject information.
X
Xdiagnostics
X
X	Diagnostic messages should be self-explanatory
X
Xauthor
X
X	Martin Minow
X
Xbugs
X
X#endif
X
X#include	<stdio.h>
X#include	<ctype.h>
X#ifdef vms
X#include		<ssdef.h>
X#include		<stsdef.h>
X#define	IO_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
X#define	IO_ERROR	SS$_ABORT
X#endif
X/*
X * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
X */
X#ifndef	IO_SUCCESS
X#define	IO_SUCCESS	0
X#endif
X#ifndef	IO_ERROR
X#define	IO_ERROR	1
X#endif
X
X#define EOS		0
X#define	FALSE		0
X#define	TRUE		1
X
X/*
X * The following status codes are returned by gethdr()
X */
X#define DONE	0
X#define	GOTCHA	1
X#define	NOGOOD	2
X
Xchar		text[513];		/* Working text line		*/
Xchar		name[81];		/* Current archive member name	*/
Xchar		filename[81];		/* Working file name		*/
Xchar		arfilename[81];		/* Archive file name		*/
Xchar		fullname[81];		/* Output for argetname()	*/
Xint		verbose		= TRUE;	/* TRUE for verbosity		*/
Xint		first_archive;		/* For mail header skipping	*/
X
Xmain(argc, argv)
Xint		argc;			/* Arg count			*/
Xchar		*argv[];		/* Arg vector			*/
X{
X	register int		i;	/* Random counter		*/
X	int			status;	/* Exit status			*/
X
X#ifdef	vms
X	argc = getredirection(argc, argv);
X#endif
X	status = IO_SUCCESS;
X	if (argc == 1)
X	    process();
X	else {
X	    for (i = 1; i < argc; i++) {
X		if (freopen(argv[i], "r", stdin) != NULL)
X		    process();
X		else {
X		    perror(argv[i]);
X		    status = IO_ERROR;
X		}
X	    }
X	}
X	exit(status);
X}
X
Xprocess()
X/*
X * Process archive open on stdin
X */
X{
X	register char		*fn;	/* File name pointer		*/
X	register FILE		*outfd;
X	register int		i;
X
X	text[0] = EOS;
X	while ((i = gethdr()) != DONE) {
X	    switch (i) {
X	    case GOTCHA:
X		if ((outfd = fopen(name, "w")) == NULL) {
X		    perror(name);
X		    fprintf(stderr, "Can't create \"%s\"\n", name);
X		    arskip();
X		    continue;
X		}
X		break;
X
X	    case NOGOOD:
X		fprintf(stderr, "Missing -h-, writing to archx.tmp\n");
X		fprintf(stderr, "Current text line: %s", text);
X		strcpy(name, "archx.tmp");
X		if ((outfd = fopen(name, "a")) == NULL) {
X		    perror(name);
X		    fprintf(stderr, "Cannot append to %s\n", name);
X		    arskip();
X		    continue;
X		}
X		break;
X	    }
X	    arexport(outfd);
X	    fclose(outfd);
X	}
X}
X
Xint
Xgethdr()
X/*
X * If text is null, read a record, returning to signal input state:
X *	DONE	Eof read
X *	NOGOOD	-h- wasn't first non-blank line.  Line is in text[]
X *	GOTCHA	-h- found, parsed into name.
X */
X{
X	register char	*tp;
X	register char	*np;
X
Xagain:	if (text[0] == EOS
X	 && fgets(text, sizeof text, stdin) == NULL)
X	    return (DONE);
X	if (text[0] == '\n' && text[1] == EOS) {
X	    text[0] = EOS;
X	    goto again;
X	}
X	if (text[0] != '-'
X	 || text[1] != 'h'
X	 || text[2] != '-')
X	    return (NOGOOD);
X	for (tp = &text[3]; isspace(*tp); tp++)
X	    ;
X	for (np = name; !isspace(*tp); *np++ = *tp++)
X	    ;
X	*np = EOS;
X	return (GOTCHA);
X}
X
Xarskip()
X/*
X * Skip to next header
X */
X{
X	while (fgets(text, sizeof text, stdin) != NULL) {
X	    if (text[0] == '-' && text[1] == 'h' && text[2] == '-')
X		return;
X	}
X	text[0] = EOS;				/* EOF signal		*/
X}
X
Xarexport(outfd)
Xregister FILE	*outfd;
X/*
X * Read secret archive format, writing archived data to outfd.
X * Clean out extraneous <cr>,<lf>'s
X */
X{
X	register char	*tp;
X	unsigned int	nrecords;
X
X	printf("Creating \"%s\", ", name);
X	nrecords = 0;
X	while (fgets(text, sizeof text, stdin) != NULL) {
X	    tp = &text[strlen(text)];
X	    if (tp > &text[1] && *--tp == '\n' && *--tp == '\r') {
X		*tp++ = '\n';
X		*tp = EOS;
X	    }
X	    if (text[0] == '-') {
X		if (text[1] == 'h')
X		    goto gotcha;
X		fputs(text+1, outfd);
X	    }
X	    else {
X		fputs(text, outfd);
X	    }
X	    nrecords++;
X	}
X	text[0] = EOS;
Xgotcha:	printf("%u records\n", nrecords);
X	if (ferror(stdin) || ferror(outfd))
X	    printf("Creation of \"%s\" completed with error\n", name);
X}
X
X/*
X * getredirection() is intended to aid in porting C programs
X * to VMS (Vax-11 C) which does not support '>' and '<'
X * I/O redirection.  With suitable modification, it may
X * useful for other portability problems as well.
X */
X
X#ifdef	vms
Xstatic int
Xgetredirection(argc, argv)
Xint		argc;
Xchar		**argv;
X/*
X * Process vms redirection arg's.  Exit if any error is seen.
X * If getredirection() processes an argument, it is erased
X * from the vector.  getredirection() returns a new argc value.
X *
X * Warning: do not try to simplify the code for vms.  The code
X * presupposes that getredirection() is called before any data is
X * read from stdin or written to stdout.
X *
X * Normal usage is as follows:
X *
X *	main(argc, argv)
X *	int		argc;
X *	char		*argv[];
X *	{
X *		argc = getredirection(argc, argv);
X *	}
X */
X{
X	register char		*ap;	/* Argument pointer	*/
X	int			i;	/* argv[] index		*/
X	int			j;	/* Output index		*/
X	int			file;	/* File_descriptor 	*/
X
X	for (j = i = 1; i < argc; i++) {   /* Do all arguments	*/
X	    switch (*(ap = argv[i])) {
X	    case '<':			/* <file		*/
X		if (freopen(++ap, "r", stdin) == NULL) {
X		    perror(ap);		/* Can't find file	*/
X		    exit(IO_ERROR);	/* Is a fatal error	*/
X		}
X
X	    case '>':			/* >file or >>file	*/
X		if (*++ap == '>') {	/* >>file		*/
X		    /*
X		     * If the file exists, and is writable by us,
X		     * call freopen to append to the file (using the
X		     * file's current attributes).  Otherwise, create
X		     * a new file with "vanilla" attributes as if
X		     * the argument was given as ">filename".
X		     * access(name, 2) is TRUE if we can write on
X		     * the specified file.
X		     */
X		    if (access(++ap, 2) == 0) {
X			if (freopen(ap, "a", stdout) != NULL)
X			    break;	/* Exit case statement	*/
X			perror(ap);	/* Error, can't append	*/
X			exit(IO_ERROR);	/* After access test	*/
X		    }			/* If file accessable	*/
X		}
X		/*
X		 * On vms, we want to create the file using "standard"
X		 * record attributes.  create(...) creates the file
X		 * using the caller's default protection mask and
X		 * "variable length, implied carriage return"
X		 * attributes. dup2() associates the file with stdout.
X		 */
X		if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
X		 || dup2(file, fileno(stdout)) == -1) {
X		    perror(ap);		/* Can't create file	*/
X		    exit(IO_ERROR);	/* is a fatal error	*/
X		}			/* If '>' creation	*/
X		break;			/* Exit case test	*/
X
X	    default:
X		argv[j++] = ap;		/* Not a redirector	*/
X		break;			/* Exit case test	*/
X	    }
X	}				/* For all arguments	*/
X	return (j);
X}
X#endif
X
END_OF_FILE
if test 7837 -ne `wc -c <'archx.c'`; then
    echo shar: \"'archx.c'\" unpacked with wrong size!
fi
# end of 'archx.c'
fi
if test -f 'readme.txt' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'readme.txt'\"
else
echo shar: Extracting \"'readme.txt'\" \(1420 characters\)
sed "s/^X//" >'readme.txt' <<'END_OF_FILE'
XThis is a suggested replacement for shar.  It is based on
Xthe archive program in Kernighan and Plauger's Software Tools,
Xbut has been heavily simplified.
X
XIt has the following advantages over shar:
X
X1. it is not tied to Unix -- thus VMS users can unpack files without
X   excessive effort.  Archc and archx should run without change on
X   all Unix and Unix lookalike systems, as well as on VMS (VaxC)
X   and all PDP-11 Decus C systems.  It has been in use for over 6
X   years.
X
X2. it does not execute the distributed image, but interprets it.  This
X   means that trojan horses cannot be concealed in distributions.
X
X3  The distribution file can be edited without damaging the archive.
X   (Also, embedded archives can be handled).
X
XIt has the following disadvantages:
X
X1. It is not as flexible as shar -- it cannot create directories or
X   access any other Unix system services.
X
X2. There is no checksum capability (it appears impossible to implement
X   checksumming in a system-independent manner).
X
XTo use, save this message.  Then, use your favorite editor to extract
Xarchx.c (delimited by lines beginning with "-h-" in column 1).  Then
Xcompile archx and run it using the command:
X	archx <this_file>
XIt should produce readme.txt, archx.c, and archc.c.
XManual pages can be produced by extracting the text delimited by
X	#ifdef DOCUMENTATION
X	...
X	#endif
X
XPlease report problems to the author:
X
XMartin Minow
Xdecvax!minow
X
END_OF_FILE
if test 1420 -ne `wc -c <'readme.txt'`; then
    echo shar: \"'readme.txt'\" unpacked with wrong size!
fi
# end of 'readme.txt'
fi
echo shar: End of shell archive.
exit 0
-- 
 _--_|\  `-_-' Peter da Silva. +1 713 274 5180.      <peter at ficc.uu.net>
/      \  'U`  Have you hugged your wolf today?  <peter at sugar.hackercorp.com>
\_.--._/       Disclaimer: commercial solicitation by email to this address
      v                    is acceptable.



More information about the Alt.sources.d mailing list