/********************************************************************
*   UnixCompatibility.c
*
*   Used to adapt the posix (unix) calls to mac calls.
*
*   Copyright (c) 1995, Willows Software Inc.  All rights reserved.
********************************************************************/

static char *UnixCompatibility_c =  "IDENT:  @(#)UnixCompatibility.c	1.8 4/3/97 18:21:39";

#include "windows.h"
#include <string.h>
#include <stdio.h>
#include <Files.h>
#include <Finder.h>
#include <Strings.h>
#include <Aliases.h>

#include "UnixCompatibility.h"
#include "StringUtils.h"

/* Undo the redirection of the unix calls for our wrapper calls */
#undef mkdir
#undef fopen
#undef freopen
#undef getcwd
#undef chdir
#undef remove
#undef rename
#undef open
#undef create

char *GetParentDir(char *macPath, int maxlen);
char *GetRootDir(char *macPath, int maxlen);

static int ResolveAliasPath(char *path);


/********************************************************************
*	UNIX_fopen
*
*	Wraps the fopen call to accept unix path names.
********************************************************************/
FILE *UNIX_fopen(const char *fname, const char *mode)
{
char macName[FILENAME_MAX];

	strncpy(macName, fname, FILENAME_MAX);
	macName[FILENAME_MAX-1] = '\0';

	UnixToMacPath(macName, FILENAME_MAX);

	return(fopen(macName, mode));
}


/********************************************************************
*	UNIX_freopen
*
*	Wraps the freopen call to accept unix path names.
********************************************************************/
FILE *UNIX_freopen(const char *fname, const char *mode, FILE *file)
{
char macName[FILENAME_MAX];

	strncpy(macName, fname, FILENAME_MAX);
	macName[FILENAME_MAX-1] = '\0';

	UnixToMacPath(macName, FILENAME_MAX);

	return(freopen(macName, mode, file));
}


/********************************************************************
*	UNIX_remove
*
*	Wraps the remove call to accept unix path names.
********************************************************************/
int UNIX_remove(const char *fname) 
{
char macName[FILENAME_MAX];

	strncpy(macName, fname, FILENAME_MAX);
	macName[FILENAME_MAX-1] = '\0';

	UnixToMacPath(macName, FILENAME_MAX);

	return(remove(macName));
}


/********************************************************************
*	UNIX_rename
*
*	Wraps the rename call to accept unix path names.
********************************************************************/
int UNIX_rename(const char *oldname, const char *newname) 
{
char oldMacName[FILENAME_MAX], newMacName[FILENAME_MAX];
	
	strncpy(oldMacName, oldname, FILENAME_MAX);
	oldMacName[FILENAME_MAX-1] = '\0';
	UnixToMacPath(oldMacName, FILENAME_MAX);

	strncpy(newMacName, newname, FILENAME_MAX);
	newMacName[FILENAME_MAX-1] = '\0';
	UnixToMacPath(newMacName, FILENAME_MAX);

	return (rename(oldMacName, newMacName));
}


/********************************************************************
*   UNIX_stat
*
*	Wraps the stat call to accept unix path names.
********************************************************************/
int UNIX_stat(const char *path, struct stat *buf)
{
#define S_ISLNK(m)	(((m)&(S_IFMT)) == (S_IFLNK))
char macPath[FILENAME_MAX];
Str255 pasMacPath;
int result, err;
FInfo fndrInfo;
FSSpec fileSpec;
Boolean targetIsFolder, wasAliased;

	strncpy(macPath, path, FILENAME_MAX);						/* Working copy */
	macPath[FILENAME_MAX-1] = '\0';
	
	/* Check for a .. up directory which could have been introduced by the dirent stuff */
	if (strcmp(macPath, "..") == 0)
		GetParentDir(macPath, FILENAME_MAX);
	else
		UnixToMacPath(macPath, FILENAME_MAX);
	
	result = stat(macPath, buf);

	if (result == noErr) {
		c2pstrcpy(pasMacPath, macPath);
		/* See if we have an alias, if so, resolve what it points to */
		if (S_ISLNK(buf->st_mode)) {
			/* Resolve the alias if possible */
			err = FSMakeFSSpec(0, 0, pasMacPath, &fileSpec);
			if (err == noErr)
				err = ResolveAliasFile(&fileSpec, TRUE, &targetIsFolder, &wasAliased);

			if ((err == noErr) && targetIsFolder)
				buf->st_mode = S_IFDIR;
			else if (err == noErr)
				buf->st_mode = S_IFREG;
		}

		/* Set everything to readable */
		buf->st_mode |= S_IREAD;
		
		/* If it is a file, check to see if it is read only or invisible. */	
		if (S_ISREG(buf->st_mode)) {
			buf->st_mode |= S_IEXEC;
			if (GetFInfo(pasMacPath, 0, &fndrInfo) == noErr) {
				if (!(fndrInfo.fdFlags & kNameLocked))
					buf->st_mode |= S_IWRITE;
				
				if (fndrInfo.fdFlags & kIsInvisible)
					result = -1;									/* Fail for invisible files */
			}
		}
		
	}
	
	return(result);
}


/********************************************************************
*   UNIX_mkdir
*
*	Wraps the mkdir call to accept unix path names.
********************************************************************/
int UNIX_mkdir(const char *path, int mode)
{
#pragma unused (mode)
OSErr err;
CInfoPBRec pb;
char macPath[FILENAME_MAX];

	strncpy(macPath, path, FILENAME_MAX);
	macPath[FILENAME_MAX-1] = '\0';
	
	UnixToMacPath(macPath, FILENAME_MAX);

	/* See if it exists */
	/* Use the PB calls as they allow more control */
	/* The FspGetFInfo will not distinguish between a directory and a file */	
	pb.dirInfo.ioCompletion = NULL;
	pb.dirInfo.ioNamePtr = c2pstr(macPath);
	pb.dirInfo.ioVRefNum = 0;		/* Full path name given */
	pb.dirInfo.ioFDirIndex = 0;					/* 0=query 1 itm, -1=query dir in ioDrDirID */
	pb.dirInfo.ioDrDirID = 0;

	err = PBGetCatInfo(&pb, false);		/* See if it exists */
	
	if (err)	
		err = PBDirCreate((HParmBlkPtr)&pb, false);	/* Create the directory */

	return(0);
}


/********************************************************************
*	UNIX_chdir
*
*	Wraps the chdir call to accept unix path names.
********************************************************************/
int UNIX_chdir(const char *path)
{
char macPath[FILENAME_MAX];
int err;

	strncpy(macPath, path, FILENAME_MAX);			/* Working copy */
	macPath[FILENAME_MAX-1] = '\0';

	/* Check for a .. up directory which could have been introduced by the dirent stuff */
	if (strcmp(macPath, "..") == 0)
		GetParentDir(macPath, FILENAME_MAX);
	else if (strcmp(macPath, "/") == 0)
		GetRootDir(macPath, FILENAME_MAX);
	else
		UnixToMacPath(macPath, FILENAME_MAX);

	err = chdir(macPath);
	
	/* See if an alias file is the target, if so, try to resolve it */
	if (err) {
		err = ResolveAliasPath(macPath);
	}

#ifdef DEBUG
//See if it worked.
	getcwd(macPath, FILENAME_MAX);					/* Get the actual current working directory */
#endif
	
	return(err);
}



/********************************************************************
*	ResolveAliasPath
*
*	Attempt to resolve an aliased path.
********************************************************************/
static int ResolveAliasPath(char *path)
{
Str255 fileName;
int err;
FSSpec fileSpec;
Boolean targetIsFolder, wasAliased;

	c2pstrcpy(fileName, path);
	
	/* Resolve the alias if possible */
	err = FSMakeFSSpec(0, 0, fileName, &fileSpec);
	if (err == noErr)
		err = ResolveAliasFile(&fileSpec, TRUE, &targetIsFolder, &wasAliased);

	if (err)
		return err;
		
	if (targetIsFolder) {
		/* An alias to a folder was resolved. */
		/* Get the parent directory path name */
		PathNameFromDirID(fileSpec.parID, fileSpec.vRefNum, fileName);
		/* Append the dir name to the parent's path name */
		pstrcat(fileName, fileSpec.name);

		return chdir(p2cstr(fileName));		
	}
	else
		return -1;
}


/********************************************************************
*	UNIX_getcwd
*
*	Wraps the getcwd call to accept unix path names.
********************************************************************/
char *UNIX_getcwd(char *buf, int size)
{
char cwd[FILENAME_MAX];
char *s = cwd, *t = buf;

	getcwd(cwd, FILENAME_MAX);					/* Get the actual current working directory */

	MacToUnixPath(cwd, size);		/* In place conversion */
	
	strncpy(buf, cwd, size);			/* Copy to callers buffer */

	return(buf);
}


/********************************************************************
*	UNIX_open
*
*	Wraps the open call to accept unix path names.
********************************************************************/
int UNIX_open(const char *fname, int oflag)
{
char macName[FILENAME_MAX];

	strncpy(macName, fname, FILENAME_MAX);
	macName[FILENAME_MAX-1] = '\0';
	UnixToMacPath(macName, FILENAME_MAX);

	return(open(macName, oflag));
}


/********************************************************************
*	UNIX_creat
*
*	Wraps the creat call to accept unix path names.
********************************************************************/
int UNIX_creat(const char *path, mode_t mode)
{
char macName[FILENAME_MAX];

	strncpy(macName, path, FILENAME_MAX);
	macName[FILENAME_MAX-1] = '\0';
	UnixToMacPath(macName, FILENAME_MAX);

	return(creat(macName, mode));

}


/********************************************************************
*	gettimeofday
*
*	Provides partial unix capability.
********************************************************************/
int gettimeofday(struct timeval *tp, struct timezone *tzp)
{
time_t tm;

	time(&tm);				/* Use the c time as it will be the same format */

	tp->tv_sec = tm;
	tp->tv_usec = 0;
	
	tzp->tz_minuteswest = 0;		/* Later add the actual time zone if needed */
	tzp->tz_dsttime = 0;
	
	return(0);		/* success */
}



/********************************************************************
*	statfs
*
*	Provides partial unix capability.
********************************************************************/
int statfs(char *path, struct statfs *buf)
{
ParamBlockRec pb;
short iErr;
char pPath[FILENAME_MAX];

	pPath[0]=strlen(path);
	strncpy((char*)pPath[1], path, FILENAME_MAX);
	
	pb.volumeParam.ioCompletion = NULL;
	pb.volumeParam.ioVolIndex = -1;		/* >0=index, <0 use name/num, 0=use num */
	

	iErr = PBGetVInfo(&pb, FALSE);				/* Get the volume info */
	
	memset(buf, 0, sizeof(struct statfs));		/* Clear the return buffer */
	
	/* LATER: for now, just set the parameters used by the xdos stuff */
	buf->f_bsize =  pb.volumeParam.ioVAlBlkSiz;		/* Allocation block size, in bytes */
	buf->f_bfree = pb.volumeParam.ioVFrBlk;				/* Count of free allocation blocks */
	buf->f_blocks = pb.volumeParam.ioVNmAlBlks;		/* Count of all allocation blocks */

	return(0);		/* success */
}

/********************************************************************
*	GetParentDir
*
*	Get parent directory based on the current working directory.
********************************************************************/
char *GetParentDir(char *macPath, int maxlen)
{
char cwd[FILENAME_MAX];
char *sep;
int len;

	/* Get the current directory, we will go backwards from there */
	getcwd(cwd, FILENAME_MAX);
	len = strlen(cwd);
	cwd[len - 1] = '\0';					/* Terminate at the seperator */
	sep = strrchr(cwd, ':');			/* Find the parents seperator */
	if (sep)
		*(++sep) = '\0';					/* Terminate at the parent */

	strncpy(macPath, cwd, maxlen);
	
	return(macPath);	
}

/********************************************************************
*	GetRootDir
*
*	Get root directory based on the current working directory.
********************************************************************/
char *GetRootDir(char *macPath, int maxlen)
{
char cwd[FILENAME_MAX];
char *sep;

	/* Get the current directory, we will go backwards from there */
	getcwd(cwd, FILENAME_MAX);

	sep = strchr(cwd, ':');				/* Find the first seperator */
	if (sep)
		*(++sep) = '\0';					/* Terminate at the first directory (root) */
	
	strncpy(macPath, cwd, maxlen);
	
	return(macPath);	

}


/********************************************************************
*	MacToUnixPath
*
*	Converts a Mac path:  	Fender:System Folder:Preferences:
*	 to a unix path:				/Fender/System Folder/Preferences
********************************************************************/
char *MacToUnixPath(char *path, int maxlen)
{
char macPath[256];
char *src = macPath, *dest = path;

	maxlen = min(maxlen, 256);

	strncpy(macPath, path, maxlen);

	/* Check to see if it is a full or partial path */
	if (*src == ':')
		src++;										/* Skip the parial delimiter */
	else {
		*dest++ = '/';							/* Place the unix root symbol */
		maxlen--;
	}
	
	/* Copy the characters, altering the delimiters */
	while (*src && (maxlen > 0)) {	
		if (*src == ':')				
			*dest = '/';							/* Convert mac ':' to unix '/' */
		else
			*dest = *src;						/* Copy character */
		src++;										/* Increment the pointers */
		dest++;
		maxlen--;
	}

	if (*(--dest) != '/')						/* Backup, see if we should remove/terminate here */
		dest++;										/* No slash, move forward again for terminator */

	*dest = '\0';								/* Terminate the string */

	return(path);
}


/********************************************************************
*	UnixToMacPath
*
*	Converts a Unix path:  	/Fender/System Folder/Preferences
*	 to a mac path:				Fender:System Folder:Preferences:
********************************************************************/
char *UnixToMacPath(char *path, int maxlen)
{
char unixPath[256];
char *src = unixPath, *dest = path;
	
	maxlen = min(maxlen, 256);
	
	strncpy(unixPath, path, maxlen);	/* Make a working copy of the path */

	/* Check to if it is a full or partial path */
	if (*src == '/')
		src++;										/* Full path, skip the unix root symbol */
	else {
		if (*src == '.' && *(src+1) == '/')
			src += 2;							/* special case skip over "./" */
		*dest++ = ':';							/* Partial path, prepend with the delimeter */
		maxlen--;
	}
	
	/* Copy the characters, altering the delimiters */
	while (*src && (maxlen > 0)) {	
		if (*src == '/')				
			*dest = ':';							/* Convert mac ':' to unix '/' */
		else
			*dest = *src;						/* Copy character */
		src++;										/* Increment the pointers */
		dest++;
		maxlen--;
	}

	/* Make sure the path ends with a seperator */
	if (*(--dest) != ':') {
		*(++dest) = ':';
	}

	*(++dest) = '\0';								/* Terminate the string */
	
	return(path);
}


/********************************************************************
* PathNameFromDirID
*
********************************************************************/
void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName)
{
	CInfoPBRec	block;
	Str255	directoryName;
	OSErr	err;

	fullPathName[0] = '\0';

	block.dirInfo.ioDrParID = dirID;
	block.dirInfo.ioNamePtr = directoryName;
	do {
			block.dirInfo.ioVRefNum = vRefNum;
			block.dirInfo.ioFDirIndex = -1;
			block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID;
			err = PBGetCatInfo(&block, FALSE);
			pstrcat(directoryName, (StringPtr)"\p:");
			pstrinsert(fullPathName, directoryName);
	} while (block.dirInfo.ioDrDirID != 2);
}


/********************************************************************
* PathNameFromWD
*
*	Given an HFS working directory, this routine returns the full pathname that
*	  corresponds to it. It does this by calling PBGetWDInfo to get the VRefNum and
*	DirID of the real directory. It then calls PathNameFromDirID, and returns its
*	  result.
********************************************************************/
void PathNameFromWD(long vRefNum, StringPtr pathName)
{
	WDPBRec	myBlock;
	OSErr	err;

	myBlock.ioNamePtr = nil;
	myBlock.ioVRefNum = vRefNum;
	myBlock.ioWDIndex = 0;
	myBlock.ioWDProcID = 0;
	/*
	 Change the Working Directory number in vRefnum into a real
	vRefnum and DirID. The real vRefnum is returned in ioVRefnum,
	 and the real DirID is returned in ioWDDirID.
	*/
	err = PBGetWDInfo(&myBlock, FALSE);
	if (err != noErr)
			return;
	PathNameFromDirID(myBlock.ioWDDirID, myBlock.ioWDVRefNum, pathName);

}





