/******************************************************************************
 SyGDirUtil_UNIX.cc

	Directory utilities implemented for the UNIX System.

	Copyright © 1997 by Glenn W. Bach. All rights reserved.

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


#include "SyGDirUtil.h"

#include <JInPipeStream.h>
#include <JUserNotification.h>

#include <jProcessUtil.h>
#include <jStreamUtil.h>
#include <jErrno.h>
#include <jDirUtil.h>
#include <jFileUtil.h>
#include <jGlobals.h>

#include <unistd.h>
#include <stdlib.h>
#include <fstream.h>
#include <iostream.h>
#include <iomanip.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <jAssert.h>

static const JCharacter* kGXFMErrorTerminatorStr = "GXFM Termination String.";

/******************************************************************************
 CreateDirectory

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

JMoveError
gRenameDirEntry
	(
	const JCharacter* oldName,
	const JCharacter* newName
	)
{
	jclear_errno();
	if (rename(oldName, newName) == 0)
		{
		return kNoMoveError;
		}

	const int err = jerrno();
	if (err == EEXIST || err == EISDIR || err == ENOTEMPTY)
		{
		return kNameAlreadyExists;
		}
	else if (err == EBUSY)
		{
		return kMoveDirBusy;
		}
	else if (err == EACCES)
		{
		return kMoveAccessDenied;
		}
	else if (err == ENOENT)
		{
		return kMoveBadPath;
		}
	else if (err == ENOTDIR)
		{
		return kMoveComponentNotDir;
		}
	else if (err == ENOSPC)
		{
		return kDeviceFull;
		}
	else if (err == EXDEV)
		{
		return kAcrossFileSystem;
		}
	else
		{
		return kMoveUnknownError;
		}
}

/******************************************************************************
 SystemCall 

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

JBoolean 
SystemCall
	(
	const JString& sysCmd,
	const JBoolean report
	)
{
/*	const int errfd = fileno(stderr);
	const int tempfd = dup(errfd);
	
	int spfd[2];
	socketpair(PF_UNIX, SOCK_STREAM, 0, spfd);
	
	if (dup2(spfd[0], errfd) != errfd)
		{
		JGetUserNotification()->ReportError("Socket error!");
		return kFalse;
		}

	ifstream is(spfd[1]);

	system(sysCmd);

	cerr << endl << kGXFMErrorTerminatorStr << endl << flush;
	
	JString output;
	JCharacter line[255];

	JString tempLine;
	
	while (tempLine != kGXFMErrorTerminatorStr)
		{
		output += tempLine;
		is.getline(line,255);
		tempLine = line;
		}

	dup2(tempfd, errfd);
	
	if (output.GetLength() > 0)
		{
		JGetUserNotification()->ReportError(output);	
		return kFalse;
		}		
		
	return kTrue;
	
	close(tempfd);
	close(spfd[0]);
	close(spfd[1]);*/
	
	int errFD;
	JError err = JExecute(sysCmd, NULL, 
						  kJIgnoreConnection, NULL,
						  kJIgnoreConnection, NULL,
						  kJCreatePipe, &errFD);
						  
	if (err.OK())
		{
		JString output;
		JInPipeStream input(errFD, kTrue);
		while (input.good())
			{
			const JString line = JReadLine(input);
			output += line;
			}
		if (output.GetLength() > 0)
			{
			if (report)
				{
				JGetUserNotification()->ReportError(output);
				}
			return kFalse;
			}		
		}
	else
		{
		if (report)
			{
			JGetUserNotification()->ReportError(err.GetMessage());
			}
		return kFalse;
		}
	return kTrue;
}

/******************************************************************************
 Remove

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

void  
gRemove
	(
	const JCharacter* src
	)
{
	JString homeDir;
	
	if (JDirectoryExists(src))
		{
		JError err = JRemoveDirectory(src);
		if (err.OK())
			{
			return;
			}
		}
	
	if (JGetHomeDirectory(&homeDir))
		{
		JString trashName = homeDir + ".trashcan";
		
		if (JFileExists(trashName))
			{
			JGetUserNotification()->ReportError("There is a file named \".trashcan\"");
			return;
			}
		if (!JDirectoryExists(trashName))
			{
			JError err = JCreateDirectory(trashName);
			if (!(err.OK()))
				{
				JGetUserNotification()->ReportError("Error creating trashcan folder!");
				return;
				}
			}
		
		JBoolean force = kTrue;
		JBoolean firstCheck = kFalse;
		JAppendDirSeparator(&trashName);	
		gMove(src, trashName, force, &firstCheck, &force);
		}
	else
		{
		JGetUserNotification()->ReportError("You need to have a home directory.");
		}
}


/******************************************************************************
 Copy

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

JBoolean  
gCopy
	(
	const JCharacter* src, 
	const JCharacter* dest,
	const JBoolean force,
	JBoolean* nextForce,
	JBoolean* firstCheck
	)
{
	JString name(src);

	if (name.GetLastCharacter() == '/')
		{
		name.RemoveSubstring(name.GetLength(), name.GetLength());
		}

	JString path;
	JString oldName;
	
	JSplitPathAndName(name, &path, &oldName);

	JString newName;
	JString destName(dest);
	
	if (JDirectoryExists(destName))
		{
		JAppendDirSeparator(&destName);
		newName = destName + oldName;
		
		JString testSrc(src);
		JAppendDirSeparator(&testSrc);
		
		if ((path == destName) || (testSrc == destName) )
			{
			return kFalse;
			}
		}
		
	else
		{
		newName = destName;
		
		if (newName == oldName)
			{
			return kFalse;
			}
		}
	
	JBoolean localForce = force;
	
	if ( (*firstCheck) && JNameUsed(newName) )
		{
		if (force == kFalse)
			{
			JString askMsg = "Replace existing files automatically?";
			localForce = JGetUserNotification()->AskUserYes(askMsg);
			*nextForce = localForce;
			}
		*firstCheck = kFalse;
		}
	
	if (JNameUsed(newName) && !localForce)
		{
		JString askMsg = "Replace existing file \"" + newName + "\"?";
		
		if (!JGetUserNotification()->AskUserYes(askMsg))
			{
			return kTrue;
			}
		}

	JString sysCmd = "cp -Rd \"" + name + "\" \"" + newName + "\"";
	if (SystemCall(sysCmd))
		{
		return kTrue;
		}
	
	return kFalse;
	
}


/******************************************************************************
 Delete

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

void  
gDelete
	(
	const JCharacter* src
	)
{
	JString sSrc(src);
	JString sysCmd = "rm -rf \"" + sSrc + "\"";
	SystemCall(sysCmd);
}

/******************************************************************************
 Move

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

JBoolean  
gMove
	(
	const JCharacter* src, 
	const JCharacter* dest,
	const JBoolean force,
	JBoolean* nextForce,
	JBoolean* firstCheck
	)
{
	JString name(src);

	if (name.GetLastCharacter() == '/')
		{
		if (name == dest)
			{
			return kFalse;
			}
		name.RemoveSubstring(name.GetLength(), name.GetLength());
		}

	JString path;
	JString oldName;
	
	JSplitPathAndName(name, &path, &oldName);
	
	JString testSrc(src);
	JAppendDirSeparator(&testSrc);
	JString testDest(dest);
	JAppendDirSeparator(&testDest);
	
	if ((path == testDest) || (testSrc == testDest) )
		{
		return kFalse;
		}
	
	JString newName = testDest + oldName;
	
	JBoolean localForce = force;
	
	if ( (*firstCheck) && JNameUsed(newName) )
		{
		if (force == kFalse)
			{
			JString askMsg = "Replace existing files automatically?";
			localForce = JGetUserNotification()->AskUserYes(askMsg);
			*nextForce = localForce;
			}
		*firstCheck = kFalse;
		}
	
	if (JNameUsed(newName) && !localForce)
		{
		JString askMsg = "Replace existing file \"" + newName + "\"?";
		
		if (!JGetUserNotification()->AskUserYes(askMsg))
			{
			return kTrue;
			}
		}
		
	JMoveError err = gRenameDirEntry(name, newName);

	if (err == kAcrossFileSystem)
		{
		if (!gCopy(src, testDest, localForce, nextForce, firstCheck))
			{
			JGetUserNotification()->ReportError("Move Failed!");
			return kFalse;
			}
		gDelete(src);
		return kTrue;
		}

	else if (err != kNoMoveError)
		{
		JGetUserNotification()->ReportError("Move Failed!");
		return kFalse;
		}
		
	return kTrue;

}

