[comp.os.minix] directory

ncpascal@ndsuvax.UUCP (Freeman Pascal) (12/17/87)

Hello,
    Here I am again.  This time I have the directory(3) library calls.
I have followed the BSD description as closely as the BSD 4.3 PRM allows
me to (some entries have so little info in them).  I hope they can be
of some use.

				N O T E
				-------

     I would not suggest using these routines if you already have a tried
and tested set of directory functions that work for you.  Use these as a
last resort (I have such confidence in my code).  I know the readdir(),
opendir(), and closedir() work properly.  Although, I had to make some
assumptions on how to implement telldir() and seekdir().  I basicly decided
to pass and returned what was needed for the lseek() call.  I hope this
was correct. If anyone knows the correct specs on these calls or know
where to point me to please do.  Enjoy.

				Freeman P. Pascal IV
				ncpascal@ndsuvax

# /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  INSTALL Makefile dir.c dir.h makelibc tst.c
# Wrapped by ncpascal@ndsuvax on Wed Dec 16 22:27:00 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f INSTALL -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"INSTALL\"
else
echo shar: Extracting \"INSTALL\" \(1907 characters\)
sed "s/^X//" >INSTALL <<'END_OF_INSTALL'
XManifest:
X--------
X	INSTALL		this file
X	Makefile	makefile for tty
X	makelibc	shell script to make C library
X	tst.c		test file for directory(3)
X	dir.c		library function to return tty name (see directory(3))
X
XInstallation instructions:
X-------------------------
X
X1.  install dir.h in /usr/include/sys.
X
X2.  Add "typedef unshort ino_t" or "#define ino_t inode_nr" to
X    /usr/include/sys/types.h depending on your thinking how this
X    declaration should be handled.
X
X3.  Compile dir.c as a library routine and place in /usr/lib/libc.a.
X    If you are using the makelibc shell script that was posted quite 
X    awhile back just append "ar av libc.a dir.s" to the beginning and run.
X    I am including my version if you don't have it.
X
X4.  Run makefile to compile tst.c and run it to test directory(3).
X
X5.  Enjoy
X
XNotes:
X-----
X
X    I'm not sure on the exact operations performed by telldir() and 
Xseekdir().  I set up telldir() to return the current position (long int
Xlike seek()) within the opened directory.  Seekdir() was set up likewise,
Xit will set the file postion within the opened directory without regard
Xif it entry boundries or if the entry is active (d_ino entry is non-zero).
X
X    In all actuality, I would suggest that if you use the POSIX version of
Xdirectory(3) that came across the net lately.  At the time I wrote these 
Xroutines I was attempting to get the BSD version of arc to work under MINIX.
XI am now leaning towards using the POSIX implementation.  I am including 
Xthese only for the sake of completeness for my scandir(3) routines I'm also
Xposting.
X
X    I must also state I don't garentee that theses routines work correctly.
XI have tested them and found them to work as I need them to.  I would 
Xsuggest you run them through your own tests to decide if they work for
Xyour own purposes.  (Gee - I have such confidence in my own code :-)
X
X				Freeman P. Pascal IV
X				ncpascal@ndsuvax
END_OF_INSTALL
if test 1907 -ne `wc -c <INSTALL`; then
    echo shar: \"INSTALL\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(125 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
XCFLAGS=-T. -i
X
Xall:	tst dir.s
X
Xtst:	tst.s dir.s
X	@cc -o tst $(CFLAGS) tst.s dir.s
X	@echo "Done."
X
Xtst.s:	tst.c
X
Xdir.s:	dir.c
END_OF_Makefile
if test 125 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f dir.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"dir.c\"
else
echo shar: Extracting \"dir.c\" \(3423 characters\)
sed "s/^X//" >dir.c <<'END_OF_dir.c'
X/*
X *	dirs.c	- BSD 4.x compatible directory functions
X *
X *	Freeman P. Pascal IV
X */
X/*
X *  History:
X *
X *	04 Dec 87	fpp	Creation
X */
X#include	<errno.h>
X#include	<sys/types.h>
X#include	<sys/dir.h>
X#include	<sys/stat.h>
X#include	<sys/file.h>
X
X#ifndef VOID
X#define	VOID	void
X#endif
X
X#ifndef	PUBLIC
X#define PUBLIC
X#endif
X
X#ifndef	NULL
X#define	NULL	0
X#endif
X
X#define	loop	while( 1 )	/* loop forever */
X
Xstruct direct		__dir;
Xextern	int	errno;
X
X/*========================================================================*\
X**				opendir()				  **
X\*========================================================================*/
XPUBLIC DIR *
Xopendir( path )
Xchar 	*path;
X{
X/*
X *	Open a directory for reading
X */
X  struct stat	sp;
X  DIR		*dirp;
X
X  if (( dirp = (DIR *) malloc( sizeof( DIR ))) == (DIR *) NULL ) {
X	errno = ENOMEM;
X	return( NULL );
X  }
X  if (( dirp->dd_fd = open( path, O_RDONLY )) == -1  ) {
X#ifdef	DIR_DEBUG
X	perror( "opendir() (open)" );
X#endif
X	return( NULL );
X  }
X  if ( fstat( dirp->dd_fd, &sp ) == -1 ) {
X#ifdef	DIR_DEBUG
X	perror( "opendir() (fstat)" );
X#endif
X	return( NULL );
X  }
X  dirp->dd_loc = 0;
X  return( dirp );
X}
X
X/*========================================================================*\
X**				readdir()				  **
X\*========================================================================*/
XPUBLIC struct direct *
Xreaddir( dirp )
Xregister DIR	*dirp;
X{
X/*
X *	Read and return the next directory structure 
X *	referenced by *dirp
X */
X  register struct direct	*dp;
X
X  loop {
X	if (dirp->dd_loc == 0) {
X		dirp->dd_size = read( dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ );
X		if (dirp->dd_size <= 0)
X			return( NULL );
X	}
X	if (dirp->dd_loc >= dirp->dd_size) {
X		dirp->dd_loc = 0;		/* reload buffer */
X		continue;
X	}
X	dp = (struct direct *) (dirp->dd_buf + dirp->dd_loc);
X	dirp->dd_loc += sizeof( struct direct );
X	if (dp->d_ino == 0) 
X		continue;			/* skip empty entries */
X	__dir.d_ino = dp->d_ino;
X	strncpy( __dir.d_name, dp->d_name, DIRSIZ );
X	__dir.d_name[ DIRSIZ ] = '\0';
X	return( &__dir );
X  }
X}
X  
X/*========================================================================*\
X**				telldir()				  **
X\*========================================================================*/
XPUBLIC long 
Xtelldir( dirp )
XDIR	*dirp;
X{
X/*
X * 	Return current position within directory stream.
X */
X return( lseek( dirp->dd_fd, 0L, L_INCR ));
X}
X
X/*========================================================================*\
X**				seekdir()				  **
X\*========================================================================*/
XPUBLIC int
Xseekdir( dirp, loc )
XDIR	*dirp;
Xlong	loc;
X{
X/*
X *	Set position within directory stream.
X */
X  long	pos;
X
X  dirp->dd_loc = 0;		/* force readdir() read into dd_buf */
X  return( lseek( dirp->dd_fd, loc, L_SET ));
X}
X
X/*========================================================================*\
X**				rewinddir()				  **
X\*========================================================================*/
XPUBLIC int
Xrewinddir( dirp )
XDIR	*dirp;
X{
X/*
X *	Resets the position of the directory stream to the beginning.
X */
X return( seekdir( dirp, 0L ));
X}
X
X/*========================================================================*\
X**				closedir()				  **
X\*========================================================================*/
XPUBLIC int
Xclosedir( dirp )
XDIR	*dirp;
X{
X/*
X *	Close given directory and free memory used by *dirp
X */
X  close( dirp->dd_fd );
X  free(( char * ) dirp );
X  return( 0 );
X}
X  	
END_OF_dir.c
if test 3423 -ne `wc -c <dir.c`; then
    echo shar: \"dir.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f dir.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"dir.h\"
else
echo shar: Extracting \"dir.h\" \(741 characters\)
sed "s/^X//" >dir.h <<'END_OF_dir.h'
X/*
X *  dir.h
X *
X *  Minix directory structures
X *
X */
X/*
X *  History:
X *
X *	16 Dec  87	fpp	Changed type of d_ino in direct structure
X *				from "inode_nr" (from orginal types.h)
X *				to "ino_t" (added to types.h for compatibl-
X *				ity)
X *	29 July 87	fpp	Creation
X */
X#define	DIRSIZ		14
X#define	DIRBLKSIZ	512		/* read this much in at a time	*/
X 
Xstruct	direct {
X  ino_t		d_ino;			/* inode of file		*/
X  char		d_name[ DIRSIZ ];	/* file name			*/
X};
X
Xtypedef struct _dirdesc
X{
X  int		dd_fd;			/* directory file descriptor	*/
X  long		dd_loc;			/* current location in dd_buf	*/
X  long		dd_size;		/* size of last read		*/
X  char		dd_buf[ DIRBLKSIZ ];	/* read buffer 			*/
X  
X} DIR;
X
XDIR		*opendir();
Xstruct direct	*readdir();
Xlong		telldir();
X
END_OF_dir.h
if test 741 -ne `wc -c <dir.h`; then
    echo shar: \"dir.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f makelibc -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"makelibc\"
else
echo shar: Extracting \"makelibc\" \(2138 characters\)
sed "s/^X//" >makelibc <<'END_OF_makelibc'
X#
X#  makelibc - Make C library
X#
X#				- NOTE -
X#
X#  This shell script will -REMOVE- the old version of libc.a if it
X#  exists in the current directory.  It will also -REPLACE- the old
X#  /usr/lib/libc.a with version just packaged.
X#
Xif (test -f ./libc.a)		# remove old libc.a if it exists
X	then rm ./libc.a
Xfi
Xar av libc.a getwd.s rename.s			# getwd(3) rename(2)
Xar av libc.a dir.s scandir.s			# directory(3), scandir(3)
Xar av libc.a getgrp.s				# process groups
Xar av libc.a termcap.s gtty.s stty.s		# v1.2 update
Xar av libc.a popen.s ctime.s system.s qsort.s	# v1.2 upgrade
Xar av libc.a regexp.s regsub.s
Xar av libc.a getopt.s getgrent.s getpwent.s crypt.s
Xar av libc.a fdopen.s
Xar av libc.a fgets.s fprintf.s fputs.s fread.s freopen.s fclose.s
Xar av libc.a fopen.s fseek.s ftell.s fwrite.s gets.s scanf.s getc.s printdat.s
Xar av libc.a fflush.s setbuf.s sprintf.s doprintf.s putc.s ungetc.s strcmp.s
Xar av libc.a access.s chdir.s chmod.s chown.s chroot.s creat.s dup.s dup2.s
Xar av libc.a exec.s exit.s cleanup.s fork.s isatty.s fstat.s getegid.s getenv.s
Xar av libc.a geteuid.s getgid.s getpass.s close.s getuid.s ioctl.s kill.s
Xar av libc.a link.s lseek.s malloc.s brk.s brk2.s brksize.s mknod.s mktemp.s
Xar av libc.a getpid.s mount.s open.s perror.s pipe.s prints.s read.s setgid.s
Xar av libc.a setuid.s sleep.s alarm.s pause.s signal.s catchsig.s stat.s
Xar av libc.a stime.s strcat.s strcpy.s strlen.s strncat.s strncmp.s strncpy.s
Xar av libc.a ftime.s
Xar av libc.a sync.s time.s times.s umask.s umount.s unlink.s utime.s wait.s
Xar av libc.a stderr.s write.s syslib.s call.s atoi.s message.s sendrec.s
Xar av libc.a printk.s abort.s itoa.s stb.s abs.s atol.s ctype.s index.s bcopy.s
Xar av libc.a getutil.s rand.s rindex.s adi.s and.s cii.s cms.s cmu4.s com.s
Xar av libc.a csa2.s csb2.s cuu.s .dup.s dvi.s dvi4.s dvu.s dvu4.s exg.s fakfp.s
Xar av libc.a gto.s iaar.s ilar.s inn.s ior.s isar.s lar2.s loi.s mli.s mli4.s
Xar av libc.a ngi.s nop.s rck.s rmi.s rmi4.s rmu.s rmu4.s rol.s ror.s sar2.s
Xar av libc.a sbi.s set.s sli.s sri.s sti.s xor.s error.s unknown.s trp.s
Xar av libc.a setjmp.s
X
Xcp libc.a /usr/lib
Xecho
Xecho "Done."
Xecho
X
END_OF_makelibc
if test 2138 -ne `wc -c <makelibc`; then
    echo shar: \"makelibc\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f tst.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"tst.c\"
else
echo shar: Extracting \"tst.c\" \(765 characters\)
sed "s/^X//" >tst.c <<'END_OF_tst.c'
X#include	<sys/types.h>
X#include	<sys/dir.h>
X
X#define NULL		(DIR *) 0
X#define	PRDIR( dp )	printf( "%14s\t%d\n", dp->d_name, dp->d_ino )
X
Xmain()
X{
X  DIR		*dirp;
X  struct direct *dp;
X
X  if (( dirp = opendir( "." ) ) == NULL ) {
X	perror( "\".\"" );
X	exit( 1 );
X  }
X  while(( dp = readdir( dirp )) != NULL ) 
X	PRDIR( dp );
X
X  prints( "\nseekdir: " );
X  seekdir( dirp, 45 ); dp = readdir( dirp ); PRDIR( dp );
X
X  printf( "\ntelldir():  %20s  %D\n", dp->d_name, telldir( dirp ));
X
X  prints( "\nrewind(): " );
X  rewinddir( dirp ); dp = readdir( dirp ); PRDIR( dp );
X   
X  printf( "\ntelldir():  %20s  %D\n", dp->d_name, telldir( dirp ));
X
X  prints( "\nseekdir: " );
X  seekdir( dirp, 4 * sizeof( struct direct ));
X  dp = readdir( dirp ); PRDIR( dp );
X
X  closedir( dirp );
X}
END_OF_tst.c
if test 765 -ne `wc -c <tst.c`; then
    echo shar: \"tst.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 1 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

tholm@uvicctr.UUCP (Terrence W. Holm) (12/13/88)

EFTH MINIX report #62  - December 1988 -  directory(3)


This posting includes Douglas Gwyn's POSIX-compatible directory
routines. If you have been using his routines for a while, please
tell me what changes you have found necessary, so that I can keep
a current copy of the POSIX library.

If you have not been using Doug's library, then this is your chance
to install it!  Please find below the 13 files:

    HEADERS

	/usr/include/dirent.h
	/usr/include/sys/dir.h
	/usr/include/sys/dirent.h

    LIBRARY

	closedir.c
	getdents.c
	opendir.c
	readdir.c
	rewinddir.c
	seekdir.c
	telldir.c

    MAN PAGES
	getdents.3
	directory.3
	dirent.4



Instructions,

    1) Install the header files, link in sys/errno.h.

    2) Put the man pages under /usr/man/cat3 and cat4.

    3) Compile and install the library routines. Note that "seekdir.s"
       must precede "readdir.s", which must precede "getdents.s" in
       libc.a

----------------------------------------------------------
echo x - directory.3
gres '^X' '' > directory.3 << '/'
XSUBROUTINES
X    directory(3)	- directory operations
X
XINVOCATION
X    #include <sys/types.h>
X    #include <dirent.h>
X
X    DIR *opendir( dirname )
X      char *dirname;
X
X    struct dirent *readdir( dirp )
X      DIR *dirp;
X
X    off_t telldir( dirp )
X      DIR *dirp;
X
X    void seekdir( dirp, loc )
X      DIR *dirp;
X      off_t loc;
X
X    void rewinddir( dirp )
X      DIR *dirp;
X
X    int closedir( dirp )
X      DIR *dirp;
X
XEXPLANATION
X    Opendir(3) establishes a connection between the directory named
X    by <dirname> and a unique object of type DIR known as a
X    directory stream that it creates. Opendir(3) returns a pointer
X    to be used to identify the directory stream in subsequent
X    operations. A NULL pointer is returned if <dirname> cannot be
X    accessed or is not a directory, or if opendir(3) is unable to
X    create the DIR object (perhaps due to insufficient  memory).
X
X    Readdir(3) returns a pointer to an internal structure containing
X    information about the next active directory entry. No inactive
X    entries are reported. The internal structure may be overwritten
X    by another operation on the same directory stream; the amount of
X    storage needed to hold a copy of the internal structure is given
X    by the value of a macro, DIRENTSIZ(strlen(direntp->d_name)), not
X    by sizeof(struct dirent) as one might expect. A NULL pointer is
X    returned upon reaching the end of the directory, upon detecting
X    an invalid location in the directory, or upon occurrence of an
X    error while reading the directory.
X
X    Telldir(3) returns the current position associated with the named
X    directory stream for later use as an argument to seekdir(3).
X
X    Seekdir(3) sets the position of the next readdir(3) operation on
X    the named directory stream. The new position reverts to the one
X    associated with the directory stream when the telldir(3) operation
X    from which <loc> was obtained was performed.
X
X    Rewinddir(3) resets the position of the named directory stream to
X    the beginning of the directory. All buffered data for the directory
X    stream is discarded, thereby guaranteeing that the actual file
X    system directory will be referred to for the next readdir(3) on the
X    directory stream.
X
X    Closedir(3) closes the named directory stream; internal resources
X    used for the directory stream are liberated, and subsequent use of
X    the associated DIR object is no longer valid. Closedir(3) returns
X    a value of zero if no error occurs, -1 otherwise.  There are several
X    possible errors that can occur as a result of these operations; the
X    external integer variable <errno> is set to indicate the specific
X    error. (Readdir's detection of the normal end of a directory is not
X    considered to be an error.)
X
XEXAMPLE
X    Sample code which searches the current working directory for
X    an entry. Usage: "a.out directory filename".
X
X    #include <sys/types.h>
X    #include <dirent.h>
X    #include <stdio.h>
X
X    main( argc, argv )
X        int   argc;
X        char *argv[];
X
X        {
X        DIR *dirp;
X        struct dirent *dp;
X
X        if ( (dirp = opendir( argv[1] )) == NULL )
X            {
X            fprintf( stderr, "Cannot open directory\n" );
X            exit( 1 );
X            }
X
X        while ( (dp = readdir( dirp )) != NULL )
X            if ( strcmp( dp->d_name, argv[2] ) == 0 )
X                {
X                (void) closedir( dirp );
X                printf( "Found\n" );
X                exit( 0 );
X                }
X
X        (void) closedir( dirp );
X        printf( "Not found\n" );
X        exit( 1 );
X        }
X
XREFERENCES
X    dirent(4)
X
XWARNINGS
X    The value returned by telldir(3) need not have any simple
X    interpretation and should only be used as an argument to
X    seekdir(3). Similarly, the <loc> argument to seekdir(3) must
X    be obtained from a previous telldir(3) operation on the same
X    directory stream. Telldir(3) and seekdir(3) are unreliable
X    when the directory stream has been closed and reopened. It
X    is best to avoid using telldir(3) and seekdir(3) altogether.
X
X    The exact set of <errno> values and meanings may vary among
X    implementations.
X
X    Because directory entries can dynamically appear and disappear,
X    and because directory contents are buffered by these routines,
X    an application may need to continually rescan a directory to
X    maintain an accurate picture of its active entries.
X
XAUTHOR
X    Douglas A. Gwyn
/
echo x - getdents.3
gres '^X' '' > getdents.3 << '/'
XSUBROUTINES
X    getdents(3)		 - get directory entries
X
XINVOCATION
X    int getdents( fildes, buf, nbyte )
X      int fildes;
X      char *buf;
X      unsigned nbyte;
X  
XEXPLANATION
X    Getdents(3) reads directory entries in a file system independent
X    format. This routine is only used by readdir(3) and should
X    never be used directly by a user's program.
X
XREFERENCES
X    directory(3), dirent(4)
/
echo x - dirent.4
gres '^X' '' > dirent.4 << '/'
XSTRUCTURES
X    dirent(4)		 - file system independent directory entry
X
XEXPLANATION
X    Different file system types may have different directory
X    entries.  The <dirent> structure defines a file system
X    independent directory entry, which contains information
X    common to directory entries in different file system types.
X    A set of these structures is returned by the getdents(3)
X    subroutine. The <dirent> structure is defined below.
X
X      struct  dirent
X	  {
X          long           d_ino;
X          off_t          d_off;
X          unsigned short d_reclen;
X          char           d_name[1];
X          };
X
X    The field d_ino is a number which is unique for each file in
X    the file system. The field d_off represents an offset of that
X    directory entry in the actual file system directory. The field
X    d_name is the beginning of the character array giving the name
X    of the directory entry. This name is null terminated and may
X    have at most {NAME_MAX} characters in addition to the null
X    terminator. This results in file system independent directory
X    entries being variable-length entities. The value of d_reclen
X    is the record length of this entry. This length is defined to
X    be the number of bytes between the beginning of the current entry
X    and the next one, adjusted so that the next entry will start on
X    a long boundary.
X
XFILES
X    /usr/include/sys/dirent.h
X
XREFERENCES
X    getdents(3)
X
XWARNING
X    The field d_off does not have a simple interpretation for
X    some file system types and should not be used directly by
X    applications.
/
echo x - dir.h
gres '^X' '' > dir.h << '/'
X#define	DIRBLKSIZ	512		/* size of directory block */
X
X#ifndef DIRSIZ
X#define	DIRSIZ	14
X#endif
X
Xstruct direct {
X	ino_t	d_ino;
X	char	d_name[DIRSIZ];
X};
/
echo x - dirent.h
gres '^X' '' > dirent.h << '/'
X/*
X	<dirent.h> -- definitions for SVR3 directory access routines
X
X	last edit:	25-Apr-1987	D A Gwyn
X
X	Prerequisite:	<sys/types.h>
X*/
X
X#include	<sys/dirent.h>
X
X#define	DIRBUF		1024		/* buffer size for fs-indep. dirs */
X	/* must in general be larger than the filesystem buffer size */
X
Xtypedef struct
X	{
X	int	dd_fd;			/* file descriptor */
X	int	dd_loc;			/* offset in block */
X	int	dd_size;		/* amount of valid data */
X	char	*dd_buf;		/* -> directory block */
X	}	DIR;			/* stream data from opendir() */
X
Xextern DIR		*opendir();
Xextern struct dirent	*readdir();
Xextern off_t		telldir();
Xextern void		seekdir();
Xextern void		rewinddir();
Xextern int		closedir();
X
X#ifndef NULL
X#define	NULL	0			/* DAG -- added for convenience */
X#endif
/
echo x - sys_dirent.h
gres '^X' '' > sys_dirent.h << '/'
X/*
X	<sys/dirent.h> -- file system independent directory entry (SVR3)
X
X	last edit:	27-Oct-1988	D A Gwyn
X
X	prerequisite:	<sys/types.h>
X*/
X
Xstruct dirent				/* data from getdents()/readdir() */
X	{
X	long		d_ino;		/* inode number of entry */
X	off_t		d_off;		/* offset of disk directory entry */
X	unsigned short	d_reclen;	/* length of this record */
X	char		d_name[1];	/* name of file */	/* non-ANSI */
X	};
X
X#ifdef BSD_SYSV				/* (e.g., when compiling getdents.c) */
Xextern struct dirent	__dirent;	/* (not actually used) */
X/* The following is portable, although rather silly. */
X#define	DIRENTBASESIZ		(__dirent.d_name - (char *)&__dirent.d_ino)
X
X#else
X/* The following nonportable ugliness could have been avoided by defining
X   DIRENTSIZ and DIRENTBASESIZ to also have (struct dirent *) arguments.
X   There shouldn't be any problem if you avoid using the DIRENTSIZ() macro. */
X
X#define	DIRENTBASESIZ		(((struct dirent *)0)->d_name \
X				- (char *)&((struct dirent *)0)->d_ino)
X#endif
X
X#define	DIRENTSIZ( namlen )	((DIRENTBASESIZ + sizeof(long) + (namlen)) \
X				/ sizeof(long) * sizeof(long))
X
X/* DAG -- the following was moved from <dirent.h>, which was the wrong place */
X#define	MAXNAMLEN	512		/* maximum filename length */
X
X#ifndef NAME_MAX
X#define	NAME_MAX	(MAXNAMLEN - 1)	/* DAG -- added for POSIX */
X#endif
/
echo x - closedir.c
gres '^X' '' > closedir.c << '/'
X/*
X	closedir -- close a directory stream
X
X	last edit:	11-Nov-1988	D A Gwyn
X*/
X
X#include	<sys/errno.h>
X#include	<sys/types.h>
X#include	<dirent.h>
X
Xtypedef char	*pointer;		/* (void *) if you have it */
X
Xextern void	free();
Xextern int	close();
X
Xextern int	errno;
X
X#ifndef NULL
X#define	NULL	0
X#endif
X
Xint
Xclosedir( dirp )
X	register DIR	*dirp;		/* stream from opendir() */
X	{
X	register int	fd;
X
X	if ( dirp == NULL || dirp->dd_buf == NULL )
X		{
X		errno = EFAULT;
X		return -1;		/* invalid pointer */
X		}
X
X	fd = dirp->dd_fd;		/* bug fix thanks to R. Salz */
X	free( (pointer)dirp->dd_buf );
X	free( (pointer)dirp );
X	return close( fd );
X	}
/
echo x - getdents.c
gres '^X' '' > getdents.c << '/'
X/*
X	getdents -- get directory entries in a file system independent format
X			(SVR3 system call emulation)
X
X	last edit:	27-Oct-1988	D A Gwyn
X
X	This single source file supports several different methods of
X	getting directory entries from the operating system.  Define
X	whichever one of the following describes your system:
X
X	UFS	original UNIX filesystem (14-character name limit)
X	BFS	4.2BSD (also 4.3BSD) native filesystem (long names)
X	NFS	getdirentries() system call
X
X	Also define any of the following flags that are pertinent:
X
X	ATT_SPEC	check user buffer address for longword alignment
X	BSD_SYSV	BRL UNIX System V emulation environment on 4.nBSD
X	INT_SIGS	<signal.h> thinks that signal handlers have
X			return type int (rather than the standard void)
X	NEG_DELS	deleted entries have inode number -1 rather than 0
X	UNK		have _getdents() system call, but kernel may not
X			support it
X
X	If your C library has a getdents() system call interface, but you
X	can't count on all kernels on which your application binaries may
X	run to support it, change the system call interface name to
X	_getdents() and define "UNK" to enable the system-call validity
X	test in this "wrapper" around _getdents().
X
X	If your system has a getdents() system call that is guaranteed 
X	to always work, you shouldn't be using this source file at all.
X*/
X
X#define  UFS
X
X#include	<sys/errno.h>
X#include	<sys/types.h>
X#ifdef BSD_SYSV
X#include	<sys/_dir.h>		/* BSD flavor, not System V */
X#else
X#include	<sys/dir.h>
X#undef	MAXNAMLEN			/* avoid conflict with SVR3 */
X	/* Good thing we don't need to use the DIRSIZ() macro! */
X#ifdef d_ino				/* 4.3BSD/NFS using d_fileno */
X#undef	d_ino				/* (not absolutely necessary) */
X#else
X#define	d_fileno	d_ino		/* (struct direct) member */
X#endif
X#endif
X#include	<sys/dirent.h>
X#include	<sys/stat.h>
X#ifdef UNK
X#ifndef UFS
X#include "***** ERROR ***** UNK applies only to UFS"
X/* One could do something similar for getdirentries(), but I didn't bother. */
X#endif
X#include	<signal.h>
X#endif
X
X#ifdef BSD_SYSV
Xstruct dirent	__dirent;		/* (just for the DIRENTBASESIZ macro) */
X#endif
X
X#ifdef UFS
X#define	RecLen( dp )	(sizeof(struct direct))	/* fixed-length entries */
X#else	/* BFS || NFS */
X#define	RecLen( dp )	((dp)->d_reclen)	/* variable-length entries */
X#endif
X
X#ifdef NFS
X#ifdef BSD_SYSV
X#define	getdirentries	_getdirentries	/* package hides this system call */
X#endif
Xextern int	getdirentries();
Xstatic long	dummy;			/* getdirentries() needs basep */
X#define	GetBlock( fd, buf, n )	getdirentries( fd, buf, (unsigned)n, &dummy )
X#else	/* UFS || BFS */
X#ifdef BSD_SYSV
X#define read	_read			/* avoid emulation overhead */
X#endif
Xextern int	read();
X#define	GetBlock( fd, buf, n )	read( fd, buf, (unsigned)n )
X#endif
X
X#ifdef UNK
Xextern int	_getdents();		/* actual system call */
X#endif
X
Xextern char	*strncpy();
Xextern int	fstat();
Xextern off_t	lseek();
X
Xextern int	errno;
X
X#ifdef NEG_DELS
X#define	DELETED	(-1)
X#else
X#define	DELETED	0
X#endif
X
X#ifndef DIRBLKSIZ
X#define	DIRBLKSIZ	4096		/* directory file read buffer size */
X#endif
X
X#ifndef NULL
X#define	NULL	0
X#endif
X
X#ifndef SEEK_CUR
X#define	SEEK_CUR	1
X#endif
X
X#ifndef S_ISDIR				/* macro to test for directory file */
X#define	S_ISDIR( mode )		(((mode) & S_IFMT) == S_IFDIR)
X#endif
X
X#ifdef UFS
X
X/*
X	The following routine is necessary to handle DIRSIZ-long entry names.
X	Thanks to Richard Todd for pointing this out.
X*/
X
Xstatic int
XNameLen( name )				/* return # chars in embedded name */
X	char		name[];		/* -> name embedded in struct direct */
X	{
X	register char	*s;		/* -> name[.] */
X	register char	*stop = &name[DIRSIZ];	/* -> past end of name field */
X
X	for ( s = &name[1];		/* (empty names are impossible) */
X	      *s != '\0'		/* not NUL terminator */
X	   && ++s < stop;		/* < DIRSIZ characters scanned */
X	    )
X		;
X
X	return s - name;		/* # valid characters in name */
X	}
X
X#else	/* BFS || NFS */
X
Xextern int	strlen();
X
X#define	NameLen( name )	strlen( name )	/* names are always NUL-terminated */
X
X#endif
X
X#ifdef UNK
Xstatic enum	{ maybe, no, yes }	state = maybe;
X					/* does _getdents() work? */
X
X#ifdef INT_SIGS
X#define	RET_SIG	int
X#else
X#define	RET_SIG	void
X#endif
X
X/*ARGSUSED*/
Xstatic RET_SIG
Xsig_catch( sig )
X	int	sig;			/* must be SIGSYS */
X	{
X	state = no;			/* attempted _getdents() faulted */
X#ifdef INT_SIGS
X	return 0;			/* telling lies */
X#endif
X	}
X#endif	/* UNK */
X
Xint
Xgetdents( fildes, buf, nbyte )		/* returns # bytes read;
X					   0 on EOF, -1 on error */
X	int			fildes;	/* directory file descriptor */
X	char			*buf;	/* where to put the (struct dirent)s */
X	unsigned		nbyte;	/* size of buf[] */
X	{
X	int			serrno;	/* entry errno */
X	off_t			offset;	/* initial directory file offset */
X	/* The following are static just to keep the stack small. */
X	static struct stat	statb;	/* fstat() info */
X	static union
X		{
X		char		dblk[DIRBLKSIZ
X#ifdef UFS
X				     +1	/* for last entry name terminator */
X#endif
X				    ];
X					/* directory file block buffer */
X		struct direct	dummy;	/* just for alignment */
X		}	u;		/* (avoids having to malloc()) */
X	register struct direct	*dp;	/* -> u.dblk[.] */
X	register struct dirent	*bp;	/* -> buf[.] */
X
X#ifdef UNK
X	if ( state == yes )		/* _getdents() is known to work */
X		return _getdents( fildes, buf, nbyte );
X
X	if ( state == maybe )		/* first time only */
X		{
X		RET_SIG		(*shdlr)();	/* entry SIGSYS handler */
X		register int	retval;	/* return from _getdents() if any */
X
X		shdlr = signal( SIGSYS, sig_catch );
X		retval = _getdents( fildes, buf, nbyte );	/* try it */
X		(void)signal( SIGSYS, shdlr );
X
X		if ( state == maybe )	/* SIGSYS did not occur */
X			{
X			state = yes;	/* so _getdents() must have worked */
X			return retval;
X			}
X		}
X
X	/* state == no; perform emulation */
X#endif
X
X	if ( buf == NULL
X#ifdef ATT_SPEC
X	  || (unsigned long)buf % sizeof(long) != 0	/* ugh */
X#endif
X	   )	{
X		errno = EFAULT;		/* invalid pointer */
X		return -1;
X		}
X
X	if ( fstat( fildes, &statb ) != 0 )
X		return -1;		/* errno set by fstat() */
X
X	if ( !S_ISDIR( statb.st_mode ) )
X		{
X		errno = ENOTDIR;	/* not a directory */
X		return -1;
X		}
X
X	if ( (offset = lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
X		return -1;		/* errno set by lseek() */
X
X#ifdef BFS				/* no telling what remote hosts do */
X	if ( (unsigned long)offset % DIRBLKSIZ != 0 )
X		{
X		errno = ENOENT;		/* file pointer probably misaligned */
X		return -1;
X		}
X#endif
X
X	serrno = errno;			/* save entry errno */
X
X	for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
X		{			/* convert next directory block */
X		int	size;
X
X		do	size = GetBlock( fildes, u.dblk, DIRBLKSIZ );
X		while ( size == -1 && errno == EINTR );
X
X		if ( size <= 0 )
X			return size;	/* EOF or error (EBADF) */
X
X		for ( dp = (struct direct *)u.dblk;
X		      (char *)dp < &u.dblk[size];
X		      dp = (struct direct *)((char *)dp + RecLen( dp ))
X		    )	{
X#ifndef UFS
X			if ( dp->d_reclen <= 0 )
X				{
X				errno = EIO;	/* corrupted directory */
X				return -1;
X				}
X#endif
X
X			if ( dp->d_fileno != DELETED )
X				{	/* non-empty; copy to user buffer */
X				register int	reclen =
X					DIRENTSIZ( NameLen( dp->d_name ) );
X
X				if ( (char *)bp + reclen > &buf[nbyte] )
X					{
X					errno = EINVAL;
X					return -1;	/* buf too small */
X					}
X
X				bp->d_ino = dp->d_fileno;
X				bp->d_off = offset + ((char *)dp - u.dblk);
X				bp->d_reclen = reclen;
X
X				{
X#ifdef UFS
X				/* Is the following kludge ugly?  You bet. */
X
X				register char	save = dp->d_name[DIRSIZ];
X					/* save original data */
X
X				dp->d_name[DIRSIZ] = '\0';
X					/* ensure NUL termination */
X#endif
X				(void)strncpy( bp->d_name, dp->d_name,
X					       reclen - DIRENTBASESIZ
X					     );	/* adds NUL padding */
X#ifdef UFS
X				dp->d_name[DIRSIZ] = save;
X					/* restore original data */
X#endif
X				}
X
X				bp = (struct dirent *)((char *)bp + reclen);
X				}
X			}
X
X		if ( (char *)dp > &u.dblk[size] )
X			{
X			errno = EIO;	/* corrupted directory */
X			return -1;
X			}
X		}
X
X	errno = serrno;			/* restore entry errno */
X	return (char *)bp - buf;	/* return # bytes read */
X	}
/
echo x - opendir.c
gres '^X' '' > opendir.c << '/'
X/*
X	opendir -- open a directory stream
X
X	last edit:	27-Oct-1988	D A Gwyn
X*/
X
X#include	<sys/errno.h>
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#include	<dirent.h>
X
X#ifdef BSD_SYSV
X#define open	_open			/* avoid emulation overhead */
X#endif
X
Xtypedef char	*pointer;		/* (void *) if you have it */
X
Xextern void	free();
Xextern pointer	malloc();
Xextern int	open(), close(), fstat();
X
Xextern int	errno;
X
X#ifndef NULL
X#define	NULL	0
X#endif
X
X#ifndef O_RDONLY
X#define	O_RDONLY	0
X#endif
X
X#ifndef S_ISDIR				/* macro to test for directory file */
X#define	S_ISDIR( mode )		(((mode) & S_IFMT) == S_IFDIR)
X#endif
X
XDIR *
Xopendir( dirname )
X	char			*dirname;	/* name of directory */
X	{
X	register DIR		*dirp;	/* -> malloc'ed storage */
X	register int		fd;	/* file descriptor for read */
X	/* The following is static just to keep the stack small. */
X	static struct stat	sbuf;	/* result of fstat() */
X
X	if ( (fd = open( dirname, O_RDONLY )) < 0 )
X		return NULL;		/* errno set by open() */
X
X	if ( fstat( fd, &sbuf ) != 0 || !S_ISDIR( sbuf.st_mode ) )
X		{
X		(void)close( fd );
X		errno = ENOTDIR;
X		return NULL;		/* not a directory */
X		}
X
X	if ( (dirp = (DIR *)malloc( sizeof(DIR) )) == NULL
X	  || (dirp->dd_buf = (char *)malloc( (unsigned)DIRBUF )) == NULL
X	   )	{
X		register int	serrno = errno;
X					/* errno set to ENOMEM by sbrk() */
X
X		if ( dirp != NULL )
X			free( (pointer)dirp );
X
X		(void)close( fd );
X		errno = serrno;
X		return NULL;		/* not enough memory */
X		}
X
X	dirp->dd_fd = fd;
X	dirp->dd_loc = dirp->dd_size = 0;	/* refill needed */
X
X	return dirp;
X	}
/
echo x - readdir.c
gres '^X' '' > readdir.c << '/'
X/*
X	readdir -- read next entry from a directory stream
X
X	last edit:	25-Apr-1987	D A Gwyn
X*/
X
X#include	<sys/errno.h>
X#include	<sys/types.h>
X#include	<dirent.h>
X
Xextern int	getdents();		/* SVR3 system call, or emulation */
X
Xextern int	errno;
X
X#ifndef NULL
X#define	NULL	0
X#endif
X
Xstruct dirent *
Xreaddir( dirp )
X	register DIR		*dirp;	/* stream from opendir() */
X	{
X	register struct dirent	*dp;	/* -> directory data */
X
X	if ( dirp == NULL || dirp->dd_buf == NULL )
X		{
X		errno = EFAULT;
X		return NULL;		/* invalid pointer */
X		}
X
X	do	{
X		if ( dirp->dd_loc >= dirp->dd_size )	/* empty or obsolete */
X			dirp->dd_loc = dirp->dd_size = 0;
X
X		if ( dirp->dd_size == 0	/* need to refill buffer */
X		  && (dirp->dd_size =
X			getdents( dirp->dd_fd, dirp->dd_buf, (unsigned)DIRBUF )
X		     ) <= 0
X		   )
X			return NULL;	/* EOF or error */
X
X		dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc];
X		dirp->dd_loc += dp->d_reclen;
X		}
X	while ( dp->d_ino == 0L );	/* don't rely on getdents() */
X
X	return dp;
X	}
/
echo x - rewinddir.c
gres '^X' '' > rewinddir.c << '/'
X/*
X	rewinddir -- rewind a directory stream
X
X	last edit:	25-Apr-1987	D A Gwyn
X
X	This is not simply a call to seekdir(), because seekdir()
X	will use the current buffer whenever possible and we need
X	rewinddir() to forget about buffered data.
X*/
X
X#include	<sys/errno.h>
X#include	<sys/types.h>
X#include	<dirent.h>
X
Xextern off_t	lseek();
X
Xextern int	errno;
X
X#ifndef NULL
X#define	NULL	0
X#endif
X
X#ifndef SEEK_SET
X#define	SEEK_SET	0
X#endif
X
Xvoid
Xrewinddir( dirp )
X	register DIR		*dirp;	/* stream from opendir() */
X	{
X	if ( dirp == NULL || dirp->dd_buf == NULL )
X		{
X		errno = EFAULT;
X		return;			/* invalid pointer */
X		}
X
X	dirp->dd_loc = dirp->dd_size = 0;	/* invalidate buffer */
X	(void)lseek( dirp->dd_fd, (off_t)0, SEEK_SET );	/* may set errno */
X	}
/
echo x - seekdir.c
gres '^X' '' > seekdir.c << '/'
X/*
X	seekdir -- reposition a directory stream
X
X	last edit:	24-May-1987	D A Gwyn
X
X	An unsuccessful seekdir() will in general alter the current
X	directory position; beware.
X
X	NOTE:	4.nBSD directory compaction makes seekdir() & telldir()
X		practically impossible to do right.  Avoid using them!
X*/
X
X#include	<sys/errno.h>
X#include	<sys/types.h>
X#include	<dirent.h>
X
Xextern off_t	lseek();
X
Xextern int	errno;
X
X#ifndef NULL
X#define	NULL	0
X#endif
X
X#ifndef SEEK_SET
X#define	SEEK_SET	0
X#endif
X
Xtypedef int	bool;			/* Boolean data type */
X#define	false	0
X#define	true	1
X
Xvoid
Xseekdir( dirp, loc )
X	register DIR	*dirp;		/* stream from opendir() */
X	register off_t	loc;		/* position from telldir() */
X	{
X	register bool	rewind;		/* "start over when stymied" flag */
X
X	if ( dirp == NULL || dirp->dd_buf == NULL )
X		{
X		errno = EFAULT;
X		return;			/* invalid pointer */
X		}
X
X	/* A (struct dirent)'s d_off is an invented quantity on 4.nBSD
X	   NFS-supporting systems, so it is not safe to lseek() to it. */
X
X	/* Monotonicity of d_off is heavily exploited in the following. */
X
X	/* This algorithm is tuned for modest directory sizes.  For
X	   huge directories, it might be more efficient to read blocks
X	   until the first d_off is too large, then back up one block,
X	   or even to use binary search on the directory blocks.  I
X	   doubt that the extra code for that would be worthwhile. */
X
X	if ( dirp->dd_loc >= dirp->dd_size	/* invalid index */
X	  || ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off > loc
X					/* too far along in buffer */
X	   )
X		dirp->dd_loc = 0;	/* reset to beginning of buffer */
X	/* else save time by starting at current dirp->dd_loc */
X
X	for ( rewind = true; ; )
X		{
X		register struct dirent	*dp;
X
X		/* See whether the matching entry is in the current buffer. */
X
X		if ( (dirp->dd_loc < dirp->dd_size	/* valid index */
X		   || readdir( dirp ) != NULL	/* next buffer read */
X		   && (dirp->dd_loc = 0, true)	/* beginning of buffer set */
X		     )
X		  && (dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off
X			<= loc		/* match possible in this buffer */
X		   )	{
X			for ( /* dp initialized above */ ;
X			      (char *)dp < &dirp->dd_buf[dirp->dd_size];
X			      dp = (struct dirent *)((char *)dp + dp->d_reclen)
X			    )
X				if ( dp->d_off == loc )
X					{	/* found it! */
X					dirp->dd_loc =
X						(char *)dp - dirp->dd_buf;
X					return;
X					}
X
X			rewind = false;	/* no point in backing up later */
X			dirp->dd_loc = dirp->dd_size;	/* set end of buffer */
X			}
X		else			/* whole buffer past matching entry */
X			if ( !rewind )
X				{	/* no point in searching further */
X				errno = EINVAL;
X				return;	/* no entry at specified loc */
X				}
X			else	{	/* rewind directory and start over */
X				rewind = false;	/* but only once! */
X
X				dirp->dd_loc = dirp->dd_size = 0;
X
X				if ( lseek( dirp->dd_fd, (off_t)0, SEEK_SET )
X					!= 0
X				   )
X					return;	/* errno already set (EBADF) */
X
X				if ( loc == 0 )
X					return; /* save time */
X				}
X		}
X	}
/
echo x - telldir.c
gres '^X' '' > telldir.c << '/'
X/*
X	telldir -- report directory stream position
X
X	last edit:	25-Apr-1987	D A Gwyn
X
X	NOTE:	4.nBSD directory compaction makes seekdir() & telldir()
X		practically impossible to do right.  Avoid using them!
X*/
X
X#include	<sys/errno.h>
X#include	<sys/types.h>
X#include	<dirent.h>
X
Xextern off_t	lseek();
X
Xextern int	errno;
X
X#ifndef SEEK_CUR
X#define	SEEK_CUR	1
X#endif
X
Xoff_t
Xtelldir( dirp )				/* return offset of next entry */
X	DIR	*dirp;			/* stream from opendir() */
X	{
X	if ( dirp == NULL || dirp->dd_buf == NULL )
X		{
X		errno = EFAULT;
X		return -1;		/* invalid pointer */
X		}
X
X	if ( dirp->dd_loc < dirp->dd_size )	/* valid index */
X		return ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off;
X	else				/* beginning of next directory block */
X		return lseek( dirp->dd_fd, (off_t)0, SEEK_CUR );
X	}
/
exit
----------------------------------------------------------

		Terrence W. Holm
		  uunet!uw-beaver!uvicctr!tholm
		  tholm%uvunix.bitnet
		  tholm@uvicctr.UVic.ca

housel@en.ecn.purdue.edu (Peter S. Housel) (12/17/88)

	There is a problem with these routines as distributed here.
readdir() will always fail in a large directory (approx. 1K or larger).

	The reason is that getdents() can't be allowed to overflow
the per-DIR buffer (which is of size DIRBUF) by reading one directory
block of size DIRBLKSIZ. By default, DIRBLKSIZ is 4096, so it is possible
to read in more directory data in one read() than can be converted in
one step. getdents() cannot return having only converted part of a
directory block. (The comment in dirent.h says that DIRBUF should be
larger than the "filesystem buffer size," but it doesn't explain why.)

	Probably the best solution is to compile getdents.c with a
"-DDIRBLKSIZ=512" compiler flag. This also has the effect of reducing
the memory requirements of programs that call these routines.

	By the way, when and where was this latest version posted? I must
have missed it...

-Peter S. Housel-	housel@en.ecn.purdue.edu	...!pur-ee!housel

tholm@uvicctr.UUCP (Terrence W. Holm) (12/20/88)

In article <7488@ea.ecn.purdue.edu> housel@en.ecn.purdue.edu (Peter S. Housel) writes:
>
>	There is a problem with these routines as distributed here.

OK, did Terrence not test this code well enough before posting?
Or, was Peter so anxious to post, that he didn't install and
test exactly what I posted?


> ... (The comment in dirent.h says that DIRBUF should be
>larger than the "filesystem buffer size," but it doesn't explain why.)

It must be larger because directory entries are expanded to the
system-independent format "struct dirent". For version 7, a 16 byte
directory entry may be expanded to as much as 28 bytes, ie. almost
twice as large.


>	Probably the best solution is to compile getdents.c with a
>"-DDIRBLKSIZ=512" compiler flag.

See /usr/include/sys/dir.h


			Terrence W. Holm
			  uunet!uw-beaver!uvicctr!tholm
			  tholm%uvunix.bitnet
			  tholm@uvicctr.UVic.ca

glenn@extro.ucc.su.oz (G. Geers [ext 3241]) (05/12/89)

Does anyone have a public domain implementation of directory(3). I need it
for minix and for installation on a 68020 box running sysV.2.
			Glenn
glenn@extro.ucc.su.oz