/****************************************************************************

        Directory.c

	This file contains the C code that implements the directory
	iteration and file information subsystem.

	This code is intended to be used as a convenient, machine
	independent interface to iterate through the contents of a
	directory.

 ****************************************************************************/

#ifdef vax11c
#include "types.h"	/* make sure we get the right definitions */
#include <stdio.h>
#include <rms.h>
#include <ssdef.h>
#endif /* vax11c */
#include "Directory.h"
#include "RegExp.h"

/*--------------------------------------------------------------------------*

        L O W    L E V E L    D I R E C T O R Y    I N T E R F A C E

 *--------------------------------------------------------------------------*/

int DirectoryOpen
#ifdef UseFunctionPrototypes
	(char *dir_name, Directory *dp)
#else
	(dir_name,dp)
char *dir_name;
Directory *dp;

#endif
{
	DirectoryDir(dp) = opendir(dir_name);
	if (DirectoryDir(dp) == NULL) return(FALSE);
	if (DirectoryPathExpand(dir_name,DirectoryPath(dp)) == NULL)
	{
		closedir(DirectoryDir(dp));
		return(FALSE);
	}
	return(TRUE);
} /* End DirectoryOpen */


void DirectoryRestart
#ifdef UseFunctionPrototypes
	(Directory *dp)
#else
	(dp)
Directory *dp;

#endif
{
	rewinddir(DirectoryDir(dp));
} /* End DirectoryRestart */


void DirectoryClose
#ifdef UseFunctionPrototypes
	(Directory *dp)
#else
	(dp)
Directory *dp;

#endif
{
	closedir(DirectoryDir(dp));
} /* End DirectoryClose */


long DirectoryTellPosition
#ifdef UseFunctionPrototypes
	(Directory *dp)
#else
	(dp)
Directory *dp;

#endif
{
	return(telldir(DirectoryDir(dp)));
} /* End DirectoryTellPosition */


void DirectorySetPosition
#ifdef UseFunctionPrototypes
	(Directory *dp, long int pos)
#else
	(dp,pos)
Directory *dp;
long pos;

#endif
{
	seekdir(dp,pos);
} /* End DirectorySetPosition */


#ifdef vax11c
/*
 * Expand the directory specification to remove any concealed logical names.
 */
unsigned long int expand_dir(cwd, es_string, es_size)
char *cwd;
char *es_string;
int *es_size;
{
	unsigned long int status, Root;
	register char *cp;
	struct FAB Fab;
	struct NAM Nam;

	/*
	 *	Check to see if this is a root directory (or is pretending)
	 */
	cp = strstr(cwd, "000000");
	if (cp)
		Root = 1;
	else
		Root = 0;

	/*
	 * fill in the RMS structures
	 */
	bzero(&Fab, sizeof(struct FAB));
	Fab.fab$b_bid = FAB$C_BID;
	Fab.fab$b_bln = sizeof(struct FAB);
	Fab.fab$l_dna = "*.*;*";
	Fab.fab$b_dns = 5;
	Fab.fab$l_nam = &Nam;
	Fab.fab$l_fop = FAB$M_NAM;
	Fab.fab$b_shr = FAB$M_SHRPUT|FAB$M_SHRGET|FAB$M_SHRDEL|FAB$M_SHRUPD;
	bzero(&Nam, sizeof(struct NAM));
	Nam.nam$b_bid = NAM$C_BID;
	Nam.nam$b_bln = sizeof(struct NAM);
	Nam.nam$l_esa = es_string;
	Nam.nam$b_ess = *es_size;
	Nam.nam$b_nop = NAM$M_NOCONCEAL;

	/*
	 *	Pointout what we are looking for
	 */
	Fab.fab$l_fna = cwd;
	Fab.fab$b_fns = strlen(cwd);

	/*
	 *	Parse the file
	 */
#ifdef DEBUG
	printf("Parsing \"%s\"\n", cwd);
#endif /* DEBUG */
	status = SYS$PARSE(&Fab);
	if (!(status&1)) return(status);

	/*
	 *	Null terminated the spec
	 */
	*es_size = Nam.nam$b_esl;
	es_string[*es_size] = '\0';

	/*
	 *	Handle the case of device:[foo.][bar.blah]
	 */
	cp = strstr(es_string, "][");
	if (cp) {
		register char *ep;
		cp++; cp++;	/* get past the "][" */
		ep = strstr(es_string, "][");
		while (*cp) *ep++ = *cp++;
		*ep = '\0';
	}

	/*
	 *	Strip the "*.*;*" off of the end
	 */
	cp = strchr(es_string, '*');
	*cp = '\0';

	/*
	 *	If this is was setup as a root directory, then
	 *	strip the "000000", iff it is not the real root
	 */
	if (Root) {
		cp = strstr(es_string, "000000");
		if (*(cp - 1) != '[') {
			/*
			 *	Not the real root, strip it
			 */
			--cp;
			*cp++ = ']'; *cp = '\0';
		}
	}

	/*
	 *	Indicate the new length
	 */
	*es_size = strlen(es_string);

	/*
	 *	Return success
	 */
	return(SS$_NORMAL);
}

int GetParentDirectory
#ifdef UseFunctionPrototypes
	(Directory *dp, DirEntry *de)
#else
	(dp,de)
Directory *dp;
DirEntry *de;

#endif
{
	u_short orig_file_type;
	unsigned long int status, sizeof_tmp_path;
	static struct dirent *_ep;
	static struct stat _lstats, _stats, _dstats;
	char full_path[MAXPATHLEN];
	char temp_path[MAXPATHLEN];
	register char *cp;

	/*
	 *	Read the parent's directory information
	 */
	strcpy(temp_path, DirectoryPath(dp));
	cp = strrchr(temp_path, '.');
	if (cp) {
		*cp++ = ']'; *cp = '\0';
	} else {
		cp = strchr(temp_path, '[');
		++cp; *cp = '\0';
		strcat(temp_path, "000000]");
	}
	cp = strrchr(temp_path, '.');
	if (cp) {
		*cp++ = ']'; *cp = '\0';
	} else {
		cp = strchr(temp_path, '[');
		++cp; *cp = '\0';
		strcat(temp_path, "000000]");
	}
	strcpy(full_path, temp_path);
	/*
	 *	If the directory path is foo:[000000]
	 *	then expand it.  IF it won't expand
	 *	ie, it is the top of the Device, then
	 *	return FALSE.
	 */
	cp = strstr(full_path, "[000000]");
	if (cp) {
		sizeof_tmp_path = sizeof(temp_path) - 1;
		status = expand_dir(full_path, temp_path, &sizeof_tmp_path);
		if (!(status&1)) return (False);
		strcpy(full_path, temp_path);
	}
	/*
	 *	Now find the parent filename
	 */
	strcpy(temp_path, DirectoryPath(dp));
	cp = strrchr(temp_path, '.');
	if (cp) {
		*cp++ = ']'; *cp = '\0';
		cp = strrchr(temp_path, '.');
		if (cp)
			cp++;
		else {
	 		cp = strchr(temp_path,'[');
			cp++;
		}
	} else {
		cp = strchr(temp_path,'[');
		cp++;
	}
	strcpy(temp_path, cp);
	cp = strchr(temp_path, ']');
	*cp = '\0';
	strcat(temp_path, ".DIR;1");
	/*
	 *	If we have reached the top, then
	 *	return false.
	 */
	if (strcmp(temp_path, "000000.DIR;1") == 0) return(FALSE);

	strcat(full_path, temp_path);

	strcpy(DirEntryFileName(de), "[-]");

	if (stat(full_path,&_lstats) != 0) return(FALSE);

	orig_file_type = _lstats.st_mode & S_IFMT;
	switch (orig_file_type)
	{
	    case S_IFDIR:
		DirEntryType(de) = F_TYPE_DIR;
		break;
	    case S_IFREG:
		DirEntryType(de) = F_TYPE_FILE;
		break;
	    case S_IFCHR:
		DirEntryType(de) = F_TYPE_CHAR_SPECIAL;
		break;
	    case S_IFBLK:
		DirEntryType(de) = F_TYPE_BLOCK_SPECIAL;
		break;
	    default:
		DirEntryType(de) = orig_file_type;
		break;
	}

	DirEntryIsBrokenLink(de) = FALSE;
	DirEntryIsDirectoryLink(de) = FALSE;
	
	_stats = _lstats;

	FileInfoOrigMode(DirEntrySelfInfo(de)) = _lstats.st_mode;
	FileInfoProt(DirEntrySelfInfo(de)) = _lstats.st_mode & 0777;
	FileInfoUserID(DirEntrySelfInfo(de)) = _lstats.st_uid;
	FileInfoGroupID(DirEntrySelfInfo(de)) = _lstats.st_gid;
	FileInfoFileSize(DirEntrySelfInfo(de)) = _lstats.st_size;
	FileInfoLastAccess(DirEntrySelfInfo(de)) = _lstats.st_atime;
	FileInfoLastModify(DirEntrySelfInfo(de)) = _lstats.st_mtime;
	FileInfoLastStatusChange(DirEntrySelfInfo(de)) = _lstats.st_ctime;

	FileInfoOrigMode(DirEntryActualInfo(de)) = _stats.st_mode;
	FileInfoProt(DirEntryActualInfo(de)) = _stats.st_mode & 0777;
	FileInfoUserID(DirEntryActualInfo(de)) = _stats.st_uid;
	FileInfoGroupID(DirEntryActualInfo(de)) = _stats.st_gid;
	FileInfoFileSize(DirEntryActualInfo(de)) = _stats.st_size;
	FileInfoLastAccess(DirEntryActualInfo(de)) = _stats.st_atime;
	FileInfoLastModify(DirEntryActualInfo(de)) = _stats.st_mtime;
	FileInfoLastStatusChange(DirEntryActualInfo(de)) = _stats.st_ctime;

	return(TRUE);
}
#endif /* vax11c */

int DirectoryReadNextEntry
#ifdef UseFunctionPrototypes
	(Directory *dp, DirEntry *de)
#else
	(dp,de)
Directory *dp;
DirEntry *de;

#endif
{
	u_short orig_file_type;
	static struct dirent *_ep;
	static struct stat _lstats, _stats, _dstats;
	char full_path[MAXPATHLEN + 2];
	char temp_path[MAXPATHLEN + 2];

	_ep = readdir(DirectoryDir(dp));
	if (_ep == NULL) return(FALSE);
	strcpy(DirEntryFileName(de),_ep->d_name);
	strcpy(full_path,DirectoryPath(dp));
	strcat(full_path,DirEntryFileName(de));

#ifdef vax11c
	if (stat(full_path,&_lstats) != 0) return(FALSE);
#else
	if (lstat(full_path,&_lstats) != 0) return(FALSE);
#endif /* vax11c */

	orig_file_type = _lstats.st_mode & S_IFMT;
	switch (orig_file_type)
	{
	    case S_IFDIR:
		DirEntryType(de) = F_TYPE_DIR;
		break;
	    case S_IFREG:
		DirEntryType(de) = F_TYPE_FILE;
		break;
	    case S_IFCHR:
		DirEntryType(de) = F_TYPE_CHAR_SPECIAL;
		break;
	    case S_IFBLK:
		DirEntryType(de) = F_TYPE_BLOCK_SPECIAL;
		break;
#ifdef S_IFLNK
	    case S_IFLNK:
		DirEntryType(de) = F_TYPE_SYM_LINK;
		break;
#endif

#ifdef S_IFSOCK
	    case S_IFSOCK:
		DirEntryType(de) = F_TYPE_SOCKET;
		break;
#endif

#ifdef S_IFIFO
	    case S_IFIFO:
		DirEntryType(de) = F_TYPE_FIFO;
		break;
#endif
	    default:
		DirEntryType(de) = orig_file_type;
		break;
	}

	DirEntryIsBrokenLink(de) = FALSE;
	DirEntryIsDirectoryLink(de) = FALSE;
	if (DirEntryIsSymLink(de))			/* Symbolic Link */
	{
		if (stat(full_path,&_stats) != 0)	/* Can't Stat File */
		{
			DirEntryIsBrokenLink(de) = TRUE;
			_stats = _lstats;
		}
		    else				/* Link Not Broken */
		{
#ifdef SLOW_DIRLINK_TEST
			if (DirectoryPathExpand(full_path,temp_path) != NULL)
			{
#else
			if ((_stats.st_mode & S_IFMT) == S_IFDIR)
			{
#endif
				DirEntryIsDirectoryLink(de) = TRUE;
			}

		}
	}
	    else					/* Not Symbolic Link */
	{
		_stats = _lstats;
	}

	FileInfoOrigMode(DirEntrySelfInfo(de)) = _lstats.st_mode;
	FileInfoProt(DirEntrySelfInfo(de)) = _lstats.st_mode & 0777;
	FileInfoUserID(DirEntrySelfInfo(de)) = _lstats.st_uid;
	FileInfoGroupID(DirEntrySelfInfo(de)) = _lstats.st_gid;
	FileInfoFileSize(DirEntrySelfInfo(de)) = _lstats.st_size;
	FileInfoLastAccess(DirEntrySelfInfo(de)) = _lstats.st_atime;
	FileInfoLastModify(DirEntrySelfInfo(de)) = _lstats.st_mtime;
	FileInfoLastStatusChange(DirEntrySelfInfo(de)) = _lstats.st_ctime;

	FileInfoOrigMode(DirEntryActualInfo(de)) = _stats.st_mode;
	FileInfoProt(DirEntryActualInfo(de)) = _stats.st_mode & 0777;
	FileInfoUserID(DirEntryActualInfo(de)) = _stats.st_uid;
	FileInfoGroupID(DirEntryActualInfo(de)) = _stats.st_gid;
	FileInfoFileSize(DirEntryActualInfo(de)) = _stats.st_size;
	FileInfoLastAccess(DirEntryActualInfo(de)) = _stats.st_atime;
	FileInfoLastModify(DirEntryActualInfo(de)) = _stats.st_mtime;
	FileInfoLastStatusChange(DirEntryActualInfo(de)) = _stats.st_ctime;

	return(TRUE);
} /* End DirectoryReadNextEntry */


char *DirectoryPathExpand
#ifdef UseFunctionPrototypes
	(char *old_path, char *new_path)
#else
	(old_path,new_path)
char *old_path,*new_path;

#endif
{
	register char *p;
	char path[MAXPATHLEN + 2];

#ifdef vax11c
	if (getcwd(path,MAXPATHLEN) == NULL) return(NULL);
#else
	if (getwd(path) == NULL) return(NULL);
#endif /* vax11c */
	if (chdir(old_path) != 0) return(NULL);
#ifdef vax11c
	if (getcwd(new_path,MAXPATHLEN) == NULL) strcpy(new_path,old_path);
#else
	if (getwd(new_path) == NULL) strcpy(new_path,old_path);
#endif /* vax11c */
	if (chdir(path) != 0) return(NULL);
#ifndef vax11c	/* not necessecary to OpenVMS */
	for (p = new_path; *p != '\0'; p++);
	if ((p != new_path) && *(p - 1) != '/')
	{
		*p++ = '/';
		*p = '\0';
	}
#else
	/*
	 *	If there is no subdirectory present (eg, no "." present)
	 *	then expand the directory specification.
	 */
	p = strchr(new_path, '.');
	if (!p) {
		int status;
		int path_size = sizeof(path) - 3;
		status = expand_dir(new_path, path, &path_size);
		if (!(status&1)) return(NULL);
		strcpy(new_path, path);
	}
#endif /* vax11c */
	return(new_path);
} /* End DirectoryPathExpand */


/*---------------------------------------------------------------------------*

             D I R E C T O R Y    E N T R Y    R O U T I N E S

 *---------------------------------------------------------------------------*/

void DirEntryDump
#ifdef UseFunctionPrototypes
	(FILE *fp, DirEntry *de)
#else
	(fp,de)
FILE *fp;
DirEntry *de;

#endif
{
	fprintf(fp,"%20s, Size %7d, Prot %3o\n",
		DirEntryFileName(de),DirEntryFileSize(de),DirEntryProt(de));
} /* End DirEntryDump */
