// RECEXE.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#include <e32uid.h>
#include <f32file.h>
#include <apacmdln.h>
#include <apaid.h>
#include "recexe.h"
#include "recstd.h"

const TInt KDefaultMinHeapSize=0x10000; // !! for now
const TInt KDefaultMaxHeapSize=0x200000; // !! for now

//
// Class CApaExeRecognizer
//

CApaFileRecognizerType::TRecognizedType CApaExeRecognizer::DoRecognizeFileL(RFs& /*aFs*/,TUidType aUidType)
// Recognises executables (eg .EXE or .DXE)
	{
	if (aUidType[0]==KExecutableImageUid)
		iRecognizedType=EProgram; 
	return iRecognizedType;
	}


#if defined(__WINS__)
typedef TInt (*ExeEntryPoint)(TAny*);

_LIT(KSSFormatString,"%S %S");

TInt ExeThreadStartFunction(TAny* aParam)
	{
	CTrapCleanup* trapCleanup=CTrapCleanup::New();
	// recreate the commandline
	CApaCommandLine* commandLine=NULL;
	TRAPD(err, commandLine = CApaCommandLine::NewL(*(TDesC*)aParam) );
	User::Free(aParam);
	if (err!=KErrNone)
		return err;
	//
	// build up a shortened commandline to pass on
	TPtrC libName=commandLine->LibraryName();
	TPtrC docName=commandLine->DocumentName();
	TPtrC8 tailEnd=commandLine->TailEnd();
	TInt len=docName.Length()+tailEnd.Length()+1; 
	TBuf<KMaxCommandLine> commandLineBuf;
	if (len<=KMaxCommandLine)
		commandLineBuf.Format(KSSFormatString,&docName,&tailEnd);
	else
		{
		TPtrC8 leftTail=tailEnd.Left(KMaxCommandLine-docName.Length());
		commandLineBuf.Format(KSSFormatString,&docName,&leftTail);
		}
	//
	// load the library
	if (!(libName.Length()>0))
		return KErrBadName;
	RLibrary lib;
	err = lib.Load(libName);
	if (!err)
		{
		ExeEntryPoint exeFunc=(ExeEntryPoint)lib.Lookup(1);
		if (!exeFunc)
			err=KErrBadLibraryEntryPoint;
		else
			err=(*exeFunc)(&commandLineBuf);
		}
	//
	delete commandLine;
	delete trapCleanup;
	return(err);
	}
#endif


TThreadId CApaExeRecognizer::RunL(TApaCommand aCommand,const TDesC* aDocFileName,const TDesC8* aTailEnd) const
// Run the recognized exe
// the commandline passed through is "aDocFileName"+" "+"aTailEnd"
//
	{
	__ASSERT_ALWAYS(iFullFileName,Panic(EPanicNoFileRecognized));

	if (iRecognizedType!=EProgram)
		User::Leave(KErrNotSupported);

	TThreadId threadId;
#if defined(__WINS__)
	CApaCommandLine* commandLine=CApaCommandLine::NewLC();
	commandLine->SetCommandL(aCommand);
	commandLine->SetLibraryNameL(*iFullFileName);
	if (aDocFileName)
		commandLine->SetDocumentNameL(*aDocFileName);
	if (aTailEnd)
		commandLine->SetTailEndL(*aTailEnd);
	//
	RThread process;
	TPtrC libName=commandLine->LibraryName();
	TBuf<KMaxFileName> threadName;
	TInt err;
	TInt num=0;
	TParsePtrC fileName(libName);
	TPtrC baseName=fileName.Name();
	do
		{
		threadName.Format(_L("%S%02d"),&baseName,num++);
		err=process.Create(threadName,ExeThreadStartFunction,KDefaultStackSize,KDefaultMinHeapSize,KDefaultMaxHeapSize,NULL);
		} while(err==KErrAlreadyExists);
	User::LeaveIfError(err);
	TPtrC fullCommandLine=commandLine->FullCommandLine();
	RHeap* heap=process.Heap();
	RHeap* originalHeap=User::SwitchHeap(heap);
	HBufC* commandLineBuf=fullCommandLine.Alloc();
	User::SwitchHeap(originalHeap);
	CleanupStack::PopAndDestroy(); // commandLine
	commandLine = NULL;
	if (!commandLineBuf)
		{
		process.Close();
		User::LeaveNoMemory();
		}
	process.SetInitialParameter(commandLineBuf);
	threadId = process.Id();
	//
#else
	RProcess process;
	TPtrC docName;
	if (aDocFileName)
		docName.Set(*aDocFileName);
	TPtrC8 tailend;
	if (aTailEnd)
		tailend.Set(*aTailEnd);
	TInt len=docName.Length()+tailend.Length()+1; 
	if (len<=KMaxCommandLine)
		{
		TBuf<KMaxCommandLine> buf;
		_LIT(KSSFormatString,"%S %S");
		buf.Format(KSSFormatString,&docName,&tailend);
		User::LeaveIfError(process.Create(*iFullFileName,buf));
		}
	else
		User::LeaveIfError(KErrArgument);
	// get the threadId of the main thread in the exe's process
	TFullName fullName(process.Name());
	_LIT(KCCMain,"::Main");
	fullName.Append(KCCMain);
	TFindThread fT(fullName);
	fT.Next(fullName);
	RThread thread;
	thread.Open(fT);
	threadId = thread.Id();
	thread.Close();
#endif
	//
	process.Resume();
	process.Close();
	return threadId;
	};


//
// dll stuff...
//

EXPORT_C CApaFileRecognizerType* CreateRecognizer()
// The gate function - ordinal 1
//
	{
	CApaFileRecognizerType* thing=NULL;
	thing = new CApaExeRecognizer();
	return thing; // null if new failed
	}


GLDEF_C void Panic(TAprPanic aPanic)
//
// Panic the process with RECAPP as the category.
//
	{
	_LIT(KExePanic,"RECEXE");
	User::Panic(KExePanic,aPanic);
	}

GLDEF_C TInt E32Dll(TDllReason /*aReason*/)
// DLL entry point
//
	{
	return KErrNone;
	}
