[comp.sources.atari.st] v01i076: sttar -- Unix-style "tar" utility

koreth@ssyx.ucsc.edu (Steven Grimm) (11/21/88)

Submitted-by: cs.buffalo.edu!sigmast!dgy (Dave Yearke)
Posting-number: Volume 1, Issue 76
Archive-name: sttar

[This arrived in the form of three shar files inside an arc file; I unpacked
 the arc file and sharred the shar files.  The result is that you'll have to
 unshar four times to get everything out.  Let me know if this causes any
 problems. -sg]

This is an implementation of "tar" for the ST.  Questions and comments
should be directed to Carl Barron at sigmast!brass!carl_barron.

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
#	Run the following text with /bin/sh to create:
#	  ctar.shr
#	  dtar.shr
#	  stftw.shr
#
sed 's/^X//' << 'SHAR_EOF' > ctar.shr &&
X#! /bin/sh
X# This is a shell archive, meaning:
X# 1. Remove everything above the #! /bin/sh line.
X# 2. Save the resulting text in a file.
X# 3. Execute the file with /bin/sh (not csh) to create the files:
X#	createta.c
X#	ctar.c
X#	detar.h
X#	makefile
X#	todo.c
X#	tounix.c
X# This archive created: Fri Aug 19 03:21:53 1988
Xexport PATH; PATH=/bin:$PATH
Xif test -f 'createta.c'
Xthen
X	echo shar: will not over-write existing file "'createta.c'"
Xelse
Xcat << \SHAR_EOF > 'createta.c'
X#include <stdio.h>
X#include "ndir.h"
X#include "ftw.h"
X#include "detar.h"
Xextern FILE *ftar;
X
Xextern char *tounix(); /* char * */
Xextern void tooct(),calc_chksum();
Xstatic union record tar_rec;
Xstatic struct stat *stbuf;
X
Xvoid bzero(p,n)char *p;int n;
X{
X	while(n--)
X		*p++='\0';
X}
X
Xstatic void dump_header(name,x)char *name;int x;
X{
X		bzero(tar_rec.charptr,RECORDSIZE);
X		strncpy(tar_rec.header.name,tounix(name),NAMSIZ);
X		tooct(0644L,8,tar_rec.header.mode);
X		tooct(0L,8,tar_rec.header.uid);
X		tooct(0L,8,tar_rec.header.gid);
X		tooct(stbuf->st_size,12,tar_rec.header.size);
X		tooct(stbuf->st_mtime,12,tar_rec.header.mtime);
X		strncpy(tar_rec.header.magic,TMAGIC,8);
X		strncpy(tar_rec.header.chksum,CHKBLANKS,8);
X		tar_rec.header.linkflag=x+'0';
X		calc_chksum(&tar_rec);
X		fwrite(tar_rec.charptr,1,RECORDSIZE,ftar);
X}
X
Xstatic void dump_file(name)char *name;
X{
X	FILE *fin;
X	int n;
X
X	fin=fopen(name,"rb");
X
X	do
X	{
X		bzero(tar_rec.charptr,RECORDSIZE);
X		n=fread(tar_rec.charptr,1,RECORDSIZE,fin);
X		fwrite(tar_rec.charptr,1,RECORDSIZE,ftar);
X	}while(n==RECORDSIZE);
X	fclose(fin);
X}
X
Xint create_tar(name,sbuf,flag)char *name;struct stat *sbuf;int flag;
X{
X	stbuf=sbuf;
X	switch(flag)
X	{
X	case FTW_NS:
X		fprintf(stderr,"ctar:can't stat %s\n",name);
X		return -1;
X	case FTW_DNR:
X		fprintf(stderr,"ctar:can't open directory %s\n");
X		return -2;
X	case FTW_F:
X		dump_header(name,0);
X		dump_file(name);
X		return 0;
X	case FTW_D:
X		strcat(name,"\\");
X		dump_header(name,5);
X		return 0;
X	}
X}
X
XSHAR_EOF
Xchmod +x 'createta.c'
Xfi # end of overwriting check
Xif test -f 'ctar.c'
Xthen
X	echo shar: will not over-write existing file "'ctar.c'"
Xelse
Xcat << \SHAR_EOF > 'ctar.c'
X#include <stdio.h>
X
XFILE *ftar;
X
Xextern int create_tar();
X
Xmain(ac,av)int ac;char *av[];
X{
X	ftar=fopen(av[1],"wb");
X	ac -= 2;
X	av += 2;
X
X	while(ac--)
X	{
X		if(ftw(*av++,create_tar,10))break;
X	}
X	fclose(ftar);
X}
XSHAR_EOF
Xchmod +x 'ctar.c'
Xfi # end of overwriting check
Xif test -f 'detar.h'
Xthen
X	echo shar: will not over-write existing file "'detar.h'"
Xelse
Xcat << \SHAR_EOF > 'detar.h'
X/*
X *	MSDOS TAR Extractor
X */
X
X#define	RECORDSIZE	512
X#define	NAMSIZ		100
X#define	TUNMLEN		32
X#define	TGNMLEN		32
X
X/*
X *	Header block on tape.
X *
X *	no byte swapping
X */
X
Xunion record {
X	char	charptr[RECORDSIZE];
X	struct	{
X		char	name[NAMSIZ];
X		char	mode[8];
X		char	uid[8];
X		char	gid[8];
X		char	size[12];
X		char	mtime[12];
X		char	chksum[8];
X		char	linkflag;
X		char	linkname[NAMSIZ];
X		char	magic[8];
X		char	uname[TUNMLEN];
X		char	gname[TGNMLEN];
X		char	devmajor[8];
X		char	devminor[8];
X	} header;
X};
X
X#define	CHKBLANKS	"        "	/* Checksum: 8 blanks, no null */
X#define	TMAGIC		"ustar  "	/* Majic: 7 bytes and a null */
X
X/* The linkflag defines the type of file */
X
X#define	LF_OLDNORMAL	'\0'		/* Normal disk file, Unix compat */
X#define	LF_NORMAL	'0'		/* Normal disk file */
X#define	LF_LINK		'1'		/* Link to previously dumped file */
X#define	LF_SYMLINK	'2'		/* Symbolic link */
X#define	LF_CHR		'3'		/* Character special file */
X#define	LF_BLK		'4'		/* Block special file */
X#define	LF_DIR		'5'		/* Directory */
X#define	LF_FIFO		'6'		/* FIFO special file */
X#define	LF_CONTIG	'7'		/* Contiguous file */
X
X/*
X *	Unix Stat Header (K&R)
X *
X */
X
Xstruct	stat  {
X	int	st_dev;
X	int	st_ino;
X	int	st_mode;
X	int	st_nlink;
X	int	st_uid;
X	int	st_gid;
X	int	st_rdev;
X	long	st_size;
X	long	st_atime;
X	long	st_mtime;
X	long	st_ctime;
X};
X
X#define	S_IFMT		0160000
X#define	S_IFDIR		0040000
X#define	S_IFCHR		0020000
X#define	S_IFBLK		0060000
X#define	S_IFREG		0100000
X#define	S_ISUID		0004000
X#define	S_ISGID		0002000
X#define	S_ISVTX		0001000
X#define	S_IREAD		0000400
X#define	S_IWRITE	0000200
X#define	S_IEXEC		0000100
XSHAR_EOF
Xchmod +x 'detar.h'
Xfi # end of overwriting check
Xif test -f 'makefile'
Xthen
X	echo shar: will not over-write existing file "'makefile'"
Xelse
Xcat << \SHAR_EOF > 'makefile'
XCFLAGS=-A
XOBJ=ctar.o createta.o tounix.o todo.o
Xctar.prg: $(OBJ)
X	cc $(OBJ) -ldir
XSHAR_EOF
Xchmod +x 'makefile'
Xfi # end of overwriting check
Xif test -f 'todo.c'
Xthen
X	echo shar: will not over-write existing file "'todo.c'"
Xelse
Xcat << \SHAR_EOF > 'todo.c'
X#include "detar.h"
Xvoid tooct();
X
Xvoid bcopy(src,dest,n)char *src,*dest;int n;
X{
X	while(n--)
X		*dest++ = *src++;
X}
X
Xvoid
Xcalc_chksum(header)
X	register union record *header;
X{
X	register int	i, sum;
X	register char	*p;
X
X	bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));
X
X	sum = 0;
X	p = header->charptr;
X	for (i = sizeof(*header); --i >= 0; ) {
X		/*
X		 * We can't use unsigned char here because of old compilers,
X		 * e.g. V7.
X		 */
X		sum += 0xFF & *p++;
X	}
X
X	/*
X	 * Fill in the checksum field.  It's formatted differently
X	 * from the other fields:  it has [6] digits, a null, then a
X	 * space -- rather than digits, a space, then a null.
X	 * We use tooct then write the null in over tooct's space.
X	 * The final space is already there, from checksumming, and
X	 * tooct doesn't modify it.
X	 *
X	 * This is a fast way to do:
X	 * (void) sprintf(header->header.chksum, "%6o", sum);
X	 */
X	tooct((long) sum,	8,  header->header.chksum);
X	header->header.chksum[6] = '\0';	/* Zap the space */
X}
X
Xvoid
Xtooct(value, digs, where)
X	register long	value;
X	register int	digs;
X	register char	*where;
X{
X	
X	--digs;				/* Trailing null slot is left alone */
X	where[--digs] = ' ';		/* Put in the space, though */
X
X	/* Produce the digits -- at least one */
X	do {
X		where[--digs] = '0' + (char)(value & 7); /* one octal digit */
X		value >>= 3;
X	} while (digs > 0 && value != 0);
X
X	/* Leading spaces, if necessary */
X	while (digs > 0)
X		where[--digs] = ' ';
X
X}
XSHAR_EOF
Xchmod +x 'todo.c'
Xfi # end of overwriting check
Xif test -f 'tounix.c'
Xthen
X	echo shar: will not over-write existing file "'tounix.c'"
Xelse
Xcat << \SHAR_EOF > 'tounix.c'
Xchar *tounix(name)register char *name;
X{
X	static char unix[100];
X	register char *p;
X	
X	bzero(unix,100);
X	for(p=unix;*name;name++,p++)
X		switch(*name)
X		{
X		case '_':
X			*p='B';
X			break;
X		case '\\':
X			*p='/';
X			break;
X		default:
X			*p = *name;
X			break;
X		}
X	return unix;
X}
XSHAR_EOF
Xchmod +x 'tounix.c'
Xfi # end of overwriting check
X#	End of shell archive
Xexit 0
SHAR_EOF
chmod 0600 ctar.shr || echo "restore of ctar.shr fails"
sed 's/^X//' << 'SHAR_EOF' > dtar.shr &&
X#! /bin/sh
X# This is a shell archive, meaning:
X# 1. Remove everything above the #! /bin/sh line.
X# 2. Save the resulting text in a file.
X# 3. Execute the file with /bin/sh (not csh) to create the files:
X#	detar.man
X#	makefile
X#	detar.c
X#	detar.h
X#	dodetar.c
X#	misc.c
X#	togem.c
X# This archive created: Mon Feb  8 14:37:02 1988
Xexport PATH; PATH=/bin:$PATH
Xif test -f 'detar.man'
Xthen
X	echo shar: will not over-write existing file "'detar.man'"
Xelse
Xcat << \SHAR_EOF > 'detar.man'
X
X	NAME - detar extract all files or list files in a tar file
X
X	USAGE - detar [d] tarfile
X
X	if d is specified a verbose listing of the files in the tar file
X	is given and no extraction occurs. Otherwise ALL the files in the
X	tar file are extracted into directories as per the tar file.
X
XSHAR_EOF
Xchmod +x 'detar.man'
Xfi # end of overwriting check
Xif test -f 'makefile'
Xthen
X	echo shar: will not over-write existing file "'makefile'"
Xelse
Xcat << \SHAR_EOF > 'makefile'
XCFLAGS=-A
XOFLAGS=
XLIB=
XPROG=detar.ttp
XOBJ=\
Xdetar.o\
Xdodetar.o misc.o togem.o
X$(PROG): $(OBJ)
X	cc -o $(PROG) $(OFLAGS) $(OBJ) $(LIB)
Xdetar.o: detar.h
Xdodetar.o: detar.h
XSHAR_EOF
Xchmod +x 'makefile'
Xfi # end of overwriting check
Xif test -f 'detar.c'
Xthen
X	echo shar: will not over-write existing file "'detar.c'"
Xelse
Xcat << \SHAR_EOF > 'detar.c'
X#include <stdio.h>
X#include "detar.h"
Xchar **extract;
Xint extsize;
XFILE *ftar;
Xextern int all(),some(),strcmp();
X
Xmain(ac,av)int ac;char *av[];
X{
X	if(ac<2)
X	{
X		fprintf(stderr,"detar: tarfile [file(s)]\n");
X		exit(1);
X	}
X	if((ftar=fopen(av[1],"rb"))==NULL)
X	{
X		fprintf(stderr,"detar:Can't open tarfile %s\n",av[1]);
X		exit(1);
X	}
X	if(ac==2)
X		do_detar(all);
X	else
X	{
X		ac -= 2;
X		av += 2;
X		extsize=ac;
X		extract=av;
X		qsort(extract,ac,sizeof(char *),strcmp);
X		do_detar(some);
X	}
X	fclose(ftar);
X}
X
Xint all(what)char *what;
X{
X	return 1;
X}
X
Xint some(what)char *what;
X{
X	int c,i;
X
X	for(i=0;i<extsize;i++)
X	{
X		if((c=strcmp(what,extract[i]))<0)
X			return 0;
X		if(c==0) return 1;
X	}
X	return 0;
X}
XSHAR_EOF
Xchmod +x 'detar.c'
Xfi # end of overwriting check
Xif test -f 'detar.h'
Xthen
X	echo shar: will not over-write existing file "'detar.h'"
Xelse
Xcat << \SHAR_EOF > 'detar.h'
X/*
X *	MSDOS TAR Extractor
X */
X
X#define	RECORDSIZE	512
X#define	NAMSIZ		100
X#define	TUNMLEN		32
X#define	TGNMLEN		32
X
X/*
X *	Header block on tape.
X *
X *	no byte swapping
X */
X
Xunion record {
X	char	charptr[RECORDSIZE];
X	struct	{
X		char	name[NAMSIZ];
X		char	mode[8];
X		char	uid[8];
X		char	gid[8];
X		char	size[12];
X		char	mtime[12];
X		char	chksum[8];
X		char	linkflag;
X		char	linkname[NAMSIZ];
X		char	magic[8];
X		char	uname[TUNMLEN];
X		char	gname[TGNMLEN];
X		char	devmajor[8];
X		char	devminor[8];
X	} header;
X};
X
X#define	CHKBLANKS	"        "	/* Checksum: 8 blanks, no null */
X#define	TMAGIC		"ustar  "	/* Majic: 7 bytes and a null */
X
X/* The linkflag defines the type of file */
X
X#define	LF_OLDNORMAL	'\0'		/* Normal disk file, Unix compat */
X#define	LF_NORMAL	'0'		/* Normal disk file */
X#define	LF_LINK		'1'		/* Link to previously dumped file */
X#define	LF_SYMLINK	'2'		/* Symbolic link */
X#define	LF_CHR		'3'		/* Character special file */
X#define	LF_BLK		'4'		/* Block special file */
X#define	LF_DIR		'5'		/* Directory */
X#define	LF_FIFO		'6'		/* FIFO special file */
X#define	LF_CONTIG	'7'		/* Contiguous file */
X
X/*
X *	Unix Stat Header (K&R)
X *
X */
X
Xstruct	stat  {
X	int	st_dev;
X	int	st_ino;
X	int	st_mode;
X	int	st_nlink;
X	int	st_uid;
X	int	st_gid;
X	int	st_rdev;
X	long	st_size;
X	long	st_atime;
X	long	st_mtime;
X	long	st_ctime;
X};
X
X#define	S_IFMT		0160000
X#define	S_IFDIR		0040000
X#define	S_IFCHR		0020000
X#define	S_IFBLK		0060000
X#define	S_IFREG		0100000
X#define	S_ISUID		0004000
X#define	S_ISGID		0002000
X#define	S_ISVTX		0001000
X#define	S_IREAD		0000400
X#define	S_IWRITE	0000200
X#define	S_IEXEC		0000100
XSHAR_EOF
Xchmod +x 'detar.h'
Xfi # end of overwriting check
Xif test -f 'dodetar.c'
Xthen
X	echo shar: will not over-write existing file "'dodetar.c'"
Xelse
Xcat << \SHAR_EOF > 'dodetar.c'
X#include <stdio.h>
X#include "detar.h"
Xstatic union record tar_rec;
Xstatic struct s_q
X{
X	char name[NAMSIZ];
X	char link[NAMSIZ];
X	int mode,linkflag;
X	long size;
X}my_header;
X
Xstatic struct link_q
X{
X	struct link_q *prev;
X	char name[NAMSIZ];
X	char link[NAMSIZ];
X}*link_list,*new_link,*todo_link;
X
Xextern long from_oct();
Xextern int calc_chksum();
Xextern FILE *ftar;
Xextern char *to_gem();
Xstatic int read_header()
X{
X	int chk;
X	do
X	{
X		fread(tar_rec.charptr,1,RECORDSIZE,ftar);
X		if(feof(ftar))return -1;
X		chk=calc_chksum(&tar_rec);
X	}while(chk==2);
X	return chk;
X}
X
Xdo_detar(test)int (*test)();
X{
X	int chk;
X	link_list=NULL;
X	while((chk=read_header())>=0)
X	{
X		if(chk==0)
X		{
X			fprintf(stderr,
X			"Checksum error on %s aborting",tar_rec.header.name);
X			exit(2);
X		}
X		if(tar_rec.header.name[strlen(tar_rec.header.name)-1]=='/')
X		{
X			tar_rec.header.name[strlen(tar_rec.header.name)-1]='\0';
X			my_header.linkflag=LF_DIR;
X		}
X		else
X			my_header.linkflag=tar_rec.header.linkflag;
X		strcpy(my_header.name,to_gem(tar_rec.header.name));
X		if(!(*test)(my_header.name))
X			continue;
X		strcpy(my_header.link,to_gem(tar_rec.header.linkname));
X		my_header.size=from_oct(12,tar_rec.header.size);
X		switch(my_header.linkflag)
X		{
X		case LF_OLDNORMAL:
X		case LF_NORMAL:
X		case LF_CHR:
X		case LF_BLK:
X		case LF_FIFO:
X		case LF_CONTIG:
X		{
X			FILE *fout;
X			int n;
X			Makedirs(my_header.name);
X			if((fout=fopen(my_header.name,"wb"))==NULL)
X			{
X				fprintf(stderr,"detar:Can't open %s\n",
X					my_header.name);
X				exit(3);
X			}
X			while(my_header.size >0)
X			{
X				fread(tar_rec.charptr,1,RECORDSIZE,ftar);
X				n=(my_header.size>RECORDSIZE)?RECORDSIZE:
X					(int)my_header.size;
X				fwrite(tar_rec.charptr,1,n,fout);
X				my_header.size = my_header.size - RECORDSIZE;
X				
X			}
X			fclose(fout);
X			break;
X		}
X		case LF_LINK:
X		case LF_SYMLINK:
X			new_link=(struct link_q *)malloc(sizeof(struct link_q));
X			new_link->prev=link_list;
X			strcpy(new_link->name,my_header.name);
X			strcpy(new_link->link,my_header.link);
X			link_list=new_link;
X			break;
X		case LF_DIR:
X			mkdir(my_header.name);
X			break;
X		}
X	}
X	do
X	{
X		todo_link=NULL;	
X		while(link_list)
X		{
X			struct stat stbuf;
X
X			new_link=link_list;
X			link_list=new_link->prev;
X			if(stat(new_link->link,&stbuf))
X			{
X				new_link->prev=todo_link;
X				todo_link=new_link;
X			}
X			else
X			{
X				FILE *fin,*fout;
X				int n;
X				Makedirs(new_link->name);
X				if((fin=fopen(new_link->link,"rb"))==NULL)
X				{
X					fprintf(stderr,
X					"Detar:Cant open %s for copying\n",
X						new_link->link);
X					exit(3);
X				}
X				if((fout=fopen(new_link->name,"wb"))==NULL)
X				{
X					fprintf(stderr,
X					"Detar:Can't open %s to copy to\n",
X						new_link->name);
X					exit(3);
X				}
X				for(n=RECORDSIZE;n;)
X				{
X					n=fread(tar_rec.charptr,1,RECORDSIZE,
X						fin);
X					fwrite(tar_rec.charptr,1,n,fout);
X				}
X				fclose(fin);
X				fclose(fout);
X				free(new_link);
X			}
X		}
X		link_list=todo_link;
X	}while(todo_link);
X}
XSHAR_EOF
Xchmod +x 'dodetar.c'
Xfi # end of overwriting check
Xif test -f 'misc.c'
Xthen
X	echo shar: will not over-write existing file "'misc.c'"
Xelse
Xcat << \SHAR_EOF > 'misc.c'
X#include <ctype.h>
X#include "detar.h"
X#define isodigit(x) (isdigit(x) && ((x)<'8'))
X
Xvoid bcopy(src,dest,n)char *src,*dest;int n;
X{
X	while(n--)
X		*dest++ = *src++;
X}
X
Xlong from_oct(digs,where)int digs;char *where;
X{
X	long ans=0;
X
X	while(isspace(*where) && digs)
X		digs--,where++;
X	if(!digs) return 0L;
X
X	while(isodigit(*where) && digs)
X	{
X		ans = ans *8 +(*where++ - '0');
X		digs--;
X	}
X	return ans;
X}
X
Xint calc_chksum(tar_rec) union record *tar_rec;
X{
X	long sum,recsum;
X	int i;
X	char *p;
X
X	p=tar_rec->charptr;
X	recsum=from_oct(8,tar_rec->header.chksum);
X	sum=0L;
X	for(i=sizeof(*tar_rec);--i>=0;)
X		sum = sum + (*p++ & 0xff);
X	for(i=sizeof(tar_rec->header.chksum);--i>=0;)
X		sum = sum - (0xff & tar_rec->header.chksum[i]);
X	sum = sum +' '*sizeof(tar_rec->header.chksum);
X	if(sum==recsum) return 1;
X	if(sum==8*' ') return 2;
X	return 0;
X}
X
Xvoid Makedirs(name)char *name;
X{
X	struct stat stbuf;
X	char *p,*q,*index();
X	int makeit;
X
X	p=name;
X	while(q=index(p,'\\'))
X	{
X		*q='\0';
X		makeit=0;
X		if(stat(name,&stbuf))
X			makeit=1;
X		else if((stbuf.st_mode & S_IFMT) != S_IFDIR)		
X			makeit=1;
X		if(makeit)
X			mkdir(name);
X		*q='\\';
X		p=q+1;
X	}
X}
X		
X	
XSHAR_EOF
Xchmod +x 'misc.c'
Xfi # end of overwriting check
Xif test -f 'togem.c'
Xthen
X	echo shar: will not over-write existing file "'togem.c'"
Xelse
Xcat << \SHAR_EOF > 'togem.c'
Xextern char *rindex(),*index();
X
Xvoid bzero(p,n)char *p;int n;
X{
X	while(n--)
X		*p++='\0';
X}
X
Xstatic char *do_one(s)char *s;
X{
X	static char work[16];
X	char *p,*q;
X
X	bzero(work,16);
X
X	if(p=rindex(s,'.'))
X	{
X		*p='\0';
X		strncpy(work,s,8);
X		for(q=work;*q;q++)
X			if(*q=='.') *q='_';
X		*p='.';
X		strncat(work,p,4);
X	}else
X		strncpy(work,s,8);
X	return work;
X}
X
Xchar *to_gem(unix)char *unix;
X{
X	static char gem[100];
X	char *p,*q;
X
X	bzero(gem,100);
X	p=unix;
X	while(q=index(p,'/'))
X	{
X		*q='\0';
X		strcat(gem,do_one(p));
X		strcat(gem,"\\");
X		*q='/';
X		p=q+1;
X	}
X	strcat(gem,do_one(p));
X}
X
X
X#ifdef TEST
Xmain(ac,av)int ac;char *av[];
X{
X	printf("Unix: %s\nGem:  %s\n",av[1],to_gem(av[1]));
X}
X#endif
XSHAR_EOF
Xchmod +x 'togem.c'
Xfi # end of overwriting check
X#	End of shell archive
Xexit 0
SHAR_EOF
chmod 0600 dtar.shr || echo "restore of dtar.shr fails"
sed 's/^X//' << 'SHAR_EOF' > stftw.shr &&
X#! /bin/sh
X# This is a shell archive, meaning:
X# 1. Remove everything above the #! /bin/sh line.
X# 2. Save the resulting text in a file.
X# 3. Execute the file with /bin/sh (not csh) to create the files:
X#	alphasor.c
X#	ftw.c
X#	ftw.h
X#	ftw.man
X#	ndir.c
X#	ndir.h
X#	ndir.man
X#	scandir.c
X#	scandir.man
X# This archive created: Mon Feb  8 14:17:43 1988
Xexport PATH; PATH=/bin:$PATH
Xif test -f 'alphasor.c'
Xthen
X	echo shar: will not over-write existing file "'alphasor.c'"
Xelse
Xcat << \SHAR_EOF > 'alphasor.c'
X/*
X**  ALPHASORT
X**  Trivial sorting predicate for scandir; puts entries in alphabetical order.
X*/
X#include <sys/types.h>
X#include <sys/dir.h>
X#ifdef	RCSID
Xstatic char RCS[] = "$Header: alphasort.c,v 1.1 87/12/29 21:35:59 rsalz Exp $";
X#endif	/* RCSID */
X
X/* A convenient shorthand. */
Xtypedef struct direct	 ENTRY;
X
Xint
Xalphasort(d1, d2)
X    ENTRY	**d1;
X    ENTRY	**d2;
X{
X    return(strcmp(d1[0]->d_name, d2[0]->d_name));
X}
XSHAR_EOF
Xchmod +x 'alphasor.c'
Xfi # end of overwriting check
Xif test -f 'ftw.c'
Xthen
X	echo shar: will not over-write existing file "'ftw.c'"
Xelse
Xcat << \SHAR_EOF > 'ftw.c'
X/*
X**  FTW
X**  Walk a directory hierarchy from a given point, calling a user-supplied
X**  function at each thing we find.  If we go below a specified depth,
X**  recycle file descriptors.
X*/
X#include <stdio.h>
X
X#ifdef MWC
X#include <types.h>
X#include <stat.h>
X#include "ndir.h"
X#include "ftw.h"
X#define SEP '\\'
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <ftw.h>
X#define SEP '/'
X#endif
X
X#ifdef	RCSID
Xstatic char RCS[] = "$Header: ftw.c,v 1.1 87/12/29 21:38:52 rsalz Exp $";
X#endif	/* RCSID */
X
X/* Handy shorthand. */
X#define EQ(a, b)	(strcmp((a), (b)) == 0)
X
X/* Linked in later. */
Xextern char		*malloc();
Xextern char		*strcpy();
X
X
Xint
Xftw(directory, funcptr, depth)
X    char		 *directory;
X    int			(*funcptr)();
X    int			  depth;
X{
X    register DIR	 *Dp;
X    register char	 *p;
X    register int	  i;
X    struct direct	 *E;
X    struct stat		  Sb;
X    long		  seekpoint;
X    char		 *fullpath;
X
X    /* If can't stat, tell the user so. */
X    if (stat(directory, &Sb) < 0)
X	return((*funcptr)(directory, &Sb, FTW_NS));
X
X    /* If it's not a directory, call the user's function. */
X    if ((Sb.st_mode & S_IFMT) != S_IFDIR)
X	/* Saying "FTW_F" here is lying, what if this is a symlink? */
X	return((*funcptr)(directory, &Sb, FTW_F));
X
X    /* Open directory; and if we can't tell the user so. */
X    if ((Dp = opendir(directory)) == NULL)
X	return((*funcptr)(directory, &Sb, FTW_DNR));
X
X    /* See if user wants to go further. */
X    if (i = (*funcptr)(directory, &Sb, FTW_D)) {
X	closedir(Dp);
X	return(i);
X    }
X
X    /* Get ready to hold the full paths. */
X    i = strlen(directory);
X    if ((fullpath = malloc(i + 1 + MAXNAMLEN + 1)) == NULL) {
X	closedir(Dp);
X	return(-1);
X    }
X    (void)strcpy(fullpath, directory);
X    p = &fullpath[i];
X    if (i && p[-1] != SEP)
X	*p++ = SEP;
X
X    /* Read all entries in the directory.. */
X    while (E = readdir(Dp))
X	if (!EQ(E->d_name, ".") && !EQ(E->d_name, "..")) {
X	    if (depth <= 1) {
X		/* Going too deep; checkpoint and close this directory. */
X		seekpoint = telldir(Dp);
X		closedir(Dp);
X		Dp = NULL;
X	    }
X
X	    /* Process the file. */
X	    (void)strcpy(p, E->d_name);
X	    if (i = ftw(fullpath, funcptr, depth - 1)) {
X		/* User's finished; clean up. */
X		free(fullpath);
X		if (Dp)
X		    closedir(Dp);
X		return(i);
X	    }
X
X	    /* Reopen the directory if necessary. */
X	    if (Dp == NULL) {
X		if ((Dp = opendir(directory)) == NULL) {
X		    /* WTF? */
X		    free(fullpath);
X		    return(-1);
X		}
X		seekdir(Dp, seekpoint);
X	    }
X	}
X
X    /* Clean up. */
X    free(fullpath);
X    closedir(Dp);
X    return(0);
X}
XSHAR_EOF
Xchmod +x 'ftw.c'
Xfi # end of overwriting check
Xif test -f 'ftw.h'
Xthen
X	echo shar: will not over-write existing file "'ftw.h'"
Xelse
Xcat << \SHAR_EOF > 'ftw.h'
X/*
X**  <FTW.H>
X**  Header values for the third parameter to the user-supplied function
X**  for ftw().
X**
X**  $Header: ftw.h,v 1.1 87/12/29 21:34:34 rsalz Exp $
X*/
X
X#define FTW_NS		100	/* Something stat(2) failed on		*/
X#define FTW_DNR		200	/* Something opendir(3) failed on	*/
X#define FTW_F		300	/* A normal file			*/
X#define FTW_D		400	/* A directory				*/
XSHAR_EOF
Xchmod +x 'ftw.h'
Xfi # end of overwriting check
Xif test -f 'ftw.man'
Xthen
X	echo shar: will not over-write existing file "'ftw.man'"
Xelse
Xcat << \SHAR_EOF > 'ftw.man'
X	NAME  ftw - walk a file tree
X
X	SYNOPSIS
X
X#include <ftw.h>
X
Xint
Xftw(directory, funcptr, depth)
X	char *directory;
X	int (*funcptr)();
X	int depth;
X
X#include <stat.h>
X
Xint
Xfuncptr(item, sb, flag)
X	char *item;
X	struct stat *sb;
X	int flag;
X
X
X	DESCRIPTION
X
X	Ftw walks through the directory tree starting from the indicated path .
XFor every entry it finds in the tree, it calls the user-supplied funcptr with 
Xthe calling sequence given in the synopsis above.  The first argument is the 
Xfull pathname of the entry (rooted from the directory parameter given to ftw );
Xthe second argument is a pointer to the stat (2) structure for the entry;
Xand the third argument is one of the #define's in the header file.
XThis value will be one of the following:
X
X	FTW_F	Item is a normal file
X	FTW_D	Item is a directory
X	FTW_NS	The stat failed on the item
X	FTW_DNR	Item is a directory which can't be read
X
X
XNote, however, that FTW_F is a misnomer; anything other than directories
Xare (e.g., symbolic links) get the FTW_F tag.
X
X	Ftw recursively calls itself when it encounters a directory. To avoid 
Xusing up all a program's file descriptors, the depth argument specifies the 
Xnumber of simultaneous open directories to maintain.  When the depth is 
Xexceeded, the routine will become noticeably slower because directories are 
Xclosed in ``most-recently-used'' order.
X
X	To stop the tree walk, the user-supplied function should return a
Xnon-zero value; this value will become the return value of ftw .  Otherwise,
Xftw will continue until it has scanned the entire tree, in which case it will
Xreturn zero, or until it hits an error such as a malloc (3) failure, in which 
Xcase it will return -1.
X
X	Because ftw uses dynamic data structures, the only safe way to exit 
Xout of a tree walk is to return a non-zero value.  To handle interrupts, for 
Xexample, mark that the interrupt occured and return a non-zero value, don't 
Xuse longjmp (3) unless the program is going to terminate.
X
X	SEE ALSO
X
X	stat(2)
XSHAR_EOF
Xchmod +x 'ftw.man'
Xfi # end of overwriting check
Xif test -f 'ndir.c'
Xthen
X	echo shar: will not over-write existing file "'ndir.c'"
Xelse
Xcat << \SHAR_EOF > 'ndir.c'
X#define DEF_NDIR
X#include <stat.h>
X#include <osbind.h>
X#include "ndir.h"
Xextern char *malloc();
X#define MALLOC(x) (x *)malloc(sizeof(x))
X
XDIR *opendir(name)char *name;
X{
X
X	DIR *directory;
X	int status;
X	char *p;
X
X	if((directory=MALLOC(DIR))==NULL)
X		return NULL;
X	
X	directory->FileFlags=D_FIRST;
X	
X	/* parse and construct full path name */
X	if(index(name,':')!=NULL)
X		strcpy(directory->FilePath,name);
X	else
X	{
X		status=Dgetdrv();
X		directory->FilePath[0]=status+'A';
X		directory->FilePath[1]=':';
X		Dgetpath(directory->FilePath+2,status+1);
X		strcat(directory->FilePath,"\\");
X		strcat(directory->FilePath,name);
X	}
X	
X	for(p=directory->FilePath;*p;p++);
X		if(*p=='/') *p='\\';
X	if(index(directory->FilePath,'*') || index(directory->FilePath,'?'))
X		;
X	else
X	{
X		p=directory->FilePath;
X		if(p[strlen(p)-1])
X			strcat(p,"\\*.*");
X		else
X			strcat(p,"*.*");
X	}
X	directory->FileDirect.d_ino=0;
X	return directory;
X}
X
Xstatic struct direct *getdent(dir)DIR *dir;
X{
X	struct direct *d_ans= &(dir->FileDirect);
X
X	strcpy(d_ans->d_name,dir->FileName);
X	d_ans->d_namlen=strlen(d_ans->d_name);
X	d_ans->d_reclen=sizeof(struct direct);
X	d_ans->d_ino++;
X	return d_ans;
X}
X
Xstruct direct *readdir(dir)DIR *dir;
X{
X	int res;
X	DMABUFFER *hold;
X
X	struct direct *d_struct= &(dir->FileDirect);
X	hold=Fgetdta();
X	Fsetdta(dir);
X	if(dir->FileFlags & D_FIRST)
X	{
X		res=Fsfirst(dir->FilePath,0xff);
X		dir->FileFlags &= ~(D_FIRST);
X	}
X	else
X		res=Fsnext();
X	Fsetdta(hold);
X	if(!res)
X		return getdent(dir);
X	else return NULL;
X}
X
Xvoid closedir(dir)DIR *dir;
X{
X	free(dir);
X}
X
Xvoid rewinddir(dir)DIR *dir;
X{
X	dir->FileFlags=D_FIRST;
X	dir->FileDirect.d_ino=0;
X}
X
Xvoid seekdir(dir,where)DIR *dir;long where;
X{
X	struct direct *w;
X	rewind(dir);
X	while(dir->FileDirect.d_ino!=(where-1))
X		w=readdir(dir);
X}
X
Xlong telldir(dir)DIR *dir;
X{
X	return (long)(dir->FileDirect.d_ino);
X}
XSHAR_EOF
Xchmod +x 'ndir.c'
Xfi # end of overwriting check
Xif test -f 'ndir.h'
Xthen
X	echo shar: will not over-write existing file "'ndir.h'"
Xelse
Xcat << \SHAR_EOF > 'ndir.h'
X/* @(#)ndir.h	1.4	4/16/85 */
X/* Modified for the atari st series 5/4/87 */
X#ifndef DEV_BSIZE
X#define	DEV_BSIZE	512
X#endif
X#define DIRBLKSIZ	DEV_BSIZE
X#define	MAXNAMLEN	255
X
Xstruct	direct {
X	long	d_ino;			/* inode number of entry */
X	short	d_reclen;		/* length of this record */ 
X	short	d_namlen;		/* length of string in d_name */
X	char	d_name[MAXNAMLEN + 1];	/* name must be no longer than this */
X};
X
X/*
X * The DIRSIZ macro gives the minimum record length which will hold
X * the directory entry.  This requires the amount of space in struct direct
X * without the d_name field, plus enough space for the name with a terminating
X * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
X */
X
X#ifdef DIRSIZ
X#undef DIRSIZ
X#endif
X#define DIRSIZ(dp) \
X    ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
X
X/*
X * Definitions for library routines operating on directories.
X */
X
X/* Structure of a directory entry under GEMDOS */
X
Xtypedef struct _dirdesc
X  {
X    char	Reserved[21];
X    char	FileAttr;
X    int		CreateTime;
X    int		CreateDate;
X    long	FileSize;
X    char	FileName[14];
X    char	FilePath[256];
X    int		FileFlags;
X    struct direct FileDirect;
X  } DIR;
X
X#ifndef NULL
X#define NULL 0L
X#endif
X/* values of FileFlags */
X#define D_NONE 0x00
X#define D_FIRST 0x01
X
X#ifndef DEF_NDIR
Xextern	DIR *opendir();
Xextern	struct direct *readdir();
Xextern	void closedir();
Xextern  void seekdir();
Xextern long telldir();
Xextern void rewinddir();
X#endif
X
XSHAR_EOF
Xchmod +x 'ndir.h'
Xfi # end of overwriting check
Xif test -f 'ndir.man'
Xthen
X	echo shar: will not over-write existing file "'ndir.man'"
Xelse
Xcat << \SHAR_EOF > 'ndir.man'
X
X	NAME ndir - portable directory reading routines
X
X	SUMMARY
X
X	#include <ndir.h>
X
X	
X	DIR *opendir(name)
X		char *name;
X
X	struct direct *readdir(dirp)
X		DIR *dirp;
X
X	void closedir(dirp);
X		DIR *dirp;
X
X	void seekdir(dirp,where)
X		DIR *dirp;
X		long where;
X
X	long telldir(dirp);
X		DIR *dirp;
X
X
X	Opendir() opens a dirctory for reading and returns a pointer to a DIR 
Xfor this directory.
X
X	readdir() reads the next entry in the directory and returns a pointer 
Xto a struct direct for this readding.
X
X	closedir() closes the directory.
X
X	seekdir() sets directory read to a given place in the directory.
X
X	telldir() returns the place of the next read in the directory.
X
X	struct direct is defined in the header file as:
X
X	struct direct
X	{
X		long d_ino;		/* inode number of entry */
X		short d_reclen;		/* length of this record */
X		short d_namlen;		/* length of the string d_name */
X		char d_name[MAXNAMLEN+1];/*basename -must not be longer than 
X						MAXNAMLEN */
X	};
XSHAR_EOF
Xchmod +x 'ndir.man'
Xfi # end of overwriting check
Xif test -f 'scandir.c'
Xthen
X	echo shar: will not over-write existing file "'scandir.c'"
Xelse
Xcat << \SHAR_EOF > 'scandir.c'
X/*
X**  SCANDIR
X**  Scan a directory, collecting all (selected) items into a an array.
X*/
X#ifdef MWC
X#include <types.h>
X#include "ndir.h"
X#else
X#include <sys/types.h>
X#include <sys/dir.h>
X#endif
X
X#ifdef	RCSID
Xstatic char RCS[] = "$Header: scandir.c,v 1.1 87/12/29 21:35:56 rsalz Exp $";
X#endif	/* RCSID */
X
X/* Initial guess at directory size. */
X#define INITIAL_SIZE	20
X
X/* A convenient shorthand. */
Xtypedef struct direct	 ENTRY;
X
X/* Linked in later. */
Xextern char		*malloc();
Xextern char		*realloc();
Xextern char		*strcpy();
X
X
Xint
Xscandir(Name, List, Selector, Sorter)
X    char		  *Name;
X    ENTRY		***List;
X    int			 (*Selector)();
X    int			 (*Sorter)();
X{
X    register ENTRY	 **names;
X    register ENTRY	  *E;
X    register DIR	  *Dp;
X    register int	   i;
X    register int	   size;
X
X    /* Get initial list space and open directory. */
X    size = INITIAL_SIZE;
X    if ((names = (ENTRY **)malloc(size * sizeof names[0])) == NULL
X     || (Dp = opendir(Name)) == NULL)
X	return(-1);
X
X    /* Read entries in the directory. */
X    for (i = 0; E = readdir(Dp); )
X	if (Selector == NULL || (*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 == NULL) {
X		    closedir(Dp);
X		    return(-1);
X		}
X	    }
X
X	    /* Copy the entry. */
X	    if ((names[i - 1] = (ENTRY *)malloc(DIRSIZ(E))) == 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] = 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}
XSHAR_EOF
Xchmod +x 'scandir.c'
Xfi # end of overwriting check
Xif test -f 'scandir.man'
Xthen
X	echo shar: will not over-write existing file "'scandir.man'"
Xelse
Xcat << \SHAR_EOF > 'scandir.man'
X
X	NAME scandir, alphasort - scan a directory
X
X	SYNOPSIS
X
X#include <types.h>
X#include <ndir.h>
X
Xint
Xscandir(name, list, selector, sorter)
X	char *name;
X	struct direct ***list;
X	int (*selector)();
X
X
Xint (*sorter)();
X
X
Xint
Xalphasort(d1, d2)
X	struct direct **d1;
X	struct direct **d2;
X
X	DESCRIPTION
X
X	Scandir reads the directory name and builds a NULL\-terminated array 
Xof pointers to the entries found in that directory.  This array is put into the
Xlocation pointed to by the list parameter.
X
X	If the selector parameter is non-NULL, it is taken to be a pointer to 
Xa function called with each entry, to determine whether or not it should be included in
Xthe returned list.  If the parameter is NULL, all entries are included.
X
X	As an added feature, the entries can be sorted (with qsort (3))
Xbefore the list is returned.  If the sorter parameter is non-NULL, it is passed 
Xto qsort to use as the comparison function.  The alphasort routine is provided 
Xto sort the array alphabetically.
X
X	The array pointed to by list and the items it points to are all space 
Xobtained through malloc (3), and their storage can be reclaimed as shown in the 
Xexample below.
X
X	"EXAMPLE"
X
X	Here is a small ls (1)\-like program:
X
X#include <stdio.h>
X#include <types.h>
X#include <stat.h>
X#include <ndir.h>
X
Xextern int alphasort();
X
Xstatic int
Xfilesonly(e)
X	struct direct *e;
X{
X	struct stat sb;
X
X	return(stat(e->d_name, &sb) >= 0 && (sb.st_mode & S_IFMT) == S_IFREG);
X}
X
Xmain(ac, av)
X	int ac;
X	char *av[];
X{
X	register int i;
X	register int j;
X	struct direct **list;
X
X	if (ac != 2) {
X		fprintf(stderr, "usage: %s dirname\n", av[0]);
X		exit(1);
X	}
X	if (chdir(av[1]) < 0) {
X		perror(av[1]);
X		exit(1);
X	}
X	if ((i = scandir(".", &list, filesonly, alphasort)) < 0) {
X		perror("Error reading directory");
X		exit(1);
X	}
X	for (j = 0; j < i; j++)
X		printf("%s\n", list[j]->d_name);
X	for (j = 0; j < i; j++)
X		free((char *)list[j]);
X	free((char *)list);
X	exit(0);
X}
X
X
X	"SEE ALSO"
X	ndir(3), qsort(3)
X
X	DIAGNOSTICS
X
X	Returns the number of entries in the ``list,'' or -1 if the directory
Xcould not be opened or a memory allocation failed.
X
X
X	BUGS
X	The routine can be slightly wasteful of space.
XSHAR_EOF
Xchmod +x 'scandir.man'
Xfi # end of overwriting check
X#	End of shell archive
Xexit 0
SHAR_EOF
chmod 0600 stftw.shr || echo "restore of stftw.shr fails"
exit 0