/*
 * macos_filesys.c
 * Filesystem handling stuff for macos
 *
 * Michael Ladwig <mike@twinpeaks.prc.com> --- November 1995 (Initial version)
 *														 --- May 1996 (Handled relative paths better)
 *														 --- June 1996 (Have open look absolute and relative)
 * Modified : Alexandre Parenteau <aubonbeurre@hotmail.com> --- December 1997
 * Modified : Richard Wesley <hawkfish@electricfish.com> --- December 2000
 */

#include "cvs.h"

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <CodeFragments.h>

#include "cvs_hqx.h"
#include "FileCopy.h"
#include "FullPath.h"

/*
	Utility for decoding a file we just got back raw from the server.  Useful for external
	diff.
	
	 - Richard Wesley <mailto:hawkfish2electricfish.com>
	
*/

int
decode_file (char *infile)
{
	OSErr	err;
	FSSpec	inspec;
	FSSpec	tempSpec;
	FSSpec	tempDirSpec;
	char*	temp_filename = cvs_temp_name ();
	Str31	temp_name_str;
	int 	bin = 0;

	/* Make source spec */
	err = FSpLocationFromFullPath (strlen (infile), infile, &inspec);
	if (noErr != err) return err;
	
	/* Make temp dir spec */
	BlockMoveData (temp_filename, temp_name_str + 1, temp_name_str[0] = strlen (temp_filename));
	err = FSMakeFSSpec (0, 0, temp_name_str, &tempSpec);
	err = FSMakeFSSpec (tempSpec.vRefNum, tempSpec.parID, nil, &tempDirSpec);
	if (noErr != err) return err;

	/* Copy to the temp file */
	err = FSpFileCopy (&inspec, &tempDirSpec, tempSpec.name, nil, 0, true);
	if (noErr != err) return err;
	
	/* Convert temp file back over infile */
	convert_file (temp_filename, O_RDONLY | OPEN_BINARY, infile, O_WRONLY | O_CREAT | O_TRUNC, bin);
	
	/* Delete the temp file */
	FSpDelete (&tempSpec);
	
	return 0;
}

char scratchPath[1024];

#undef mkdir
#undef open
#undef creat
#undef fopen
#undef chdir
#undef access
#undef opendir
#undef stat
#undef rename
#undef rmdir
#undef unlink
#undef chmod
#undef lstat

#if 0
#	define TRACE(arg) \
	{ \
		char cur_wd[512]; \
		getcwd(cur_wd, 512); \
		fprintf(stderr,"%.40s> ", cur_wd); \
		fprintf arg; \
		fprintf(stderr,"\n"); \
	}
#else
#	define TRACE(arg)
#endif

int
macos_mkdir( const char *path, int oflag )
{
	int res = mkdir( macos_fixpath(path) );
	TRACE((stderr, "mkdir %s -> %d", macos_fixpath(path), res));
	return res;
}

int
macos_open( const char *path, int oflag, ... )
{
	int	r;
	char	*macPath;
	
	macPath = macos_fixpath(path);

	r = open( macPath, oflag );
	TRACE((stderr, "open %s -> %d", macPath, r));
	if( r < 0 )
	{
		r = open( macPath+1, oflag );
		TRACE((stderr, "open %s -> %d", macPath + 1, r));
	}

	return r;
}

int
macos_chmod( const char *path, mode_t mode )
{
	int res = chmod(macos_fixpath(path), mode);
	TRACE((stderr, "chmod %s %x -> %d", macos_fixpath(path), mode, res));
	return res;
}

int
macos_creat( const char *path, mode_t mode )
{
	int res = creat( macos_fixpath(path) );
	TRACE((stderr, "creat %s -> %d", macos_fixpath(path), res));
	return res;
}

FILE *
macos_fopen( const char *path, const char *mode )
{
	FILE	*fp;
		
	fp = fopen(macos_fixpath(path), mode);
	
	TRACE((stderr, "fopen %s %x -> %d", macos_fixpath(path), mode, fp != 0L));
	
	/* I'm getting ENOTDIR, CVS expects ENOENT */
	
	if( (fp == NULL) && (errno == ENOTDIR) ) errno = ENOENT;
	
	return fp;
}

int
macos_chdir( const char *path )
{
	int	r;
	char	*macPath;
	
	macPath = macos_fixpath(path);

	r = chdir(macPath);
	TRACE((stderr, "chdir %s -> %d", macPath, r));
	if( r < 0 )
	{
		r = chdir(macPath+1);
		TRACE((stderr, "chdir %s -> %d", macPath+1, r));
	}

	return r;
}

int
macos_access(const char *path, int amode)
{	
	int res = access( macos_fixpath(path), amode );
	TRACE((stderr, "access %s %x -> %d", macos_fixpath(path), amode, res));
	return res;
}

DIR *
macos_opendir(const char *path)
{
	DIR *res = opendir( macos_fixpath(path) );
	TRACE((stderr, "opendir %s -> %d", macos_fixpath(path), res != 0L));
	return res;
}

int
macos_stat (const char *path, struct stat *ststr)
{
	int res = stat( macos_fixpath(path), ststr );
	TRACE((stderr, "stat %s -> %d", macos_fixpath(path), res));
	return res;
}

int
macos_lstat (const char *path, struct stat *ststr)
{
	int res = lstat( macos_fixpath(path), ststr );
	TRACE((stderr, "lstat %s -> %d", macos_fixpath(path), res));
	return res;
}

int
macos_rename (const char *path, const char *newpath)
{
	char	macPath_from[1024], macPath_to[1024];
	int res;
	
	strcpy( macPath_from, macos_fixpath(path) );
	strcpy( macPath_to, macos_fixpath(newpath) );

	res = rename( macPath_from, macPath_to );
	TRACE((stderr, "rename %s %s -> %d", macPath_from, macPath_to, res));
	return res;
}

int
macos_rmdir (const char *path)
{
	int res = rmdir( macos_fixpath(path) );
	TRACE((stderr, "rmdir %s -> %d", macos_fixpath(path), res));
	return res;
}

int
macos_unlink (const char *path)
{
	int res;
	chmod(path, S_IRWXU);
	res = unlink( macos_fixpath(path) );
	TRACE((stderr, "unlink %s -> %d", macos_fixpath(path), res));
	return res;
}

char *
macos_fixpath (const char *path)
{
	char		*sepCh;
	
	// tip to not make unix->mac path conversion when
	// this should not happen.
	// - this should be obsolete now (4/7/98). Instead
	// gives a Mac path for the HOME env. variable
	// like "System:Preferences" and cvs will convert it
	// back to a Unix path.
	if(strncmp(path, "@@@", 3) == 0)
	{
		strcpy( scratchPath, path + 3 );
		Debugger();
			// we should never get here anymore
		return scratchPath;
	}
	
	// special case chdir("..")
	if(strcmp(path, "..") == 0)
	{
		strcpy( scratchPath, "::" );
		return scratchPath;
	}
	
	// if it is an absolute Unix path, do
	// not prefix by ':'
	if(path[0] != '/')
		strcpy( scratchPath, ":" );
	else
	{
		scratchPath[0] = '\0';
		++path;
	}

	// Unix "./xxx" is equivalent to ":xxx"
	if( (*path == '.') && (*(path+1) == '/') )
		strcat( scratchPath, path+2 );
	// ???
	else if( strcmp(path, ".") != 0 )
		strcat( scratchPath, path );

	// Finally replace '/' by ':'
	while( (sepCh = strchr(scratchPath, '/')) != NULL )
		*sepCh = ':';
	
	return scratchPath;
}
