[comp.sys.apple] Aztec C65 directory library

gwyn@BRL.ARPA (05/28/87)

install


			INSTALLATION INSTRUCTIONS


The following instructions are specifically for Manx Software Systems Aztec C
running under ProDOS (C65 V3.20B).  This package should be adaptable to other
ProDOS C environments with relatively little trouble.  Installation should be
done only by someone who is comfortable with modifying the standard C library
and header files.  I assume that you have environment variables CLIB and
INCLUDE set up to refer to the system library and header directories.

DISCLAIMER:  Although I believe the code and procedures described here to be
correct, I make no warranty of any kind, and you are advised to perform your
own careful testing before making any substantial change like this to your
programming environment.


0)  Unpack the archive in an empty subdirectory; assuming you saved it (minus
    any header and trailer added by mail systems) in a file named "gwyn.arc":

	$ arcv gwyn.arc

    If this does not work, use a text editor to hack apart the files; the
    divisions and filenames should be obvious.

1)  Install the header files:

	$ unlock $INCLUDE
	$ cp dirent.h $INCLUDE
	$ mkdir $INCLUDE/sys
	$ cp sys.dir.h $INCLUDE/sys/dir.h
	$ cp sys.dirent.h $INCLUDE/sys/dirent.h
	$ cp sys.types.h $INCLUDE/sys/types.h

2)  Update the C libraries:

	$ unlock $CLIB/c.lib $CLIB/ci.lib
	$ cc closedir.c
	$ cc getdents.c
	$ cc opendir.c
	$ cc readdir.c
	$ cc rewinddir.c
	$ cc seekdir.c
	$ cc telldir.c
	$ echo *.o >xlist
	$ ord xlist olist
	$ rm xlist
	$ lb $CLIB/c.lib -b+ -f olist
	$ rm *.o olist
	$ cci closedir.c
	$ cci getdents.c
	$ cci opendir.c
	$ cci readdir.c
	$ cci rewinddir.c
	$ cci seekdir.c
	$ cci telldir.c
	$ echo *.i >xlist
	$ ord xlist ilist
	$ rm xlist
	$ lb $CLIB/ci.lib -b+ -f ilist
	$ rm *.i ilist

3)  To verify installation, try compiling, linking, and running the program
    testdir.c:

	$ cc testdir.c
	$ ln testdir.o -lc
	$ rm testdir.o

    This program searches the current directory "." for each file named as a
    program argument and prints `"FOO" found.' or `"FOO" not found.' where
    FOO is of course replaced by the name being sought in the directory.
    Try something like

	$ testdir foo testdir bar dirent.h xyzzy

    which should produce the output

	"foo" not found.
	"testdir" found.
	"bar" not found.
	"dirent.h" found.
	"xyzzy" not found.

    This program does not test the seekdir() and telldir() functions.

4)  I have also included the freebie utility, listdir.c.  This program acts
    like a plain vanilla "ls ." (one entry per line).  Build it by invoking:

	$ cc listdir.c
	$ ln listdir.o -lc
	$ rm listdir.o

    Feel free to hack on this but please DON'T send me your improvements!

5)  The file "notes" explains what this package is all about.  I posted the
    UNIX manual entries (usage documentation) in an earlier separate message.

6)  In case of difficulty, you can TRY to reach me by e-mail, but I don't
    guarantee I'll respond (it will depend on the message volume).  Remember
    that this is free software and you shouldn't expect much support at these
    prices.	- Gwyn@BRL.MIL
notes


NOTES FOR NEARLY-POSIX-COMPATIBLE C LIBRARY DIRECTORY-ACCESS ROUTINES


Older UNIX C libraries lacked support for reading directories, so historically
programs had knowledge of UNIX directory structure hard-coded into them.  When
Berkeley changed the format of directories for 4.2BSD, it became necessary to
change programs to work with the new structure.  Fortunately, Berkeley
designed a small set of directory access routines to encapsulate knowledge of
the new directory format so that user programs could deal with directory
entries as an abstract data type.  (Unfortunately, they didn't get it quite
right.)  The interface to these routines was nearly independent of the
particular implementation of directories on any given UNIX system; this has
become a particularly important requirement with the advent of heterogeneous
network filesystems such as NFS.  The availability of C for the Apple II has
prompted me to extend this package beyond the UNIX domain; the system-
independent interface works well for this environment also.

It has consequently become possible to write portable applications that search
directories by restricting all directory access to use these new interface
routines.  The sources supplied here are a total rewrite of Berkeley's code,
incorporating ideas from a variety of sources and conforming as closely to
published standards as possible, and are in the PUBLIC DOMAIN to encourage
their widespread adoption.  They support access to Apple ProDOS directories
by exploiting an emulation of the SVR3 getdents() system call, which attains
commonality with the generic UNIX implementation at the cost of slightly more
overhead than absolutely necessary.  These routines should be added to the
standard (UNIX-compatible) C library on all Apple II systems, and all existing
and future applications should be changed to use this interface.  Once this is
done (and similar effort expended in the UNIX community), there should be no
portability problems between UNIX and ProDOS due to differences in underlying
directory structures.  (When porting your applications to UNIX systems, you
can always carry the corresponding UNIX package around with you.)

An additional benefit of these routines is that they buffer directory input,
which provides improved access speed over raw read()s of one entry at a time.

One annoying compatibility problem has arisen along the way, namely that the
original Berkeley interface used the same name, struct direct, for the new
data structure as had been used for the original UNIX filesystem directory
record structure.  This name was changed by the IEEE 1003.1 (POSIX) Working
Group to "struct dirent" and was picked up for SVR3 under the new name; it is
also the name used in this portable package.  I believe it is necessary to
bite the bullet and adopt the new non-conflicting name.  Code using a 4.2BSD-
compatible package needs to be slightly revised to work with this new package,
as follows:
	Change
		#include <ndir.h>	/* Ninth Edition UNIX */
	or
		#include <sys/dir.h>	/* 4.2BSD */
	or
		#include <dir.h>	/* BRL System V emulation */
	to
		#include <sys/types.h>	/* if not already #included */
		#include <dirent.h>

	Change
		struct direct
	to
		struct dirent

	Change
		(anything)->d_namlen
	to
		strlen( (anything)->d_name )

There is a minor compatibility problem in that the closedir() function was
originally defined to have type void, but IEEE 1003.1 changed this to type
int, which is what this implementation supports (even though I disagree with
the change).  However, the difference does not affect most applications.

Another minor problem is that IEEE 1003.1 defined the d_name member of a
struct dirent to be an array of maximum length; this does not permit use of
compact variable-length entries directly from a directory block buffer.  This
part of the specification is incompatible with efficient use of the getdents()
system call, and I have therefore chosen to follow the SVID specification
instead of IEEE 1003.1 (which I hope is changed for the final-use standard).
This deviation should have little or no impact on sensibly-coded applications,
since the relevant d_name length is that given by strlen(), not the declared
array size.

Error handling is not completely satisfactory, due to the variety of possible
failure modes in a general setting.  For example, the rewinddir() function
might fail, but there is no good way to indicate this.  I have tried to
follow the specifications in IEEE 1003.1 and the SVID as closely as possible,
but there are minor deviations in this regard.  Applications should not rely
too heavily on exact failure mode semantics.

Please do not change the new standard interface in any way, as that would
defeat the major purpose of this package!  (It's okay to alter the internal
implementation if you really have to.)

Installation instructions can be found in the file named "install".

This implementation is provided by:

	Douglas A. Gwyn
	U.S. Army Ballistic Research Laboratory
	SLCBR-VL-V
	Aberdeen Proving Ground, MD 21005-5066

	(301)278-6647

	Gwyn@BRL.MIL

This is UNSUPPORTED, use-at-your-own-risk, free software in the public domain.
However, I would appreciate hearing of any actual bugs you find in this
implementation and/or any improvements you come up with.
dirent.h
/*
	<dirent.h> -- definitions for SVR3 directory access routines
		(Aztec C65 ProDOS version)

	last edit:	23-May-1987	D A Gwyn

	Prerequisite:	<sys/types.h>
*/

#include	<sys/dirent.h>

#define	DIRBUF		364		/* buffer size for fs-indep. dirs */

typedef struct
	{
	int	dd_fd;			/* file descriptor */
	int	dd_loc;			/* offset in block */
	int	dd_size;		/* amount of valid data */
	char	*dd_buf;		/* -> directory block */
	}	DIR;			/* stream data from opendir() */

extern DIR		*opendir();
extern struct dirent	*readdir();
extern off_t		telldir();
extern void		seekdir();
extern void		rewinddir();
extern int		closedir();

#ifndef NULL
#define	NULL	0			/* DAG -- added for convenience */
#endif
sys.dir.h
/*
	<sys/dir.h> -- definitions for ProDOS directories

	last edit:	23-May-1987	D A Gwyn

	Reference:	Apple IIGS ProDOS 16 Reference Manual

	A directory consists of some number of blocks of DIRBLKSIZ bytes each.

	Each DIRBLKSIZ-byte block contains two daddr_t block linkage pointers
	followed by entries_per_block file entry structures, except that the
	first entry in the first block is a directory header entry instead.
	All entries (including the header) are entry_length bytes long.

	There are file_count active entries in the directory; inactive entries
	have 0 for their storage_type/name_length byte.

	prerequisite:	<sys/types.h>
*/

#define	DIRBLKSIZ	512		/* size of directory block */

#define	MAXNAMELEN	15		/* maximum filename length */
/* NOTE:  not MAXNAMLEN, which has been preempted by SVR3 <dirent.h> */

struct direct				/* directory entry data from read() */
	{
	unsigned char	d_namlen;	/* storage_type << 4 | name_length */
#define	NAMELENMASK	0x0F		/* mask to isolate name_length */
#define	STSHIFT		4		/* shift to isolate storage_type */
#define	ST_SEEDLING	0x1		/* seedling file */
#define	ST_SAPLING	0x2		/* sapling file */
#define	ST_TREE		0x3		/* tree file */
#define	ST_PASCAL	0x4		/* Pascal area */
#define	ST_SUBDIR	0xD		/* subdirectory */
#define	ST_SUBKEY	0xE		/* subdirectory key block */
#define	ST_VOLKEY	0xF		/* volume directory key block */
	char		d_name[MAXNAMELEN];	/* space-padded filename */
	/* next four members valid for file entry only */
	unsigned char	d_filtyp;	/* file_type */
	daddr_t		d_keyptr;	/* key_pointer */
	unsigned short	d_blused;	/* blocks_used */
	unsigned char	d_eof[3];	/* 3-byte EOF */
	Pdate_t		d_cdate;	/* create_date */
	Ptime_t		d_ctime;	/* create_time */
	unsigned char	d_ver;		/* version */
	unsigned char	d_minver;	/* min_version */
	union	{
		struct	{		/* file entry only */
			unsigned char	f_access;	/* access */
			unsigned short	f_auxtyp;	/* aux_type */
			Pdate_t		f_mdate;	/* mod_date */
			Ptime_t		f_mtime;	/* mod_time */
			daddr_t		f_hdrptr;	/* header_pointer */
			}	f;
		struct	{		/* volume or subdirectory only */
			unsigned char	vs_access;	/* access */
			unsigned char	vs_entlen;	/* entry_length */
			unsigned char	vs_eperbl;	/* entries_per_block */
			unsigned short	vs_filcnt;	/* file_count */
			union	{
				struct	{	/* volume directory only */
					daddr_t		v_bitmap;
						/* bit_map_pointer */
					unsigned short	v_blocks;
						/* total_blocks */
					}	v;
				struct	{	/* subdirectory only */
					daddr_t		s_parptr;
						/* parent_pointer */
					unsigned char	s_parnum;
						/* parent_entry_number */
					unsigned char	s_parlen;
						/* parent_entry_length */
					}	s;
				}	u;
			}	vs;
		}	u;
	};
sys.dirent.h
/*
	<sys/dirent.h> -- file system independent directory entry (SVR3)
		(Aztec C65 ProDOS version)

	last edit:	23-May-1987	D A Gwyn

	prerequisite:	<sys/types.h>
*/

struct dirent				/* data from getdents()/readdir() */
	{
	long		d_ino;		/* key block number of entry */
	off_t		d_off;		/* offset of disk directory entry */
	unsigned short	d_reclen;	/* length of this record */
	char		d_name[1];	/* name of file */	/* non-POSIX */
	};

#define	DIRENTBASESIZ		(sizeof(long) + sizeof(off_t) \
				+ sizeof(unsigned short))	/* Aztec C65 */
#define	DIRENTSIZ( namlen )	((DIRENTBASESIZ + sizeof(long) + (namlen)) \
				/ sizeof(long) * sizeof(long))

/* DAG -- the following was moved from <dirent.h>, which was the wrong place */
#define	MAXNAMLEN	128		/* maximum filename length */

#ifndef NAME_MAX
#define	NAME_MAX	(MAXNAMLEN - 1)	/* DAG -- added for POSIX */
#endif
sys.types.h
/*
	<sys/types.h> -- primitive system data types for ProDOS

	last edit:	22-May-1987	D A Gwyn
*/

typedef	unsigned short	daddr_t;	/* disk block address */
typedef	char		*caddr_t;	/* byte address */
typedef unsigned long	time_t;		/* time in seconds */
typedef long		off_t;		/* file position offset */

typedef	unsigned short	Pdate_t;	/* ProDOS date format */
typedef unsigned short	Ptime_t;	/* ProDOS time format */
closedir.c
/*
	closedir -- close a directory stream
		(Aztec C65 ProDOS version)

	last edit:	23-May-1987	D A Gwyn
*/

#include	<errno.h>
#include	<sys/types.h>
#include	<dirent.h>

typedef void	*pointer;

extern void	free();
extern int	close();

int
closedir( dirp )
	register DIR	*dirp;		/* stream from opendir() */
	{
	if ( dirp == NULL || dirp->dd_buf == NULL )
		{
		errno = EFAULT;
		return -1;		/* invalid pointer */
		}

	free( (pointer)dirp->dd_buf );
	free( (pointer)dirp );
	return close( dirp->dd_fd );
	}
opendir.c
/*
	opendir -- open a directory stream
		(Aztec C65 ProDOS version)

	last edit:	24-May-1987	D A Gwyn
*/

#include	<errno.h>
#include	<fcntl.h>
#include	<sys/types.h>
#include	<dirent.h>

typedef void	*pointer;

extern void	free();
extern pointer	malloc();
extern int	getdents(), open(), close();

DIR *
opendir( dirname )
	char		*dirname;	/* name of directory */
	{
	register DIR	*dirp;		/* -> malloc'ed storage */
	register int	fd;		/* file descriptor for read */

	if ( (fd = open( dirname, O_RDONLY )) < 0 )
		return NULL;		/* errno set by open() */

	if ( (dirp = (DIR *)malloc( sizeof(DIR) )) == NULL
	  || (dirp->dd_buf = (char *)malloc( (unsigned)DIRBUF )) == NULL
	   )	{
		if ( dirp != NULL )
			free( (pointer)dirp );

		(void)close( fd );
		errno = ENOMEM;
		return NULL;		/* not enough memory */
		}

	dirp->dd_fd = fd;
	dirp->dd_loc = 0;		/* no need to skip header */

	/* Special for ProDOS: check header entry rather than fstat()ing */

	if ( (dirp->dd_size = getdents( fd, dirp->dd_buf, (unsigned)DIRBUF ))
		<= 0		/* EOF or error */
	  || ((struct dirent *)dirp->dd_buf)->d_ino != -1L	/* kludge */
	   )	{
		(void)closedir( dirp );
		errno = ENOTDIR;
		return NULL;		/* not a directory */
		}

	return dirp;
	}
readdir.c
/*
	readdir -- read next entry from a directory stream
		(Aztec C65 ProDOS version)

	last edit:	24-May-1987	D A Gwyn
*/

#include	<errno.h>
#include	<sys/types.h>
#include	<dirent.h>

extern int	getdents();		/* SVR3 system call emulation */

struct dirent *
readdir( dirp )
	register DIR		*dirp;	/* stream from opendir() */
	{
	register struct dirent	*dp;	/* -> directory data */

	if ( dirp == NULL || dirp->dd_buf == NULL )
		{
		errno = EFAULT;
		return NULL;		/* invalid pointer */
		}

	do	{
		if ( dirp->dd_loc >= dirp->dd_size )	/* empty or obsolete */
			dirp->dd_loc = dirp->dd_size = 0;

		if ( dirp->dd_size == 0	/* need to refill buffer */
		  && (dirp->dd_size =
			getdents( dirp->dd_fd, dirp->dd_buf, (unsigned)DIRBUF )
		     ) <= 0
		   )
			return NULL;	/* EOF or error */

		dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc];
		dirp->dd_loc += dp->d_reclen;
		}
	while ( dp->d_ino <= 0L );	/* skip header, empty slots */

	return dp;
	}
rewinddir.c
/*
	rewinddir -- rewind a directory stream
		(Aztec C65 ProDOS version)

	last edit:	24-May-1987	D A Gwyn

	This is not simply a call to seekdir(), because seekdir()
	will use the current buffer whenever possible and we need
	rewinddir() to forget about buffered data.
*/

#include	<errno.h>
#include	<sys/types.h>
#include	<dirent.h>

extern off_t	lseek();

#ifndef SEEK_SET
#define	SEEK_SET	0
#endif

void
rewinddir( dirp )
	register DIR		*dirp;	/* stream from opendir() */
	{
	if ( dirp == NULL || dirp->dd_buf == NULL )
		{
		errno = EFAULT;
		return;			/* invalid pointer */
		}

	(void)lseek( dirp->dd_fd, (off_t)0, SEEK_SET );	/* may set errno */
	dirp->dd_size = getdents( dirp->dd_fd, dirp->dd_buf, (unsigned)DIRBUF );
	dirp->dd_loc = 0;		/* no need to skip header */
	}
seekdir.c
/*
	seekdir -- reposition a directory stream
		(Aztec C65 ProDOS version)

	last edit:	24-May-1987	D A Gwyn

	An unsuccessful seekdir() will in general alter the current
	directory position; beware.
*/

#include	<errno.h>
#include	<sys/types.h>
#include	<dirent.h>

extern off_t	lseek();

#ifndef SEEK_SET
#define	SEEK_SET	0
#endif

typedef int	bool;			/* Boolean data type */
#define	false	0
#define	true	1

void
seekdir( dirp, loc )
	register DIR	*dirp;		/* stream from opendir() */
	register off_t	loc;		/* position from telldir() */
	{
	register bool	rewind;		/* "start over when stymied" flag */

	if ( dirp == NULL || dirp->dd_buf == NULL )
		{
		errno = EFAULT;
		return;			/* invalid pointer */
		}

	/* Monotonicity of d_off is heavily exploited in the following. */

	/* This algorithm is tuned for modest directory sizes.  For
	   huge directories, it might be more efficient to read blocks
	   until the first d_off is too large, then back up one block,
	   or even to use binary search on the directory blocks.  I
	   doubt that the extra code for that would be worthwhile. */

	if ( dirp->dd_loc >= dirp->dd_size	/* invalid index */
	  || ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off > loc
					/* too far along in buffer */
	   )
		dirp->dd_loc = 0;	/* reset to beginning of buffer */
	/* else save time by starting at current dirp->dd_loc */

	for ( rewind = true; ; )
		{
		register struct dirent	*dp;

		/* See whether the matching entry is in the current buffer. */

		if ( (dirp->dd_loc < dirp->dd_size	/* valid index */
		   || readdir( dirp ) != NULL	/* next buffer read */
		   && (dirp->dd_loc = 0, true)	/* beginning of buffer set */
		     )
		  && (dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off
			<= loc		/* match possible in this buffer */
		   )	{
			for ( /* dp initialized above */ ;
			      (char *)dp < &dirp->dd_buf[dirp->dd_size];
			      dp = (struct dirent *)((char *)dp + dp->d_reclen)
			    )
				if ( dp->d_off == loc )
					{	/* found it! */
					dirp->dd_loc =
						(char *)dp - dirp->dd_buf;
					return;
					}

			rewind = false;	/* no point in backing up later */
			dirp->dd_loc = dirp->dd_size;	/* set end of buffer */
			}
		else			/* whole buffer past matching entry */
			if ( !rewind )
				{	/* no point in searching further */
				errno = EINVAL;
				return;	/* no entry at specified loc */
				}
			else	{	/* rewind directory and start over */
				rewind = false;	/* but only once! */

				dirp->dd_loc = dirp->dd_size = 0;

				if ( lseek( dirp->dd_fd, (off_t)0, SEEK_SET )
					!= 0
				   )
					return;	/* errno already set (EBADF) */

				if ( loc == 0 )
					return; /* save time */
				}
		}
	}
telldir.c
/*
	telldir -- report directory stream position
		(Aztec C65 ProDOS version)

	last edit:	24-May-1987	D A Gwyn
*/

#include	<errno.h>
#include	<sys/types.h>
#include	<dirent.h>

extern off_t	lseek();

#ifndef SEEK_CUR
#define	SEEK_CUR	1
#endif

off_t
telldir( dirp )				/* return offset of next entry */
	DIR	*dirp;			/* stream from opendir() */
	{
	if ( dirp == NULL || dirp->dd_buf == NULL )
		{
		errno = EFAULT;
		return -1;		/* invalid pointer */
		}

	if ( dirp->dd_loc < dirp->dd_size )	/* valid index */
		return ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off;
	else				/* beginning of next directory block */
		return lseek( dirp->dd_fd, (off_t)0, SEEK_CUR );
	}
getdents.c
/*
	getdents -- get directory entries in a file system independent format
			(SVR3 system call emulation) (Aztec C65 ProDOS version)

	last edit:	25-May-1987	D A Gwyn
*/

#include	<errno.h>
#include	<sys/types.h>
#include	<sys/dir.h>
#include	<sys/dirent.h>

#define	RecLen( dp )	(sizeof(struct direct))	/* fixed-length entries */

extern int	read();
#define	GetBlock( fd, buf, n )	read( fd, buf, (unsigned)n )

extern int	tolower();
extern off_t	lseek();

#ifndef NULL
#define	NULL	0
#endif

#ifndef SEEK_CUR
#define	SEEK_CUR	1
#endif

int
getdents( fildes, buf, nbyte )		/* returns # bytes read;
					   0 on EOF, -1 on error */
	int			fildes;	/* directory file descriptor */
	char			*buf;	/* where to put the (struct dirent)s */
	unsigned		nbyte;	/* size of buf[] */
	{
	off_t			offset;	/* initial directory file offset */
	union	{
		char		dblk[DIRBLKSIZ];
					/* directory file block buffer */
		struct direct	dummy;	/* just for alignment */
		}	u;		/* (avoids having to malloc()) */
	register struct direct	*dp;	/* -> u.dblk[.] */
	register struct dirent	*bp;	/* -> buf[.] */

	if ( buf == NULL )	/* no alignment constraint for Aztec C65 */
		{
		errno = EFAULT;		/* invalid pointer */
		return -1;
		}

	if ( (offset = lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
		return -1;		/* errno set by lseek() */

	for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
		{			/* convert next directory block */
		int	size;

		if ( (size = GetBlock( fildes, u.dblk, DIRBLKSIZ )) <= 0 )
			return size;	/* EOF or error (EBADF) */

		for ( dp = (struct direct *)(u.dblk + 2 * sizeof(daddr_t));
					/* skip block linkage pointers */
		      (char *)dp < &u.dblk[size];
		      dp = (struct direct *)((char *)dp + RecLen( dp ))
		    )	{
			if ( dp->d_namlen != 0 )
				{	/* non-empty; copy to user buffer */
				register int	reclen =
					DIRENTSIZ( dp->d_namlen & NAMELENMASK );

				if ( (char *)bp + reclen > &buf[nbyte] )
					{
					errno = EINVAL;
					return -1;	/* buf too small */
					}

				switch ( dp->d_namlen >> STSHIFT )
					{
				case ST_VOLKEY:
				case ST_SUBKEY:
					bp->d_ino = -1L;	/* kludge */
					break;

				default:
					bp->d_ino = dp->d_keyptr;
					break;
					}

				bp->d_off = offset + ((char *)dp - u.dblk);
				bp->d_reclen = reclen;

				/* translate filename to lower case */
				{
					register char	*bnp, *dnp;
					register int	i;

					bnp = bp->d_name;
					dnp = dp->d_name;

					for ( i = 0; i < dp->d_namlen; ++i )
						*bnp++ = tolower( *dnp++ );

					for ( i += DIRENTBASESIZ;
					      i < reclen;
					      ++i
					    )	/* NUL padding */
						*bnp++ = '\0';
				}

				bp = (struct dirent *)((char *)bp + reclen);
				}
			}
		}

	return (char *)bp - buf;	/* return # bytes read */
	}
testdir.c
/*
	testdir -- basic test for C library directory access routines

	last edit:	25-Apr-1987	D A Gwyn
*/

#include	<sys/types.h>
#include	<stdio.h>
#include	<dirent.h>

extern void	exit();
extern int	strcmp();

main( argc, argv )
	int			argc;
	register char		**argv;
	{
	register DIR		*dirp;
	register struct dirent	*dp;
	int			nerrs = 0;	/* total not found */

	if ( (dirp = opendir( "." )) == NULL )
		{
		(void)fprintf( stderr, "Cannot open \".\" directory\n" );
		exit( 1 );
		}

	while ( --argc > 0 )
		{
		++argv;

		while ( (dp = readdir( dirp )) != NULL )
			if ( strcmp( dp->d_name, *argv ) == 0 )
				{
				(void)printf( "\"%s\" found.\n", *argv );
				break;
				}

		if ( dp == NULL )
			{
			(void)printf( "\"%s\" not found.\n", *argv );
			++nerrs;
			}

		rewinddir( dirp );
		}

	(void)closedir( dirp );
	exit( nerrs );
	}
listdir.c
/*
	listdir -- basic test for C library directory access routines

	last edit:	22-May-1987	D A Gwyn
*/

#include	<sys/types.h>
#include	<stdio.h>
#include	<dirent.h>

extern void	exit();
extern int	strcmp();

main( argc, argv )
	int			argc;
	register char		**argv;
	{
	register DIR		*dirp;
	register struct dirent	*dp;
	int			nerrs = 0;	/* total not found */

	if ( (dirp = opendir( "." )) == NULL )
		{
		(void)fprintf( stderr, "Cannot open \".\" directory\n" );
		exit( 1 );
		}

	while ( (dp = readdir( dirp )) != NULL )
		(void)printf( "%s\n", dp->d_name );

	(void)closedir( dirp );
	exit( nerrs );
	}