home *** CD-ROM | disk | FTP | other *** search
- Path: uunet!rs
- From: rs@uunet.UU.NET (Rich Salz)
- Newsgroups: comp.sources.unix
- Subject: v10i042: Bugfix to Doug Gwyn's portable directory library
- Message-ID: <582@uunet.UU.NET>
- Date: 7 Jul 87 23:23:20 GMT
- Organization: UUNET Communications Services, Arlington, VA
- Lines: 489
- Approved: rs@uunet.uu.net
-
- Mod.sources: Volume 10, Number 42
- Submitted by: Doug Gwyn (VLD/VMB) <gwyn@brl.arpa>
- Archive-name: dir-lib.pch
-
- [ This was recently put out on comp.sources.bugs; it fixes a problem
- in V7 directories when the name is 14 characters long. I think
- Doug would agree with me that Amiga, MS-DOS, Minix, and other ports
- would be a great thing to have... --r$ ]
-
- #!/bin/sh
- # Self-unpacking archive format. To unbundle, sh this file.
- echo 'opendir.c' 1>&2
- cat >'opendir.c' <<'END OF opendir.c'
- /*
- opendir -- open a directory stream
-
- last edit: 16-Jun-1987 D A Gwyn
- */
-
- #include <sys/errno.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <dirent.h>
-
- #ifdef BSD_SYSV
- #define open _open /* avoid emulation overhead */
- #endif
-
- typedef char *pointer; /* (void *) if you have it */
-
- extern void free();
- extern pointer malloc();
- extern int open(), close(), fstat();
-
- extern int errno;
-
- #ifndef NULL
- #define NULL 0
- #endif
-
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #endif
-
- #ifndef S_ISDIR /* macro to test for directory file */
- #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
- #endif
-
- DIR *
- opendir( dirname )
- char *dirname; /* name of directory */
- {
- register DIR *dirp; /* -> malloc'ed storage */
- register int fd; /* file descriptor for read */
- struct stat sbuf; /* result of fstat() */
-
- if ( (fd = open( dirname, O_RDONLY )) < 0 )
- return NULL; /* errno set by open() */
-
- if ( fstat( fd, &sbuf ) != 0 || !S_ISDIR( sbuf.st_mode ) )
- {
- (void)close( fd );
- errno = ENOTDIR;
- return NULL; /* not a directory */
- }
-
- if ( (dirp = (DIR *)malloc( sizeof(DIR) )) == NULL
- || (dirp->dd_buf = (char *)malloc( (unsigned)DIRBUF )) == NULL
- ) {
- register int serrno = errno;
- /* errno set to ENOMEM by sbrk() */
-
- if ( dirp != NULL )
- free( (pointer)dirp );
-
- (void)close( fd );
- errno = serrno;
- return NULL; /* not enough memory */
- }
-
- dirp->dd_fd = fd;
- dirp->dd_loc = dirp->dd_size = 0; /* refill needed */
-
- return dirp;
- }
- END OF opendir.c
- echo 'seekdir.c' 1>&2
- cat >'seekdir.c' <<'END OF seekdir.c'
- /*
- seekdir -- reposition a directory stream
-
- last edit: 24-May-1987 D A Gwyn
-
- An unsuccessful seekdir() will in general alter the current
- directory position; beware.
-
- NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
- practically impossible to do right. Avoid using them!
- */
-
- #include <sys/errno.h>
- #include <sys/types.h>
- #include <dirent.h>
-
- extern off_t lseek();
-
- extern int errno;
-
- #ifndef NULL
- #define NULL 0
- #endif
-
- #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 */
- }
-
- /* A (struct dirent)'s d_off is an invented quantity on 4.nBSD
- NFS-supporting systems, so it is not safe to lseek() to it. */
-
- /* 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 */
- }
- }
- }
- END OF seekdir.c
- echo 'getdents.c' 1>&2
- cat >'getdents.c' <<'END OF getdents.c'
- /*
- getdents -- get directory entries in a file system independent format
- (SVR3 system call emulation)
-
- last edit: 06-Jul-1987 D A Gwyn
-
- This single source file supports several different methods of
- getting directory entries from the operating system. Define
- whichever one of the following describes your system:
-
- UFS original UNIX filesystem (14-character name limit)
- BFS 4.2BSD (also 4.3BSD) native filesystem (long names)
- NFS getdirentries() system call
-
- Also define any of the following that are pertinent:
-
- ATT_SPEC check user buffer address for longword alignment
- BSD_SYSV BRL UNIX System V emulation environment on 4.nBSD
- UNK have _getdents() system call, but kernel may not
- support it
-
- If your C library has a getdents() system call interface, but you
- can't count on all kernels on which your application binaries may
- run to support it, change the system call interface name to
- _getdents() and define "UNK" to enable the system-call validity
- test in this "wrapper" around _getdents().
-
- If your system has a getdents() system call that is guaranteed
- to always work, you shouldn't be using this source file at all.
- */
-
- #include <sys/errno.h>
- #include <sys/types.h>
- #ifdef BSD_SYSV
- #include <sys/_dir.h> /* BSD flavor, not System V */
- #else
- #include <sys/dir.h>
- #undef MAXNAMLEN /* avoid conflict with SVR3 */
- /* Good thing we don't need to use the DIRSIZ() macro! */
- #ifdef d_ino /* 4.3BSD/NFS using d_fileno */
- #undef d_ino /* (not absolutely necessary) */
- #else
- #define d_fileno d_ino /* (struct direct) member */
- #endif
- #endif
- #include <sys/dirent.h>
- #include <sys/stat.h>
- #ifdef UNK
- #ifndef UFS
- #include "***** ERROR ***** UNK applies only to UFS"
- /* One could do something similar for getdirentries(), but I didn't bother. */
- #endif
- #include <signal.h>
- #endif
-
- #if defined(UFS) + defined(BFS) + defined(NFS) != 1 /* sanity check */
- #include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
- #endif
-
- #ifdef UFS
- #define RecLen( dp ) (sizeof(struct direct)) /* fixed-length entries */
- #else /* BFS || NFS */
- #define RecLen( dp ) ((dp)->d_reclen) /* variable-length entries */
- #endif
-
- #ifdef NFS
- #ifdef BSD_SYSV
- #define getdirentries _getdirentries /* package hides this system call */
- #endif
- extern int getdirentries();
- static long dummy; /* getdirentries() needs basep */
- #define GetBlock( fd, buf, n ) getdirentries( fd, buf, (unsigned)n, &dummy )
- #else /* UFS || BFS */
- #ifdef BSD_SYSV
- #define read _read /* avoid emulation overhead */
- #endif
- extern int read();
- #define GetBlock( fd, buf, n ) read( fd, buf, (unsigned)n )
- #endif
-
- #ifdef UNK
- extern int _getdents(); /* actual system call */
- #endif
-
- extern char *strncpy();
- extern int fstat();
- extern off_t lseek();
-
- extern int errno;
-
- #ifndef DIRBLKSIZ
- #define DIRBLKSIZ 4096 /* directory file read buffer size */
- #endif
-
- #ifndef NULL
- #define NULL 0
- #endif
-
- #ifndef SEEK_CUR
- #define SEEK_CUR 1
- #endif
-
- #ifndef S_ISDIR /* macro to test for directory file */
- #define S_ISDIR( mode ) (((mode) & S_IFMT) == S_IFDIR)
- #endif
-
- #ifdef UFS
-
- /*
- The following routine is necessary to handle DIRSIZ-long entry names.
- Thanks to Richard Todd for pointing this out.
- */
-
- static int
- NameLen( name ) /* return # chars in embedded name */
- char name[]; /* -> name embedded in struct direct */
- {
- register char *s; /* -> name[.] */
- register char *stop = &name[DIRSIZ]; /* -> past end of name field */
-
- for ( s = &name[1]; /* (empty names are impossible) */
- *s != '\0' /* not NUL terminator */
- && ++s < stop; /* < DIRSIZ characters scanned */
- )
- ;
-
- return s - name; /* # valid characters in name */
- }
-
- #else /* BFS || NFS */
-
- extern int strlen();
-
- #define NameLen( name ) strlen( name ) /* names are always NUL-terminated */
-
- #endif
-
- #ifdef UNK
- static enum { maybe, no, yes } state = maybe;
- /* does _getdents() work? */
-
- /*ARGSUSED*/
- static void
- sig_catch( sig )
- int sig; /* must be SIGSYS */
- {
- state = no; /* attempted _getdents() faulted */
- }
- #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[] */
- {
- int serrno; /* entry errno */
- off_t offset; /* initial directory file offset */
- struct stat statb; /* fstat() info */
- 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[.] */
-
- #ifdef UNK
- switch ( state )
- {
- void (*shdlr)(); /* entry SIGSYS handler */
- register int retval; /* return from _getdents() if any */
-
- case yes: /* _getdents() is known to work */
- return _getdents( fildes, buf, nbyte );
-
- case maybe: /* first time only */
- shdlr = signal( SIGSYS, sig_catch );
- retval = _getdents( fildes, buf, nbyte ); /* try it */
- (void)signal( SIGSYS, shdlr );
-
- if ( state == maybe ) /* SIGSYS did not occur */
- {
- state = yes; /* so _getdents() must have worked */
- return retval;
- }
- /* else fall through into emulation */
-
- /* case no: /* fall through into emulation */
- }
- #endif
-
- if ( buf == NULL
- #ifdef ATT_SPEC
- || (unsigned long)buf % sizeof(long) != 0 /* ugh */
- #endif
- ) {
- errno = EFAULT; /* invalid pointer */
- return -1;
- }
-
- if ( fstat( fildes, &statb ) != 0 )
- return -1; /* errno set by fstat() */
-
- if ( !S_ISDIR( statb.st_mode ) )
- {
- errno = ENOTDIR; /* not a directory */
- return -1;
- }
-
- if ( (offset = lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
- return -1; /* errno set by lseek() */
-
- #ifdef BFS /* no telling what remote hosts do */
- if ( (unsigned long)offset % DIRBLKSIZ != 0 )
- {
- errno = ENOENT; /* file pointer probably misaligned */
- return -1;
- }
- #endif
-
- serrno = errno; /* save entry errno */
-
- for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
- { /* convert next directory block */
- int size;
-
- do size = GetBlock( fildes, u.dblk, DIRBLKSIZ );
- while ( size == -1 && errno == EINTR );
-
- if ( size <= 0 )
- return size; /* EOF or error (EBADF) */
-
- for ( dp = (struct direct *)u.dblk;
- (char *)dp < &u.dblk[size];
- dp = (struct direct *)((char *)dp + RecLen( dp ))
- ) {
- #ifndef UFS
- if ( dp->d_reclen <= 0 )
- {
- errno = EIO; /* corrupted directory */
- return -1;
- }
- #endif
-
- if ( dp->d_fileno != 0 )
- { /* non-empty; copy to user buffer */
- register int reclen =
- DIRENTSIZ( NameLen( dp->d_name ) );
-
- if ( (char *)bp + reclen > &buf[nbyte] )
- {
- errno = EINVAL;
- return -1; /* buf too small */
- }
-
- bp->d_ino = dp->d_fileno;
- bp->d_off = offset + ((char *)dp - u.dblk);
- bp->d_reclen = reclen;
- (void)strncpy( bp->d_name, dp->d_name,
- reclen - DIRENTBASESIZ
- ); /* adds NUL padding */
-
- bp = (struct dirent *)((char *)bp + reclen);
- }
- }
-
- #ifndef BFS /* 4.2BSD screwed up; fixed in 4.3BSD */
- if ( (char *)dp > &u.dblk[size] )
- {
- errno = EIO; /* corrupted directory */
- return -1;
- }
- #endif
- }
-
- errno = serrno; /* restore entry errno */
- return (char *)bp - buf; /* return # bytes read */
- }
- END OF getdents.c
-
- --
-
- Rich $alz "Anger is an energy"
- Cronus Project, BBN Labs rsalz@pineapple.bbn.com
- Moderator, comp.sources.unix sources@uunet.uu.net
-