Path: tut!santra!kth!sunic!mcsun!uunet!wuarchive!texbell!uhnix1!sugar!ficc!peter
From: peter@ficc.uu.net (Peter da Silva)
Newsgroups: comp.os.minix
Subject: Seperating out postings. Software tools stuff.
Message-ID: <6434@ficc.uu.net>
Date: 5 Oct 89 18:56:27 GMT
Organization: Xenix Support, FICC
Lines: 733

Why is a new standard for included files needed in comp.os.minix? What's
wrong with shars?

If you want to do things differently, how about the old Software Tools
arch format? It's easy enough to assemble by hand... from volume 4 of
comp.sources.unix:

-h- readme.txt	Thu Oct  5 13:53:48 1989	archx/readme.txt
This is a suggested replacement for shar.  It is based on
the archive program in Kernighan and Plauger's Software Tools,
but has been heavily simplified.

It has the following advantages over shar:

1. it is not tied to Unix -- thus VMS users can unpack files without
   excessive effort.  Archc and archx should run without change on
   all Unix and Unix lookalike systems, as well as on VMS (VaxC)
   and all PDP-11 Decus C systems.  It has been in use for over 6
   years.

2. it does not execute the distributed image, but interprets it.  This
   means that trojan horses cannot be concealed in distributions.

3  The distribution file can be edited without damaging the archive.
   (Also, embedded archives can be handled).

It has the following disadvantages:

1. It is not as flexible as shar -- it cannot create directories or
   access any other Unix system services.

2. There is no checksum capability (it appears impossible to impleme41 records read from //xds13/usr1/xds/src/archx/readme.txt
nt
   checksumming in a system-independent manner).

To use, save this message.  Then, use your favorite editor to extract
archx.c (delimited by lines beginning with "-h-" in column 1).  Then
compile archx and run it using the command:
	archx <this_file>
It should produce readme.txt, archx.c, and archc.c.
Manual pages can be produced by extracting the text delimited by
	#ifdef DOCUMENTATION
	...
	#endif

Please report problems to the author:

Martin Minow
decvax!minow

-h- archc.c	Thu Oct  5 13:53:48 1989	archx/archc.c
/*
 *			A R C H I V E
 *
 * Create an archive
 *
 */

/*)BUILD	$(TKBOPTIONS) = {
			TASK	= ...ARC
		}
*/

#ifdef	DOCUMENTATION

title	archc	text file archive creation
index		text file archive creation

synopsis

	archc file[s] >archive

description

	Archc manages archives (libraries) of source files, allowing
	a large number of small files to be stored without using
	excessive system resources.  It copies the set of named
	files to standard output in archive format.

	The archx program will recreate the files from an archive.

	Note: there are no checks against the same file appearing
	twice in an archive.

archive file format

	Archive files are standard text files.  Each archive element is
	preceeded by a line of the format:
	.s.nf
	-h-	file.name	date	true_path_name
	.s.f
	Note that there is no line or byte count.  To prevent problems,
	a '-' at the beginning of a record within a user file or embedded
	archive will be "quoted" by doubling it.  The date and true filename
	fields are ignored.  On Dec operating systems, file.name is
	forced to lowercase.  Certain bytes at the beginning of a record are
	also prefixed by '-' to prevent mailers from treating them
	as commands.

diagnostics

	Diagnostic messages should be self-explanatory

author

	Martin Minow

#endif

#include	<stdio.h>
#include	<ctype.h>

#define unix
#undef vms

#ifdef vms
#include		<ssdef.h>
#include		<stsdef.h>
#define	IO_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
#define	IO_ERROR	SS$_ABORT
#endif
/*
 * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
 */
#ifndef	IO_SUCCESS
#define	IO_SUCCESS	0
#endif
#ifndef	IO_ERROR
#define	IO_ERROR	1
#endif
#define EOS		0
#define	FALSE		0
#define	TRUE		1

char		text[513];		/* Working text			*/
char		name[81];		/* Current archive member name	*/
char		pathname[81];		/* Output for argetname()	*/
char		*timetext;		/* Time of day text		*/
int		verbose		= TRUE; /* TRUE for verbosity		*/
FILE		*infd;			/* Input file			*/

main(argc, argv)
int		argc;			/* Arg count			*/
char		*argv[];		/* Arg vector			*/
{
	register int		i;	/* Random counter		*/
	register char		*fn;	/* File name pointer		*/
	register char		*argp;	/* Arg pointer			*/
	int			nfiles;
	extern char		*ctime();
	extern long		time();
	long			timval;

	time(&timval);
	timetext = ctime(&timval);
	timetext[24] = EOS;
#ifdef vms
	argc = getredirection(argc, argv);
#endif
	if (argc <= 1)
	    fprintf(stderr, "No files to archive?\n");
#ifdef	unix
	for (i = 1; i < argc; i++) {
	    if ((infd = fopen(argv[i], "r")) == NULL)
		perror(argv[i]);
	    else {
		strcpy(pathname, argv[i]);
		import();
		fclose(infd);
	    }
	}
#else
	/*
	 * Decus C supports fwild/fnext for explicit processing
	 * of wild-card filenames.
	 */
	for (i = 1; i < argc; i++) {
	    if ((infd = fwild(argv[i], "r")) == NULL)
		perror(argv[i]);
	    else {
		for (nfiles = 0; fnext(infd) != NULL; nfiles++) {
		    fgetname(infd, pathname);
		    import();
		}
		fclose(infd);
		if (nfiles == 0)
		    fprintf(stderr, "No files match \"%s\"\n", argv[i]);
	    }
	}
#endif
}

import()
/*
 * Add the file open on infd (with file name in pathname) to
 * the archive.
 */
{
	unsigned int	nrecords;

	fixname();
	nrecords = 0;
	printf("-h- %s\t%s\t%s\n", name, timetext, pathname);
	while (fgets(text, sizeof text, infd) != NULL) {
	    switch (text[0]) {
	    case '-':
	    case '.':
	    case '~':
		putchar('-');				/* Quote	*/
	    }
	    fputs(text, stdout);
	    nrecords++;
	}
	if (ferror(infd)) {
	    perror(name);
	    fprintf(stderr, "Error when importing a file\n");
	}
	if (verbose) {
	    fprintf(stderr, "%u records read from %s\n",
		nrecords, pathname);
	}
}

fixname()
/*
 * Get file name (in pathname), stripping off device:[directory]
 * and ;version.  The archive name ("file.ext") is written to name[].
 * On a dec operating system, name is forced to lowercase.
 */
{
	register char	*tp;
	register char	*ip;
	char		bracket;
	extern char	*strrchr();

#ifdef	unix
	/*
	 * name is after all directory information
	 */
	if ((tp = strrchr(pathname, '/')) != NULL)
	    tp++;
	else
	    tp = pathname;
	strcpy(name, tp);
#else
	strcpy(name, pathname);
	if ((tp = strrchr(name, ';')) != NULL)
		*tp = EOS;
	while ((tp = strchr(name, ':')) != NULL)
		strcpy(name, tp + 1);
	switch (name[0]) {
	case '[':	bracket = ']';
			break;
	case '<':	bracket = '>';
			break;
	case '(':	bracket = ')';
			break;
	default:	bracket = EOS;
			break;
	}
	if (bracket != EOS) {
	    if ((tp = strchr(name, bracket)) == NULL) {
		fprintf(stderr, "? Illegal file name \"%s\"\n",
		    pathname);
	    }
	    else {
		strcpy(name, tp + 1);
	    }
	}
	for (tp = name; *tp != EOS; tp++) {
	    if (isupper(*tp))
		*tp = tolower(*tp);
	}
#endif
}

#ifdef	unix
char *
strrchr(stng, chr)
register char	*stng;
register char	chr;
/*
 * Return rightmost instance of chr in stng.
 * This has the wrong name on some Unix systems.
 */
{
	register char	*result;

	result = NULL;

	do {
	    if (*stng == chr)
		result = stng;
	} while (*stng++ != EOS);
	return (result);
}
#endif

/*
 * getredirection() is intended to aid in porting C programs
 * to VMS (Vax-11 C) which does not support '>' and '<'
 * I/O redirection.  With suitable modification, it may
 * useful for other portability problems as well.
 */

static int
getredirection(argc, argv)
int		argc;
char		**argv;
/*
 * Process vms redirection arg's.  Exit if any error is seen.
 * If getredirection() processes an argument, it is erased
 * from the vector.  getredirection() returns a new argc value.
 *
 * Warning: do not try to simplify the code for vms.  The code
 * presupposes that getredirection() is called before any data is
 * read from stdin or written to stdout.
 *
 * Normal usage is as follows:
 *
 *	main(argc, argv)
 *	int		argc;
 *	char		*argv[];
 *	{
 *		argc = getredirection(argc, argv);
 *	}
 */
{
#ifdef	vms
	register char		*ap;	/* Argument pointer	*/
	int			i;	/* argv[] index		*/
	int			j;	/* Output index		*/
	int			file;	/* File_descriptor 	*/

	for (j = i = 1; i < argc; i++) {   /* Do all arguments	*/
	    switch (*(ap = argv[i])) {
	    case '<':			/* <file		*/
		if (freopen(++ap, "r", stdin) == NULL) {
		    perror(ap);		/* Can't find file	*/
		    exit(IO_ERROR);	/* Is a fatal error	*/
		}

	    case '>':			/* >file or >>file	*/
		if (*++ap == '>') {	/* >>file		*/
		    /*
		     * If the file exists, and is writable by us,
		     * call freopen to append to the file (using the
		     * file's current attributes).  Otherwise, create
		     * a new file with "vanilla" attributes as if
		     * the argument was given as ">filename".
		     * access(name, 2) is TRUE if we can write on
		     * the specified file.
		     */
		    if (access(++ap, 2) == 0) {
			if (freopen(ap, "a", stdout) != NULL)
			    break;	/* Exit case statement	*/
			perror(ap);	/* Error, can't append	*/
			exit(IO_ERROR);	/* After access test	*/
		    }			/* If file accessable	*/
		}
		/*
		 * On vms, we want to create the file using "standard"
		 * record attributes.  create(...) creates the file
		 * using the caller's default protection mask and
		 * "variable length, implied carriage return"
		 * attributes. dup2() associates the file with stdout.
		 */
		if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
		 || dup2(file, fileno(stdout)) == -1) {
		    perror(ap);		/* Can't create file	*/
		    exit(IO_ERROR);	/* is a fatal error	*/
		}			/* If '>' creation	*/
		break;			/* Exit case test	*/

	    default:
		argv[j++] = ap;		/* Not a redirector	*/
		break;			/* Exit case test	*/
	    }
	}				/* For all arguments	*/
	return (j);
#else
	/*
	 * Note: argv[] is referenced to fool the Dec341 records read from //xds13/usr1/xds/src/archx/archc.c
us C
	 * syntax analyser, supressing an unneeded warning
	 * message.
	 */
	return (argv[0], argc);		/* Just return as seen	*/
#endif
}



-h- archx.c	Thu Oct  5 13:53:48 1989	archx/archx.c
/*
 *			A R C H X
 *
 * Archive extraction
 *
 */

/*
 * Note: the )BUILD comment is extracted by a Decus C tool to construct
 * system-dependent compiler command lines.
 *
 * Text inside #ifdef DOCUMENTATION is converted to runoff by a
 * Decus C tool.
 */

/*)BUILD	$(TKBOPTIONS) = {
			TASK	= ...ARX
		}
*/

#ifdef	DOCUMENTATION

title	archx	text file archiver extraction
index		text file archiver extraction

synopsis

	archx archive_files

description

	Archx manages archives (libraries) of source files, allowing
	a large number of small files to be stored without using
	excessive system resources.  Archx extracts all files from
	an archive.

	If no archive_name file is given, the standard input is read.
	Archive header records are echoed to the standard output.

archive file format

	Archive files are standard text files.  Each archive element is
	preceeded by a line of the format:
	.s.nf
	-h-	file.name	date	true_name
	.s.f
	Note that there is no line or byte count.  To prevent problems,
	a '-' at the beginning of a record within a user file or embedded
	archive will be "quoted" by doubling it.  The date and true filename
	fields are ignored.  On some operating systems, file.name is
	forced to lowercase.  The archive builder (archc) may prefix
	other characters by '-'.

	If the first non-blank line of an input file does not
	begin with "-h", the text will be appended to "archx.tmp"
	This is needed if archives are distributed by mail
	and arrive with initial routing and subject information.

diagnostics

	Diagnostic messages should be self-explanatory

author

	Martin Minow

bugs

#endif

#include	<stdio.h>
#include	<ctype.h>
#ifdef vms
#include		<ssdef.h>
#include		<stsdef.h>
#define	IO_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
#define	IO_ERROR	SS$_ABORT
#endif
/*
 * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
 */
#ifndef	IO_SUCCESS
#define	IO_SUCCESS	0
#endif
#ifndef	IO_ERROR
#define	IO_ERROR	1
#endif

#define EOS		0
#define	FALSE		0
#define	TRUE		1

/*
 * The following status codes are returned by gethdr()
 */
#define DONE	0
#define	GOTCHA	1
#define	NOGOOD	2

char		text[513];		/* Working text line		*/
char		name[81];		/* Current archive member name	*/
char		filename[81];		/* Working file name		*/
char		arfilename[81];		/* Archive file name		*/
char		fullname[81];		/* Output for argetname()	*/
int		verbose		= TRUE;	/* TRUE for verbosity		*/
int		first_archive;		/* For mail header skipping	*/

main(argc, argv)
int		argc;			/* Arg count			*/
char		*argv[];		/* Arg vector			*/
{
	register int		i;	/* Random counter		*/
	int			status;	/* Exit status			*/

#ifdef	vms
	argc = getredirection(argc, argv);
#endif
	status = IO_SUCCESS;
	if (argc == 1)
	    process();
	else {
	    for (i = 1; i < argc; i++) {
		if (freopen(argv[i], "r", stdin) != NULL)
		    process();
		else {
		    perror(argv[i]);
		    status = IO_ERROR;
		}
	    }
	}
	exit(status);
}

process()
/*
 * Process archive open on stdin
 */
{
	register char		*fn;	/* File name pointer		*/
	register FILE		*outfd;
	register int		i;

	text[0] = EOS;
	while ((i = gethdr()) != DONE) {
	    switch (i) {
	    case GOTCHA:
		if ((outfd = fopen(name, "w")) == NULL) {
		    perror(name);
		    fprintf(stderr, "Can't create \"%s\"\n", name);
		    arskip();
		    continue;
		}
		break;

	    case NOGOOD:
		fprintf(stderr, "Missing -h-, writing to archx.tmp\n");
		fprintf(stderr, "Current text line: %s", text);
		strcpy(name, "archx.tmp");
		if ((outfd = fopen(name, "a")) == NULL) {
		    perror(name);
		    fprintf(stderr, "Cannot append to %s\n", name);
		    arskip();
		    continue;
		}
		break;
	    }
	    arexport(outfd);
	    fclose(outfd);
	}
}

int
gethdr()
/*
 * If text is null, read a record, returning to signal input state:
 *	DONE	Eof read
 *	NOGOOD	-h- wasn't first non-blank line.  Line is in text[]
 *	GOTCHA	-h- found, parsed into name.
 */
{
	register char	*tp;
	register char	*np;

again:	if (text[0] == EOS
	 && fgets(text, sizeof text, stdin) == NULL)
	    return (DONE);
	if (text[0] == '\n' && text[1] == EOS) {
	    text[0] = EOS;
	    goto again;
	}
	if (text[0] != '-'
	 || text[1] != 'h'
	 || text[2] != '-')
	    return (NOGOOD);
	for (tp = &text[3]; isspace(*tp); tp++)
	    ;
	for (np = name; !isspace(*tp); *np++ = *tp++)
	    ;
	*np = EOS;
	return (GOTCHA);
}

arskip()
/*
 * Skip to next header
 */
{
	while (fgets(text, sizeof text, stdin) != NULL) {
	    if (text[0] == '-' && text[1] == 'h' && text[2] == '-')
		return;
	}
	text[0] = EOS;				/* EOF signal		*/
}

arexport(outfd)
register FILE	*outfd;
/*
 * Read secret archive format, writing archived data to outfd.
 * Clean out extraneous <cr>,<lf>'s
 */
{
	register char	*tp;
	unsigned int	nrecords;

	printf("Creating \"%s\", ", name);
	nrecords = 0;
	while (fgets(text, sizeof text, stdin) != NULL) {
	    tp = &text[strlen(text)];
	    if (tp > &text[1] && *--tp == '\n' && *--tp == '\r') {
		*tp++ = '\n';
		*tp = EOS;
	    }
	    if (text[0] == '-') {
		if (text[1] == 'h')
		    goto gotcha;
		fputs(text+1, outfd);
	    }
	    else {
		fputs(text, outfd);
	    }
	    nrecords++;
	}
	text[0] = EOS;
gotcha:	printf("%u records\n", nrecords);
	if (ferror(stdin) || ferror(outfd))
	    printf("Creation of \"%s\" completed with error\n", name);
}

/*
 * getredirection() is intended to aid in porting C programs
 * to VMS (Vax-11 C) which does not support '>' and '<'
 * I/O redirection.  With suitable modification, it may
 * useful for other portability problems as well.
 */

#ifdef	vms
static int
getredirection(argc, argv)
int		argc;
char		**argv;
/*
 * Process vms redirection arg's.  Exit if any error is seen.
 * If getredirection() processes an argument, it is erased
 * from the vector.  getredirection() returns a new argc value.
 *
 * Warning: do not try to simplify the code for vms.  The code
 * presupposes that getredirection() is called before any data is
 * read from stdin or written to stdout.
 *
 * Normal usage is as follows:
 *
 *	main(argc, argv)
 *	int		argc;
 *	char		*argv[];
 *	{
 *		argc = getredirection(argc, argv);
 *	}
 */
{
	register char		*ap;	/* Argument pointer	*/
	int			i;	/* argv[] index		*/
	int			j;	/* Output index		*/
	int			file;	/* File_descriptor 	*/

	for (j = i = 1; i < argc; i++) {   /* Do all arguments	*/
	    switch (*(ap = argv[i])) {
	    case '<':			/* <file		*/
		if (freopen(++ap, "r", stdin) == NULL) {
		    perror(ap);		/* Can't find file	*/
		    exit(IO_ERROR);	/* Is a fatal error	*/
		}

	    case '>':			/* >file or >>file	*/
		if (*++ap == '>') {	/* >>file		*/
		    /*
		     * If the file exists, and is writable by us,
		     * call freopen to append to the file (using the
		     * file's current attributes).  Otherwise, create
		     * a new file with "vanilla" attributes as if
		     * the argument was given as ">filename".
		     * access(name, 2) is TRUE if we can write on
		     * the specified file.
		     */
		    if (access(++ap, 2) == 0) {
			if (freopen(ap, "a", stdout) != NULL)
			    break;	/* Exit case statement	*/
			perror(ap);	/* Error, can't append	*/
			exit(IO_ERROR);	/* After access test	*/
		    }			/* If file accessable	*/
		}
		/*
		 * On vms, we want to create the file using "standard"
		 * record attributes.  create(...) creates the file
		 * using the caller's default protection mask and
		 * "variable length, implied carriage return"
		 * attributes. dup2() associates the file with stdout.
		 */
		if ((file = creat(ap, 0,333 records read from //xds13/usr1/xds/src/archx/archx.c
 "rat=cr", "rfm=var")) == -1
		 || dup2(file, fileno(stdout)) == -1) {
		    perror(ap);		/* Can't create file	*/
		    exit(IO_ERROR);	/* is a fatal error	*/
		}			/* If '>' creation	*/
		break;			/* Exit case test	*/

	    default:
		argv[j++] = ap;		/* Not a redirector	*/
		break;			/* Exit case test	*/
	    }
	}				/* For all arguments	*/
	return (j);
}
#endif
-h- signature
-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
``I feel that any [environment] with users in it is "adverse".''           'U`
	-- Eric Peterson <lcc.eric@seas.ucla.edu>
