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