[comp.unix.questions] Curdir

abcscnge@csuna.csun.edu (Scott "The Pseudo-Hacker" Neugroschl) (08/01/89)

For those people who want a curdir(3) function, but can't seem to find the
library, I have one available.  It uses the directory(3X) functions, 
but I put #ifdef'ed code in to simulate it for those without <dirent.h>.
Send me E-mail if you want it; if enough people ask, I'll post.

Note, I wrote it to be as portable as possible, only assumption is that
if you don't have <dirent.h>, you have a SystemV type file system
(16 bytes per directory entry).

Scott

(Send email here, or to ...!csun!litvax!abctdev!scott)

-- 
Scott "The Pseudo-Hacker" Neugroschl
UUCP:  ...!sm.unisys.com!csun!csuna.csun.edu!abcscnge
-- Beat me, Whip me, make me code in Ada
-- Disclaimers?  We don't need no stinking disclaimers!!!

abcscnge@csuna.csun.edu (Scott "The Pseudo-Hacker" Neugroschl) (08/07/89)

Several people have mailed and asked for the source to my curdir function,
so (as it is now easier to post than reply), here it is.  I apologize
if this is the wrong news group, please do not flame... I am posting
here because this is where I made the original comment...


---- cut here ----
/*
** if your system does not support the directory(3X) functions, i.e. no
** <dirent.h>, then define _NO_DIRENT_H_ at the top of the file.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _NO_DIRENT_H_
#include <dirent.h>
#else
#include <sys/dir.h>
typedef int DIR;
struct dirent {
	long		d_ino;
	off_t		d_off;
	unsigned short	d_reclen;
	char		d_name[DIRSIZ+2];
};
static DIR		*opendir();
static struct dirent	*readdir();
static long		telldir();
static void		seekdir();
static int		closedir();
#define rewinddir(dirp)	seekdir(dirp,0L);
#endif

curdir(str)
char str[];
{
	static char tmpstr[BUFSIZ];
	static char openpath[BUFSIZ+BUFSIZ];
	static char tmpnam[BUFSIZ+BUFSIZ];
	struct stat buf, rootbuf, tmpstat;
	DIR *dirp;
	struct dirent *dp;
	int l;

	if (stat("/",&rootbuf) == -1)
		return(-1);

	if (stat(".",&buf) == -1)
		return(-1);

	openpath[0] = tmpstr[0] = '\0';
	strcpy(openpath,"..");
	while (buf.st_dev != rootbuf.st_dev ||
	       buf.st_ino != rootbuf.st_ino)
	{
		if ((dirp = opendir(openpath)) == NULL)
			return(-1);
		while ((dp = readdir(dirp)) != NULL)
		{
			sprintf(tmpnam,"%s/%s",openpath,dp->d_name);
			if (stat(tmpnam,&tmpstat) == -1)
			{
				closedir(dirp);
				return(-1);
			}
			if (tmpstat.st_dev == buf.st_dev &&
			    tmpstat.st_ino == buf.st_ino)
				break;
		}
		closedir(dirp);
		if (dp == NULL)
			return(-1);
		append(dp->d_name,tmpstr);
		if (stat(openpath,&buf) == -1)
			return(-1);
		sprintf(openpath,"../%s",openpath);
	}
	strcpy(str,"/");
	strcat(str,tmpstr);
	if (str[l = (strlen(str) - 1)] == '/')
		str[l] = '\0';
	return(0);


}

static append(head,tail)
char *head, *tail;
{
	char tmpbuf[BUFSIZ+BUFSIZ];

	strcpy(tmpbuf,head);
	strcat(tmpbuf,"/");
	strcat(tmpbuf,tail);
	strcpy(tail,tmpbuf);

}

#ifdef _NO_DIRENT_H

static off_t curoff = ((off_t) 0);

static DIR *opendir(name)
int name;
{
	char *malloc();
	DIR *dp;
	struct stat buf;

	/* no file, or not a directory */
	if (stat(name,&buf) == -1 || (buf.st_mode & S_IFMT) != S_IFDIR)
		return(NULL);

	/* no memory? */
	if ((dp = (DIR *)malloc(sizeof(DIR))) == NULL)
		return(NULL);

	/* can't open? */
	if ((*dp = open(name,0)) == -1)
	{
		free(dp);
		return(NULL);
	}
	curoff = (off_t)0;
	return(dp);
}

static struct dirent *readdir(dirp)
register DIR *dirp;
{
	static struct dirent dp;
	struct direct dir;

	if (read(*dirp,&dir,sizeof(dir)) != sizeof(dir))
		return(NULL);
	dp.d_ino = (long) dir.d_ino;
	dp.d_off = curoff;
	curoff += sizeof(dir);
	dp.d_reclen = sizeof(dir);
	strncpy(dp.d_name,dir.d_name,DIRSIZ);
	dp.d_name[DIRSIZ] = '\0';
	return(&dp);
}

static long telldir(dp)
register DIR *dp;
{
	long lseek();
	return((long)(curoff = (off_t) lseek(*dp,0L,1)));
}

static void seekdir(dp,offset)
register DIR *dp;
register long offset;
{
	long lseek();

	curoff = (off_t)lseek(*dp,offset,0);
}

static int closedir(dp)
register DIR *dp;
{
	close(*dp);
	free(dp);
	return(0);
}
#endif

--- cut here ---
-- 
Scott "The Pseudo-Hacker" Neugroschl
UUCP:  ...!sm.unisys.com!csun!csuna.csun.edu!abcscnge
-- Beat me, Whip me, make me code in Ada
-- Disclaimers?  We don't need no stinking disclaimers!!!

abcscnge@csuna.csun.edu (Scott "The Pseudo-Hacker" Neugroschl) (08/10/89)

If you aren't interested in curdir, hit 'n' now.

It has been brought to my attention that I posted my own quickly hacked
version of curdir, and that it had no comments on what I was doing.
This message is a repost.  The functions are now documented.  Thank
you all for not flaming the uncommented code.


---- cut here -- 


/*
** if your system does not support the directory(3X) functions, i.e. no
** <dirent.h>, then define _NO_DIRENT_H_ at the top of the file.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _NO_DIRENT_H_
#include <dirent.h>
#else
/* dirent.h is not present.  Dummy up the defs in dirent.h */
#include <sys/dir.h>
typedef struct {
	int fd;		/* file descriptor		*/
	off_t offset;	/* offset in file		*/
} DIR;
struct dirent {		/* stolen from directory(3X) in the Sys V manual */
	long		d_ino;
	off_t		d_off;
	unsigned short	d_reclen;
	char		d_name[DIRSIZ+2];
};

/* declarations found in dirent.h	*/
static DIR		*opendir();
static struct dirent	*readdir();
static long		telldir();
static void		seekdir();
static int		closedir();
#define rewinddir(dirp)	seekdir(dirp,0L);
#endif

/*
** curdir()	--	return the current directory
**
** INPUTS:	NONE
**
** OUTPUTS:	str	-- string containing the current directory
**
** RETURNS:	0 if successful
**		-1 if error
*/
curdir(str)
char str[];
{
	static char tmpstr[BUFSIZ];		/* work space		*/
	static char openpath[BUFSIZ+BUFSIZ];	/* more work space	*/
	static char tmpnam[BUFSIZ+BUFSIZ];	/* yet more work space	*/
	struct stat buf, rootbuf, tmpstat;
	DIR *dirp;				/* current directory	*/
	struct dirent *dp;			/* current dir. entry	*/
	int l;					/* length of dir. ent	*/

	/* get the root inode and device information			*/
	if (stat("/",&rootbuf) == -1)
		return(-1);

	/* get the information for the current directory		*/
	if (stat(".",&buf) == -1)
		return(-1);

	/* there is no path yet!					*/
	openpath[0] = tmpstr[0] = '\0';

	/* start by looking at parent directory				*/
	strcpy(openpath,"..");

	/* while the parent isn't the root directory, loop		*/
	while (buf.st_dev != rootbuf.st_dev ||
	       buf.st_ino != rootbuf.st_ino)
	{
		/* open the parent to search for the current directory	*/
		if ((dirp = opendir(openpath)) == NULL)
			return(-1);


		/* read the parent directory				*/
		while ((dp = readdir(dirp)) != NULL)
		{
			/* build the file to stat			*/
			sprintf(tmpnam,"%s/%s",openpath,dp->d_name);

			/* stat the entry				*/
			if (stat(tmpnam,&tmpstat) == -1)
			{
				/* couldn't stat!			*/
				closedir(dirp);
				return(-1);
			}


			/* is the entry the same as the current one?	*/
			if (tmpstat.st_dev == buf.st_dev &&
			    tmpstat.st_ino == buf.st_ino)
				break;
		}

		/* either we've found the current directory entry, or	*/
		/* something is horribly wrong -- (readdir return NULL	*/
		/* on EOF						*/
		closedir(dirp);

		/* the current entry was not found!!!!			*/
		if (dp == NULL)
			return(-1);

		/* build up the path to the current directory		*/
		append(dp->d_name,tmpstr);

		/* the parent directory is now the current directory	*/
		if (stat(openpath,&buf) == -1)
			return(-1);

		/* move up one level					*/
		sprintf(openpath,"../%s",openpath);
	}

	/* prepend a leading "/"					*/
	strcpy(str,"/");
	strcat(str,tmpstr);

	/* check for "/path.../"					*/
	if (str[l = (strlen(str) - 1)] == '/')
		str[l] = '\0';	/* strip trailing "/"			*/

	/* successful return						*/
	return(0);

}

/* append	-- append "tail" to "head", and return the result in tail
**
** INPUTS:	head	-- leading path part
**		tail	-- trailing path part
**
** OUTPUTS:	tail	-- "head/tail"
**
** RETURNS:	NONE
**
*/
static append(head,tail)
char *head, *tail;
{
	char tmpbuf[BUFSIZ+BUFSIZ];

	strcpy(tmpbuf,head);
	strcat(tmpbuf,"/");
	strcat(tmpbuf,tail);
	strcpy(tail,tmpbuf);

}

#ifdef _NO_DIRENT_H
/*
** routines to simulate directory(3X)
**
*/

/* 
** opendir	-- open a directory
**
** INPUTS:	name	-- name of directory to open
**
** OUTPUTS:	NONE
**
** RETURNS:	pointer to DIR structure
*/
static DIR *opendir(name)
int name;
{
	char *malloc();
	DIR *dp;
	struct stat buf;

	/* no file, or not a directory */
	if (stat(name,&buf) == -1 || (buf.st_mode & S_IFMT) != S_IFDIR)
		return(NULL);

	/* no memory? */
	if ((dp = (DIR *)malloc(sizeof(DIR))) == NULL)
		return(NULL);

	/* can't open? */
	if ((dp->fd = open(name,0)) == -1)
	{
		free(dp);
		return(NULL);
	}

	/* at start of directory	*/
	dp->offset = (off_t)0;
	return(dp);
}

/*
** readdir()	-- read a directory entry	
**
** INPUTS:	dirp	-- pointer to current DIR
**
** OUTPUTS:	NONE
**
** RETURNS:	pointer to struct dirent describing current directory entry
**		NULL on EOF or error
**
** NOTES:	RETURN VALUE POINTS TO STATIC DATA!!!!!
**
*/
static struct dirent *readdir(dirp)
register DIR *dirp;
{
	static struct dirent dp;
	struct direct dir;

	/* read the current directory entry	*/
	if (read(dirp->fd,&dir,sizeof(dir)) != sizeof(dir))
		return(NULL);

	/* build a dirent structure		*/
	dp.d_ino = (long) dir.d_ino;
	dp.d_off = dirp->offset;
	dirp->offset += sizeof(dir);
	dp.d_reclen = sizeof(dir);
	strncpy(dp.d_name,dir.d_name,DIRSIZ);
	dp.d_name[DIRSIZ] = '\0';

	/* return the dirent			*/
	return(&dp);
}

/*
** telldir()	-- return the current offset in the directory
**
** INPUTS:	dp	-- pointer to the DIR to examine
**
** OUTPUTS:	NONE
**
** RETURNS:	offset in the specified DIR
**
*/
static long telldir(dp)
register DIR *dp;
{
	return((long)dp->offset);
}

/*
** seekdir()	-- seek to a specified offset in the directory
**
** INPUTS:	dp	-- pointer to the DIR to see k on
**		offset	-- offset to seek to
**
** OUTPUTS:	NONE
**
** RETURNS:	NONE
*/
static void seekdir(dp,offset)
register DIR *dp;
register long offset;
{
	long lseek();

	dp->offset = (off_t)lseek(dp->fd,offset,0);
}

/*
** closedir()	-- close a DIR
**
** INPUTS:	dp	-- DIR to close
**
** OUTPUTS:	NONE
**
** RETURNS:	NONE
*/
static int closedir(dp)
register DIR *dp;
{
	/* close the directory file descriptor	*/
	close(dp->fd);

	/* free up the allocated memory		*/
	free(dp);
	return(0);
}
#endif

--- cut here ---

Scott
-- 
Scott "The Pseudo-Hacker" Neugroschl
UUCP:  ...!sm.unisys.com!csun!csuna.csun.edu!abcscnge
-- Beat me, Whip me, make me code in Ada
-- Disclaimers?  We don't need no stinking disclaimers!!!