/* $Header$ */

// FILEOPS Non-Visual Component
// Copyright (C) OMNIS Holdings Inc 1998

/* Changes
Date			Edit			Fault				Description
06-Jun-25	jmg1531		ST/EC/1954	FileOps copy always replaced the destination on Linux.
03-Jun-25	jmg1529		ST/EC/1954	Made isDirectory a static method.
29-May-25	jmg1527		ST/FU/926		Copy/Move folder command just deleted the destination folder if it exists and 'replace' param is true.
09-Apr-25	caa2656		ST/FU/921		$isdirectory was not finding paths ending in path separator.
03-Mar-25	caa2645		ST/EC/1938	FileOps Workers were not extracting all parameter correctly.
30-Jan-25	caa2641		ST/FU/915		Issues on Linux/Windows when using isDirectory internally.
19-Dec-24	caa2630		ST/FU/909		Deprecated $copyfile and $move file and added $copy and $move.
17-Dec-24	caa2632		ST/FU/912		Ensure $writefile uses default parameters when the params are null.
17-Dec-24	caa2631		ST/FU/911		Added create missing directories parameter to $writefile.
24-Oct-24	PK1796		ST/FU/653		$readfile does not work with binary variables that have been initialised with #NULL
01-Oct-24	caa2616		ST/EC/1910	Added $joinpath.
25-Jun-24	caa2595		ST/EC/1886	Removed lock on main thread when cancelling a fileops worker.
19-Jun-24	caa2593		ST/EC/1885	Added tag parameter to fileops workers.
20-May-24	caa2581		ST/EC/1876	Added $isdirectory and $isfile.
30-Apr-24	caa2579		ST/EC/1872	Avoid crashing if $readfile attempts reading more than 0x7ffffff0.
30-Apr-24	caa2578		ST/EC/1871	e_readtoolarge was never returned by instance $readfile.
28-Feb-24	caa2554		ST/EC/1495	Added option to define exclusion filters in $filelist by prepending !!.
24-Feb-24	caa2553		ST/FU/719		Added support for multiple selection to FileOps.
06-Feb-24	rmm11962	ST/FU/857		Added static method FileOps.$spaceinfo.
26-Oct-23 caa2540		ST/EC/1770	Progress had stopped working when reusing the same worker object.
02-Oct-23	caa1545		ST/EC/1770	Added progress tracking to FileOps Workers.
28-Sep-23	caa0334								Added FileOps Workers to move, copy and delete files or folders.
18-Aug-23	caa2042		ST/EC/1805	Added $filepath property to get an opened file's path.
09-May-22	rmm11553	ST/JS/2976	The dialog opened by FileOps.$putfilename() now prompts if the file already exists when running on Windows, unless kFileOpsWindowsDisablePrompt is passed in the iFlags parameter.
																This makes the default behaviour compatible with macOS (the file already exists prompt cannot be disabled on macOS).
02-Mar-22	rmm11466							Implemented new build numbering and serial numbers to support Studio Now releases and periodic releases.
16-Feb-22	rmm11443							Small improvements to parameter highlighting.
16-Jul-21	rmm11107	ST/FU/798		Added file filter classes.
02-Jul-21	caa2019		ST/FU/796		Added InfoCalcDirectorySize to $getfileinfo.
19-May-21	rmm11014	ST/NV/074		Added FileOps.$readfile() and FileOps.$writefile() static methods.
15-Oct-20	caa0121		ST/FU/774		$createdir can create unexisting parent folders via optional boolean parameter.
07-Aug-20	CR0671		ST/WO/2568	$readfile now returns binary into fftPicture.
03-Mar-20	caa0002		ST/PF/1077	Deleting file/folder now exhibits consistent cross-platform behaviour.
14-Jan-20	rmm10372	ST/BE/1491	Added static method FileOps.$selectfilesinsystemviewer.
21-Nov-19	caa0001		ST/EC/1586	Fileops xcomp $readcharacter crashes when file size is greater than 2GB.
02-Sep-19	rmm10234	ST/EC/1565	Added parameter to $writecharacter() that can be used to control whether to write a BOM when writing to the start of the file.
24-Jun-19	rmm10137	ST/FU/744		The last 3 parameters of FileOps.$splitpathname are now optional.
21-Jul-17	rmm9434		ST/FU/709		Added bCheckUTF8 parameter to $readcharacter.
23-May-17	rmm9393		ST/EC/1461	Deprecated $readentirefile and $writeentirefile.
14-Feb-17	rmmheadless						Support for headless Omnis server.
30-Aug-16	rmm9025		ST/NV/059		Added support for long pathnames to FileOps and to core qfile.
26-Aug-16	rmm9020								Renamed CHR...EncodedCharacters to CHR...Utf8 and modified their interface.Also made some other small improvements.
19-Aug-16	rmm9010		ST/EC/1416	Added support for large files (files with a size > maximum signed 32 bit integer).
19-Aug-16	rmm8939a							Tidied up some parameter issues.
13-Jun-16	rmm8939		ST/FU/670		Code assistant now restricts constants to the set suitable for function and external component object method parameters.
07-Apr-16	rmm8840		ST/PF/947		Cocoa crash.
24-Aug-15	CR0270		ST/BE/1048	Cocoa path separator needed for $parentdir.
18-Jun-13	rmm8013								Updated Omnis X dictionary and added new JNI API to return dictionary as XML.
19-Mar-13	rmm64bit6							Changes required for compiling with 64 bit target.
02-Mar-10	rmm6921		ST/PF/523		Added offset and length arguments to $readcharacter.
02-Mar-10	rmm6920		ST/FU/577		Added append option to $writecharacter.
23-Jul-08	rmm_mobile						Added hidden static functions: $extractcab and $makecab.
15-Jul-08	jc00010		ST/EC/1152	filelist now returns an error when supplied with an empty path
08-Oct-07	rmm6248		ST/FU/530		OSX Package flags did not work correctly for $selectdirectory.
11-May-07 AE6785		ST/EC/1101	Readuntil failed with files > 1K
13-Mar-07	rmm6016		ST/EC/1088	Removed Mac restriction on size of file that can be read with $readfile.
09-Feb-07 AE6765		ST/EC/1067	Createtempfile didn't obtain a full name so getfileinfo failed
04-Jan-07	rmm5941		ST/FU/493		MacIntel issues with creator and type codes.
03-Oct-06	AE6728		ST/FU/492		Matchsearchstr failed if multiple extension markers
07-Sep-06	rmm5885		ST/DC/351		Tidied up method descriptions.
31-Aug-06	AE6723		ST/EC/1031	Added linkedname to fileops
11-Aug-06 MHn0483		ST/FU/486		Added kFileOpsInfoIsDirectory.
17-May-06 MHn0470		ST/FU/459		setunixpermissions support for osx
28-Apr-06 AE6692		ST/FU/459		Added setunixpermissions
10-Apr-06	AE6684		ST/EC/986		$readuntil didn't returned the last block (if string not found)
14-Jun-05	PK6727		ST/fu/440		$parentdir added as static method
18-Apr-05	rmm5334		UN/IE/092		...continued: $importencoding and $exportencoding.
14-Apr-05	rmm5333		UN/IE/092		Unicode version import-export now supports different Ansi code pages.
17-Sep-04 MHn0407								Added ECM_SQLOBJECT_COPY to fileops
14-Sep-04	rmm5093								Fileops now recognises Unicode encodings; Formfile recognises some additional Unicode encodings.
01-Jul-04	rmm5009		ST/EC/821		FileOps component is now multi-threaded.
18-May-04	AE6466		ST/NV/038 	Added $readuntil
26-Mar-04 MHn0334		ST/FU/402		Added support for selecting apps in OSX.
19-Feb-04	rmmuni_osx6						Mac OSX Unicode Port.
19-Feb-04 MHn0318		ST/EC/805		Fixed $converthfspathtoposixpath for windows.
12-Feb-04	ijt239		ST/EC/752		Ensured Mac parameters are implemented for $createfile.
28-Oct-03 ijt218		ST/NV/037		Added callback to core versions of convertHFStoPosix and convertPosixToHFS.
29-Sep-03	AE6342		ST/EC/777		Writing an empty binary var caused a GPF
17-Dec-02 MHn0201								added $converthfstoposix and $convertposixtohfs functions for OSX.
26-Feb-01	rmm4067								$setfileinfo for an object now returns the same value as the static $setfileinfo.
26-Feb-01	AE5184		ST/EC/634		$write now returns false if writing to a readonly file
13-Jan-01	rmm4013		ST/EC/629		Reading zero bytes now clears the destination field, like the read file external commands.
03-Jan-01	rmm3994		ST/FU/304		Problem with $filelist on the Mac.
29 NOV 00 mpmCarbon							Implements $openresources, $readentirefile and $writeentirefile
19-Oct-00	AE5106								Installer changes
30-Jul-99 MHn0082		ST/MC/131		(Windows Only) Force file dialogs to top and ensure that omnis window is parent.
06-Apr-99 MHn0070		ST/NV/026		Out of memory error is now returned on Mac for $readfile instead of crash.
04-Feb-99 MHn0032		ST/NV/019		Added kFIleOpsResSize and kFileOpsActualResSize for returning resource fork information.
28-Aug-98	AE4792		ST/FU/177		Fileops:$putfilename/getfilename/selectdirectory was failing
*/

#include <extcomp.he>
#include <extfval.he>
#include <hwnd.he>
#include <gdi.he>
#include <extdam.he>	// rmm5093
#include "fileops.he"
#include "dmconst.he"	// rmm5334
#include <filesystem> // rmm11962
#include <random> // caa2630
#include <sstream> // caa2630
// Start rmm8939a
const qshort	cFILEOPSfirstSetFileInfoConstant = 20053,
							cFILEOPSlastSetFileInfoConstant = 20058;
// End rmm8939a

#define VERSION_MAJOR			1				// Major Version number - Update on MAJOR release builds  
#define VERSION_MINOR			4				// Minor Version number - Update on release builds 

std::vector<FileOpsWorker*> FileOpsWorker::smDetachedWorkers; // Vectore of all currently detached workers // caa2595
UINT_PTR FileOpsWorker::smCheckDetachedWorkersTimerId = 0;		// ID of timer used for checking detached worker // caa2595

// caa0334 start
qlong FileOpsCopyWorker::smThreadCount   = 0;
qlong FileOpsMoveWorker::smThreadCount   = 0;
qlong FileOpsDeleteWorker::smThreadCount = 0;

ECOobject fileObjects[ cObject_Count ] = 
{
	cObject_FileOps,								2000, 0, 0,
  cObject_FileOpsCopyWorker,      2001, EXTD_OBJFLAG_WORKER, RES_LIB_NAME,
  cObject_FileOpsMoveWorker,      2002, EXTD_OBJFLAG_WORKER, RES_LIB_NAME,
  cObject_FileOpsDeleteWorker,    2003, EXTD_OBJFLAG_WORKER, RES_LIB_NAME
};
// caa0334 end

// caa2670 start
static ECOparam FILEOPSgetFileIDParams[] =
{
	7019, fftCharacter, 0, 0,
};
#define FILEOPSgetFileIDParamsCount (sizeof(FILEOPSgetFileIDParams)/sizeof(FILEOPSgetFileIDParams[0]))
// caa2670 end

// Start rmm8013: Static function parameter definitions added here; also updated fileStaticFuncs array
// $createdir(cPath)
static ECOparam FILEOPScreateDirParams[] =
{
	7019, fftCharacter,	0, 0,
	7050, fftBoolean, 0, 0, // caa0121
};
#define FILEOPScreateDirParamsCount (sizeof(FILEOPScreateDirParams)/sizeof(FILEOPScreateDirParams[0]))

// $deletefile(cPath[,deleteContents=kFalse,recursive=kFalse])
static ECOparam FILEOPSdeleteFileParams[] =
{
	7019, fftCharacter,	0, 0,
	7048, fftBoolean, EXTD_FLAG_PARAMOPT, 0, // caa0002
	7049, fftBoolean, EXTD_FLAG_PARAMOPT, 0 // caa0002
};
#define FILEOPSdeleteFileParamsCount (sizeof(FILEOPSdeleteFileParams)/sizeof(FILEOPSdeleteFileParams[0]))

// caa2630 start
// $copyfile(cFromPath,cToPath) // caa2630: deprecated! please only enhance $copy instead.
static ECOparam FILEOPScopyFileParams[] =
{
	7020, fftCharacter,	0, 0,
	7021, fftCharacter,	0, 0,
};
#define FILEOPScopyFileParamsCount (sizeof(FILEOPScopyFileParams)/sizeof(FILEOPScopyFileParams[0]))

// $movefile(cFromPath,cToPath) // caa2630: deprecated! please only enhance $move instead.
static ECOparam FILEOPSmoveFileParams[] =
{
	7020, fftCharacter,	0, 0,
	7021, fftCharacter,	0, 0,
};
#define FILEOPSmoveFileParamsCount (sizeof(FILEOPSmoveFileParams)/sizeof(FILEOPSmoveFileParams[0]))

static ECOparam FILEOPScopyParams[] =
{
  7020, fftCharacter,                0, 0,  // cFromPath
  7021, fftCharacter,                0, 0,  // cToPath
  7050, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bCreatePath=kFalse
  7066, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bReplace=kFalse
  7067, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bMerge=kFalse
};
#define FILEOPScopyParamsCount (sizeof(FILEOPScopyParams)/sizeof(FILEOPScopyParams[0]))

static ECOparam FILEOPSmoveParams[] =
{
  7020, fftCharacter,                0, 0,  // cFromPath
  7021, fftCharacter,                0, 0,  // cToPath
  7050, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bCreatePath=kFalse
  7066, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bReplace=kFalse
  7067, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bMerge=kFalse
};
#define FILEOPSmoveParamsCount (sizeof(FILEOPSmoveParams)/sizeof(FILEOPSmoveParams[0]))

// caa2630 end

// caa2593 start
static ECOparam FILEOPSdeleteWorkerParams[] =
{
	7019, fftCharacter,	0, 0,									// cPath
	7048, fftBoolean, EXTD_FLAG_PARAMOPT, 0,	// deleteContents=kFalse
	7049, fftBoolean, EXTD_FLAG_PARAMOPT, 0,	// recursive=kFalse
	7064, fftNone, EXTD_FLAG_PARAMOPT, 0,			// vTag
};
#define FILEOPSdeleteWorkerParamsCount (sizeof(FILEOPSdeleteWorkerParams)/sizeof(FILEOPSdeleteWorkerParams[0]))

static ECOparam FILEOPScopyWorkerParams[] =
{
	7020, fftCharacter,	0, 0,									// cFromPath
	7021, fftCharacter,	0, 0,									// cToPath
	7064, fftNone, EXTD_FLAG_PARAMOPT, 0,			// vTag
  7050, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bCreatePath=kFalse // caa2630
  7066, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bReplace=kFalse // caa2630
  7067, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bMerge=kFalse // caa2630
};
#define FILEOPScopyWorkerParamsCount (sizeof(FILEOPScopyWorkerParams)/sizeof(FILEOPScopyWorkerParams[0]))


static ECOparam FILEOPSmoveWorkerParams[] =
{
	7020, fftCharacter,	0, 0,									// cFromPath
	7021, fftCharacter,	0, 0,									// cToPath
	7064, fftNone, EXTD_FLAG_PARAMOPT, 0,			// vTag
  7050, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bCreatePath=kFalse // caa2630
  7066, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bReplace=kFalse // caa2630
  7067, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bMerge=kFalse // caa2630
};
#define FILEOPSmoveWorkerParamsCount (sizeof(FILEOPSmoveWorkerParams)/sizeof(FILEOPSmoveWorkerParams[0]))
// caa2593 end

// Start rmm11553
// $putfilename(&cPath[,cPrompt,cFilter,cInitialDirectory,iFlags])#
static ECOparam FILEOPSputFileNameParams[] =
{
	7057, fftCharacter,	EXTD_FLAG_PARAMALTER, 0,
	7022, fftCharacter,	EXTD_FLAG_PARAMOPT, 0,
	7023, fftCharacter,	EXTD_FLAG_PARAMOPT, 0,
	7024, fftCharacter,	EXTD_FLAG_PARAMOPT, 0,
	7025, fftInteger, EXTD_FLAG_PARAMOPT, MAKELONG(20069, 20071),
};
#define FILEOPSputFileNameParamsCount (sizeof(FILEOPSputFileNameParams)/sizeof(FILEOPSputFileNameParams[0]))

// $getfilename(&vListOrCharacter[,cPrompt,cFilter,cInitialDirectory,iFlags])
static ECOparam FILEOPSgetFileNameParams[] =
{
	7063, fftNone,	    EXTD_FLAG_PARAMALTER, 0, // rmm11443 // caa2553
	7022, fftCharacter,	EXTD_FLAG_PARAMOPT,   0,
	7023, fftCharacter,	EXTD_FLAG_PARAMOPT,   0,
	7024, fftCharacter,	EXTD_FLAG_PARAMOPT,   0,
	7025, fftInteger,   EXTD_FLAG_PARAMOPT, MAKELONG(20069, 20070), // rmm8939 // caa2019
};
#define FILEOPSgetFileNameParamsCount (sizeof(FILEOPSgetFileNameParams)/sizeof(FILEOPSgetFileNameParams[0]))
// End rmm11553

// $selectdirectory(&cPath[,cPrompt,cInitialDirectory,iFlags])
static ECOparam FILEOPSselDirParams[] =
{
	7058, fftCharacter,	EXTD_FLAG_PARAMALTER, 0, // rmm11443
	7022, fftCharacter,	EXTD_FLAG_PARAMOPT, 0,
	7024, fftCharacter,	EXTD_FLAG_PARAMOPT, 0,
	7025, fftInteger, EXTD_FLAG_PARAMOPT, MAKELONG(20069, 20070), // rmm8939 // caa2019
};
#define FILEOPSselDirParamsCount (sizeof(FILEOPSselDirParams)/sizeof(FILEOPSselDirParams[0]))

// $doesfileexist(cPath)
static ECOparam FILEOPSdoesFileExistParams[] =
{
	7019, fftCharacter,	0, 0,
};
#define FILEOPSdoesFileExistParamsCount (sizeof(FILEOPSdoesFileExistParams)/sizeof(FILEOPSdoesFileExistParams[0]))

// $changeworkingdir(cPath)
static ECOparam FILEOPScwdParams[] =
{
	7019, fftCharacter,	0, 0,
};
#define FILEOPScwdParamsCount (sizeof(FILEOPScwdParams)/sizeof(FILEOPScwdParams[0]))

// $splitpathname(cPath,&cDriveName[,&cDirectoryName,&cFileName,&cFileExtension])
static ECOparam FILEOPSsplitParams[] =
{
	7019, fftCharacter,	0, 0,
	7026, fftCharacter,	EXTD_FLAG_PARAMALTER, 0,
	7027, fftCharacter,	EXTD_FLAG_PARAMOPT | EXTD_FLAG_PARAMALTER, 0,	// rmm10137
	7028, fftCharacter,	EXTD_FLAG_PARAMOPT | EXTD_FLAG_PARAMALTER, 0,	// rmm10137
	7029, fftCharacter,	EXTD_FLAG_PARAMOPT | EXTD_FLAG_PARAMALTER, 0,	// rmm10137
};
#define FILEOPSsplitParamsCount (sizeof(FILEOPSsplitParams)/sizeof(FILEOPSsplitParams[0]))

// $filelist(iInclude,cPath[,iInformation,cFilter,&cErrorText])			
static ECOparam FILEOPSfileListParams[] =
{
	7030, fftInteger, 0, MAKELONG(20050, 20052), // rmm8939
	7019, fftCharacter,	0, 0,
	7031, fftInteger, EXTD_FLAG_PARAMOPT, MAKELONG(20053, 20067), // rmm8939
	7023, fftCharacter,	EXTD_FLAG_PARAMOPT, 0,
	7032, fftCharacter,	EXTD_FLAG_PARAMOPT|EXTD_FLAG_PARAMALTER, 0,
};
#define FILEOPSfileListParamsCount (sizeof(FILEOPSfileListParams)/sizeof(FILEOPSfileListParams[0]))

// $getfileinfo(cPath,iInformation)
static ECOparam FILEOPSgetFileInfoParams[] =
{
	7019, fftCharacter,	0, 0,
	7031, fftInteger, 0, MAKELONG(20053, 20068),	// rmm8939 // caa2019
};
#define FILEOPSgetFileInfoParamsCount (sizeof(FILEOPSgetFileInfoParams)/sizeof(FILEOPSgetFileInfoParams[0]))

// $setfileinfo(cPath,iInfoFlag,vInfoValue,...) 
static ECOparam FILEOPSsetFileInfoParams[] =
{
	7019, fftCharacter,	0, 0,
	7033, fftInteger, 0, MAKELONG(cFILEOPSfirstSetFileInfoConstant, cFILEOPSlastSetFileInfoConstant), // rmm8939 // rmm8939a
	7034,	fftUnknown, 0, 0,
};
#define FILEOPSsetFileInfoParamsCount (sizeof(FILEOPSsetFileInfoParams)/sizeof(FILEOPSsetFileInfoParams[0]))

// $rename(cOldname,cNewname)
static ECOparam FILEOPSrenameParams[] =
{
	7035, fftCharacter,	0, 0,
	7036, fftCharacter,	0, 0,
};
#define FILEOPSrenameParamsCount (sizeof(FILEOPSrenameParams)/sizeof(FILEOPSrenameParams[0]))

// $getunixpermissions(cPath)
static ECOparam FILEOPSgupParams[] =
{
	7019, fftCharacter,	0, 0,
};
#define FILEOPSgupParamsCount (sizeof(FILEOPSgupParams)/sizeof(FILEOPSgupParams[0]))

// $readentirefile(cPath,&xVariable)
static ECOparam FILEOPSrefParams[] =
{
	7019, fftCharacter,	0, 0,
	7037, fftBinary, EXTD_FLAG_PARAMALTER, 0,
};
#define FILEOPSrefParamsCount (sizeof(FILEOPSrefParams)/sizeof(FILEOPSrefParams[0]))

// $writeentirefile(cPath,xVariable)
static ECOparam FILEOPSwefParams[] =
{
	7019, fftCharacter,	0, 0,
	7037, fftBinary, 0, 0,
};
#define FILEOPSwefParamsCount (sizeof(FILEOPSwefParams)/sizeof(FILEOPSwefParams[0]))

// $converthfspathtoposixpath(cHfsPath,&cPosixPath)
static ECOparam FILEOPShfsToPosixParams[] =
{
	7038, fftCharacter,	0, 0,
	7039, fftCharacter, EXTD_FLAG_PARAMALTER, 0,
};
#define FILEOPShfsToPosixParamsCount (sizeof(FILEOPShfsToPosixParams)/sizeof(FILEOPShfsToPosixParams[0]))

// $convertposixpathtohfspath(cPosixPath,&cHfsPath)
static ECOparam FILEOPSposixToHfsParams[] =
{
	7039, fftCharacter,	0, 0,
	7038, fftCharacter, EXTD_FLAG_PARAMALTER, 0,
};
#define FILEOPSposixToHfsParamsCount (sizeof(FILEOPSposixToHfsParams)/sizeof(FILEOPSposixToHfsParams[0]))

// $parentdir(cPath)
static ECOparam FILEOPSparentDirParams[] =
{
	7019, fftCharacter,	0, 0,
};
#define FILEOPSparentDirParamsCount (sizeof(FILEOPSparentDirParams)/sizeof(FILEOPSparentDirParams[0]))

// $setunixpermissions(cPath)
static ECOparam FILEOPSsupParams[] =
{
	7019, fftCharacter,	0, 0,
	7040, fftCharacter,	0, 0,
};
#define FILEOPSsupParamsCount (sizeof(FILEOPSsupParams)/sizeof(FILEOPSsupParams[0]))
// End rmm8013
// Start rmm10372
static ECOparam FILEOPSselectParams[] =
{
	7046, fftCharacter, 0, 0,
	7047, fftList, EXTD_FLAG_PARAMOPT, 0,
};
#define FILEOPSselectParamsCount (sizeof(FILEOPSselectParams)/sizeof(FILEOPSselectParams[0]))
// End rmm10372
// Start rmm11014
// $readfile(cFilePath,&vVariable[,iEnc=kUniTypeAuto])
static ECOparam FILEOPSreadFileParams[] =
{
	7000, fftCharacter, 0, 0,
	7051,	fftNone, EXTD_FLAG_PARAMALTER, 0,
	7052, fftInteger, EXTD_FLAG_PARAMOPT, EXT_BUILTIN | MAKELONG(preUniTypeF, (preUniTypeBinary - 1)),
};
#define FILEOPSreadFileParamsCount (sizeof(FILEOPSreadFileParams)/sizeof(FILEOPSreadFileParams[0]))

// $writefile(cFilePath,vVariable[,iEnc=kUniTypeUTF8,bBOM=kTrue,bReplace=kTrue,bCreateParentDirectories=kFalse])
static ECOparam FILEOPSwriteFileParams[] =
{
	7000, fftCharacter, 0, 0,
	7053,	fftNone, 0, 0,
	7052, fftInteger, EXTD_FLAG_PARAMOPT, EXT_BUILTIN | MAKELONG(preUniTypeUTF8, (preUniTypeBinary - 1)),
	7056, fftBoolean, EXTD_FLAG_PARAMOPT, 0,
	7055, fftBoolean, EXTD_FLAG_PARAMOPT, 0,
  7050, fftBoolean, EXTD_FLAG_PARAMOPT, 0,  // bCreateParentDirectories=kFalse // caa2630
};
#define FILEOPSwriteFileParamsCount (sizeof(FILEOPSwriteFileParams)/sizeof(FILEOPSwriteFileParams[0]))
// End rmm11014

// Start rmm11962
// $spaceinfo(cPath,&iSize,&iFree,&iAvailable)
static ECOparam FILEOPSspaceInfoParams[] =
{
	7059, fftCharacter,	0, 0,
	7060, fftLong, EXTD_FLAG_PARAMALTER, 0,
	7061, fftLong, EXTD_FLAG_PARAMALTER, 0,
	7062, fftLong, EXTD_FLAG_PARAMALTER, 0,
};
#define FILEOPSspaceInfoParamsCount (sizeof(FILEOPSspaceInfoParams)/sizeof(FILEOPSspaceInfoParams[0]))
// End rmm11962

// caa2581 start
static ECOparam FILEOPSisDirectoryParams[] =
{
	7019, fftCharacter,	0, 0,
};
#define FILEOPSisDirectoryParamsCount (sizeof(FILEOPSisDirectoryParams)/sizeof(FILEOPSisDirectoryParams[0]))

static ECOparam FILEOPSisFileParams[] =
{
	7019, fftCharacter,	0, 0,
};
#define FILEOPSisFileParamsCount (sizeof(FILEOPSisFileParams)/sizeof(FILEOPSisFileParams[0]))
// caa2581 end

// caa2616 start
static ECOparam FILEOPSjoinPathParams[] =
{
  7065, fftCharacter,  EXTD_FLAG_PARAMOPT, 0,
};
#define FILEOPSjoinPathParamsCount (sizeof(FILEOPSjoinPathParams)/sizeof(FILEOPSjoinPathParams[0]))
// caa2616 end

// caa2634 start
static ECOparam FILEOPScreateTmpDirParams[] =
{
  7068, fftCharacter, 0, 0,
};
#define FILEOPScreateTmpDirParamsCount (sizeof(FILEOPScreateTmpDirParams)/sizeof(FILEOPScreateTmpDirParams[0]))
// caa2634 end

////////////////////////////
ECOmethodEvent fileStaticFuncs[] =
{
  cSMethodId_CreateDir				, 8000,	fftInteger,		FILEOPScreateDirParamsCount, FILEOPScreateDirParams, 0, 0,
	cSMethodId_DeleteFile 			,	8001,	fftInteger,		FILEOPSdeleteFileParamsCount, FILEOPSdeleteFileParams, 0, 0,
	cSMethodId_CopyFile 				,	8002,	fftInteger,		FILEOPScopyFileParamsCount, FILEOPScopyFileParams, 0, 0,
	cSMethodId_MoveFile 				,	8003,	fftInteger,		FILEOPSmoveFileParamsCount, FILEOPSmoveFileParams, 0, 0,
	cSMethodId_PutFileName 			,	8004,	fftBoolean,		FILEOPSputFileNameParamsCount, FILEOPSputFileNameParams, 0, 0,	// rmm11553
	cSMethodId_GetFileName 			,	8005,	fftBoolean,		FILEOPSgetFileNameParamsCount, FILEOPSgetFileNameParams, 0, 0,	// rmm11553
	cSMethodId_SelectDirectory 	,	8006,	fftBoolean,		FILEOPSselDirParamsCount, FILEOPSselDirParams, 0, 0,
	cSMethodId_DoesFileExist		,	8007,	fftBoolean,		FILEOPSdoesFileExistParamsCount, FILEOPSdoesFileExistParams, 0, 0,
	cSMethodId_ChangeCDir 			,	8008,	fftInteger,		FILEOPScwdParamsCount, FILEOPScwdParams, 0, 0,
	cSMethodId_GetCDir 					,	8009,	fftCharacter,	0, 0, 0, 0,
	cSMethodId_SplitPath 				,	8010,	fftInteger,		FILEOPSsplitParamsCount, FILEOPSsplitParams, 0, 0,
	cSMethodId_FileList 				,	8011,	fftList,			FILEOPSfileListParamsCount, FILEOPSfileListParams, 0, 0,
	cSMethodId_GetFileInfo  		,	8012,	fftList,			FILEOPSgetFileInfoParamsCount, FILEOPSgetFileInfoParams, 0, 0,
	cSMethodId_SetFileInfo 			,	8013,	fftInteger,		FILEOPSsetFileInfoParamsCount, FILEOPSsetFileInfoParams, 0, 0,
	cSMethodId_Rename 					,	8014,	fftInteger,		FILEOPSrenameParamsCount, FILEOPSrenameParams, 0, 0,
	cSMethodId_GetUnixPerm			, 8015,	fftCharacter,	FILEOPSgupParamsCount, FILEOPSgupParams, 0, 0,			 // AE5106
	cSMethodId_ReadEntire				, 8016,	fftInteger,		FILEOPSrefParamsCount, FILEOPSrefParams, EXTD_FLAG_HIDDEN, 0,			 // mpmCarbon // rmm9393
	cSMethodId_WriteEntire			, 8017,	fftInteger,		FILEOPSwefParamsCount, FILEOPSwefParams, EXTD_FLAG_HIDDEN, 0,			 // mpmCarbon	// rmm9393
	cSMethodId_convertHFSToPosix, 8018,	fftInteger,		FILEOPShfsToPosixParamsCount, FILEOPShfsToPosixParams, 0, 0,			 // MHn0201
	cSMethodId_convertPosixToHFS, 8019,	fftInteger,		FILEOPSposixToHfsParamsCount, FILEOPSposixToHfsParams, 0, 0,			 // MHn0201
	cSMethodId_ParentDIR,					8020,	fftCharacter,	FILEOPSparentDirParamsCount, FILEOPSparentDirParams, 0, 0,			// PK6727
	cSMethodId_SetUnixPerm			, 8021, fftInteger,		FILEOPSsupParamsCount, FILEOPSsupParams, 0, 0,			 // AE6692
	cSMethodId_MakeCAB					, 8022, 0,	0, 0,			EXTD_FLAG_HIDDEN, 0,	// rmm_mobile
	cSMethodId_ExtractCAB				, 8023, 0,	0, 0,			EXTD_FLAG_HIDDEN, 0,	// rmm_mobile
	cSMethodId_SelectFilesInSystemViewer, 8024, fftInteger, FILEOPSselectParamsCount, FILEOPSselectParams, 0, 0,	// rmm10372
	cSMethodId_ReadFile					,	8025, fftInteger,		FILEOPSreadFileParamsCount, FILEOPSreadFileParams, 0, 0,			// rmm11014
	cSMethodId_WriteFile				,	8026, fftInteger,		FILEOPSwriteFileParamsCount, FILEOPSwriteFileParams, 0, 0,		// rmm11014
	cSMethodId_SpaceInfo				, 8027, fftInteger,		FILEOPSspaceInfoParamsCount, FILEOPSspaceInfoParams, 0, 0,		// rmm11962
	cSMethodId_IsDirectory			, 8028, fftBoolean,		FILEOPSisDirectoryParamsCount, FILEOPSisDirectoryParams, 0, 0, // caa2581
	cSMethodId_IsFile						, 8029, fftBoolean,		FILEOPSisFileParamsCount,	FILEOPSisFileParams, 0, 0, // caa2581
  cSMethodId_JoinPath					, 8030, fftCharacter,	FILEOPSjoinPathParamsCount, FILEOPSjoinPathParams, 0, 0, // caa2616
  cSMethodId_Copy							, 8031, fftInteger,		FILEOPScopyParamsCount, FILEOPScopyParams, 0, 0, // caa2630
  cSMethodId_Move							, 8032, fftInteger,		FILEOPSmoveParamsCount, FILEOPSmoveParams, 0, 0, // caa2630
  cSMethodId_CreateTmpDir			, 8033, fftInteger,		FILEOPScreateTmpDirParamsCount, FILEOPScreateTmpDirParams, 0, 0, // caa2634
	cSMethodId_GetFileID				, 8034, fftCharacter,	FILEOPSgetFileIDParamsCount,		FILEOPSgetFileIDParams, 0, 0, // caa2670
};
#define cSMethod_Count (sizeof(fileStaticFuncs)/sizeof(fileStaticFuncs[0]))	// rmm_mobile

ECOparam fileObjParams[] =
{
	7000, fftCharacter, 0, 0,											//0 $openfile filename
	7001, fftBoolean, EXTD_FLAG_PARAMOPT, 0,			//1 $openfile readonly
	7002, fftBinary, 	EXTD_FLAG_PARAMALTER, 0,		//2 $readfile variable
	7003, fftLong,		EXTD_FLAG_PARAMOPT, 0,			//3 $readfile startpos // rmm9010
	7004,	fftInteger, EXTD_FLAG_PARAMOPT, 0,			//4 $readfile length
	7005, fftLong,		0, 0,												//5 $setposition position // rmm9010
	7006, fftLong,		0, 0,												//6 $setfilesize New size // rmm9010
	7007, fftInteger, 0, MAKELONG(20053, 20068),	//7 $getfileinfo Infoflags // rmm8939 // caa2019
	7008, fftInteger, 0, MAKELONG(cFILEOPSfirstSetFileInfoConstant, cFILEOPSlastSetFileInfoConstant),	//8 $SETFILEINFO Infotype // rmm8939 // rmm8939a
	7009, fftNone,		0, 0,												//9 $setfileinfo value
	7002, fftBinary, 	0, 0,												//10 $writefile variable
	7003, fftLong,		EXTD_FLAG_PARAMOPT, 0,			//11 $writefile startpos // rmm9010
	7010, fftCharacter, 0, 0,											//12 $createfile filename	//<ijt239>
	7011, fftCharacter, EXTD_FLAG_PARAMOPT, 0,		//13 $createfile filetype   //<ijt239>
	7012, fftCharacter, EXTD_FLAG_PARAMOPT, 0,		//14 $createfile creator type   //<ijt239>
	7013, fftBoolean, EXTD_FLAG_PARAMOPT, 0,			//15 $createfile create res	  //<ijt239>	// rmm8939a: No longer used, but still present in the array so later offsets do not change
	7002, fftNone, 	EXTD_FLAG_PARAMALTER, 0,			//16 $readuntil variable
	7014, fftCharacter, 	0, 0,										//17 $readuntil variable
	7014, fftCharacter, 	EXTD_FLAG_PARAMOPT, 0,	//18 $readuntil variable
	7015, fftInteger, 0, EXT_BUILTIN | MAKELONG(preUniTypeF, (preUniTypeBinary - 1)),	//19 $readcharacter encoding	// Start rmm5093 // rmm8939
	7043, fftCharacter, EXTD_FLAG_PARAMALTER, 0,	//20 $readcharacter variable
	7017, fftLong, EXTD_FLAG_PARAMOPT|EXTD_FLAG_PARAMALTER, 0,	// 21 rmm6921: $readcharacter offset // rmm9010
	7018, fftInteger, EXTD_FLAG_PARAMOPT, 0,			//22 $readcharacter maxlen // rmm6921
	7042, fftBoolean, EXTD_FLAG_PARAMOPT, 0,			//23 $readcharacter checkUTF8 // rmm9434
	7015, fftInteger, 0, EXT_BUILTIN | MAKELONG(preUniTypeUTF8, (preUniTypeBinary - 1)),	//24 $writecharacter encoding // rmm8939
	7044, fftCharacter, 0, 0,											//25 $writecharacter variable // End rmm5093
	7016, fftBoolean, EXTD_FLAG_PARAMOPT, 0,			//26 $writecharacter append // rmm6920
	7045, fftBoolean, EXTD_FLAG_PARAMOPT, 0,			//27 $writecharacter BOM // rmm10234
	7041, fftCharacter,EXTD_FLAG_PARAMOPT|EXTD_FLAG_PARAMALTER, 0,	// 28 $getlasterror errorText // rmm9010
};

ECOmethodEvent fileObjFuncs[] =	// rmm5093 
{
	cIMethodId_OpenFile				, 6000, fftBoolean, 2, &fileObjParams[0], 0, 0,
	cIMethodId_CreateFile			, 6001, fftBoolean, 3, &fileObjParams[12], 0, 0,	//<ijt239> // rmm8939a: Removed resource Boolean parameter from OSX $createfile
	cIMethodId_CreateFileTemp	, 6002, fftBoolean, 0, 0, 0, 0,
	cIMethodId_ReadFile				, 6003, fftBoolean, 3, &fileObjParams[2], 0, 0,
	cIMethodId_WriteFile			, 6004, fftBoolean, 2, &fileObjParams[10], 0, 0,
	cIMethodId_CloseFile			, 6005, 0, 0, 0, 0, 0,
	cIMethodId_IsOpen					, 6006, fftBoolean, 0, 0, 0, 0,
	cIMethodId_GetPosition		, 6007, fftLong, 0, 0, 0, 0, // rmm9010
	cIMethodId_SetPosition		, 6008, fftBoolean, 1, &fileObjParams[5], 0, 0,
	cIMethodId_SetFileSize		,	6009, fftBoolean, 1, &fileObjParams[6], 0, 0,
	cIMethodId_GetFileSize		, 6010, fftLong, 0, 0, 0, 0, // rmm9010
	cIMethodId_GetFileInfo		, 6011, fftList, 1, &fileObjParams[7], 0, 0,
	cIMethodId_SetFileInfo		, 6012, fftInteger, 2, &fileObjParams[8], 0, 0,
	cIMethodId_OpenResources	, 6013, fftBoolean, 2, &fileObjParams[0], EXTD_FLAG_HIDDEN, 0,		// mpmCarbon // rmm9025: Hide this - no longer required
	cIMethodId_ReadCharacter	, 6015, fftBoolean, 5, &fileObjParams[19], 0, 0,	// rmm5093 // rmm6921 // rmm9434
	cIMethodId_WriteCharacter	, 6016, fftBoolean, 4, &fileObjParams[24], 0, 0,		// rmm5093 // rmm6920 // rmm6921 // rmm9434 // rmm10234
	cIMethodId_GetLastError		, 6017,	fftInteger,	1, &fileObjParams[28], 0, 0,	// rmm9010 // rmm9434 // rmm10234
};

#define cIMethod_Count (sizeof(fileObjFuncs)/sizeof(fileObjFuncs[0]))	// rmm5093

// caa2042 start
ECOproperty fileObjProps[] =
{
  cIPropertyId_Filepath,      3000, fftCharacter, 0, 0, 0, 0,
};

#define cIProp_Count (sizeof(fileObjProps)/sizeof(fileObjProps[0]))
// caa2042 end

// caa0334 start
ECOparam fileOpsWorkerProgressParams[] =
{
  5500, fftRow, 0, 0,
};
#define fileOpsWorkerProgressParamsCount (sizeof(fileOpsWorkerProgressParams)/sizeof(fileOpsWorkerProgressParams[0]))

ECOparam fileOpsWorkerCompletedParams[] =
{
  5501, fftRow, 0, 0,
};
#define fileOpsWorkerCompletedParamsCount (sizeof(fileOpsWorkerCompletedParams)/sizeof(fileOpsWorkerCompletedParams[0]))

ECOproperty FileOpsWorkerProperties[] =
{
  cPropertyWorker_state, cPropertyWorker_state, fftInteger, EXTD_FLAG_EDITRONLY | EXTD_FLAG_INTCONSTANT, 0, preWorkerStateF, preWorkerStateL,
  cPropertyWorker_errorcode, cPropertyWorker_errorcode, fftInteger, EXTD_FLAG_EDITRONLY, 0, 0, 0,
  cPropertyWorker_errortext, cPropertyWorker_errortext, fftCharacter, EXTD_FLAG_EDITRONLY, 0, 0, 0,
  cPropertyWorker_threadcount, cPropertyWorker_threadcount, fftInteger, EXTD_FLAG_EDITRONLY, 0, 0, 0,
  anumCallbackInst, 0, 0, 0, 0, 0, 0,
  cPropertyWorker_callprogress, cPropertyWorker_callprogress, 0, 0, 0, 0, 0,
  cPropertyWorker_progressinterval, cPropertyWorker_progressinterval, fftInteger, 0, 0, 0, 0 // caa1545
};
#define FileOpsWorkerProperties_Count (sizeof(FileOpsWorkerProperties)/sizeof(FileOpsWorkerProperties[0]))


ECOmethodEvent FileOpsWorkerMethods[] =
{
  cMethod_run, cMethod_run, fftBoolean, 0, 0, 0, 0,
  cMethod_start, cMethod_start, fftBoolean, 0, 0, 0, 0,
  cMethod_cancel, cMethod_cancel, fftBoolean, 0, 0, 0, 0,
  cMethod_completed, cMethod_completed, fftBoolean, fileOpsWorkerCompletedParamsCount, fileOpsWorkerCompletedParams, 0, 0,
  cMethod_cancelled, cMethod_cancelled, fftBoolean, 0, 0, 0, 0,
  cMethod_progress, cMethod_progress, fftBoolean, fileOpsWorkerProgressParamsCount,  fileOpsWorkerProgressParams, 0, 0
};
#define FileOpsWorkerMethods_Count (sizeof(FileOpsWorkerMethods)/sizeof(FileOpsWorkerMethods[0]))

ECOmethodEvent FileOpsCopyWorkerMethods[] =
{
  cMethod_initcopy, cMethod_initcopy, fftBoolean, FILEOPScopyWorkerParamsCount, FILEOPScopyWorkerParams, 0, 0 // caa2593
};
#define FileOpsCopyWorkerMethods_Count (sizeof(FileOpsCopyWorkerMethods)/sizeof(FileOpsCopyWorkerMethods[0]))


ECOmethodEvent FileOpsMoveWorkerMethods[] =
{
  cMethod_initmove, cMethod_initmove, fftBoolean, FILEOPSmoveWorkerParamsCount, FILEOPSmoveWorkerParams, 0, 0 // caa2593
};
#define FileOpsMoveWorkerMethods_Count (sizeof(FileOpsMoveWorkerMethods)/sizeof(FileOpsMoveWorkerMethods[0]))

ECOmethodEvent FileOpsDeleteWorkerMethods[] =
{
  cMethod_initdelete, cMethod_initdelete, fftBoolean, FILEOPSdeleteWorkerParamsCount, FILEOPSdeleteWorkerParams, 0, 0 // caa2593
};
#define FileOpsDeleteWorkerMethods_Count (sizeof(FileOpsDeleteWorkerMethods)/sizeof(FileOpsDeleteWorkerMethods[0]))
// caa0334 end

tqfFileOpsObj::tqfFileOpsObj(qobjinst pObjInst)
							:EXTfile(),OmnisObject(pObjInst) // caa0334
{
	mFileName.setEmpty(fftCharacter, dpDefault);	// rmm9025
	mIsOpen = qfalse;
	mFileEncoding = 0;	// rmm6921
	mLastError = e_ok;	// rmm9010
}
	
tqfFileOpsObj::~tqfFileOpsObj()
{
	close();
}

// caa2042 start
qlong tqfFileOpsObj::propertySupport( LPARAM pMessage, EXTCompInfo* eci )
{
  switch (pMessage)
  {
    case ECM_PROPERTYCANASSIGN:   /* Object: Is the property assignable (ie readonly?) */
    {
      return 0L; // We only have one property and it's read-only.
    }
    case ECM_SETPROPERTY:         /* Object: Assignment to a property */
    {
      return 0L; // We only have one property and it's read-only.
    }
    case ECM_GETPROPERTY:         /* Object: Retrieve value from property */
    {
      EXTfldval fval;
      switch (ECOgetId(eci))
      {
        case cIPropertyId_Filepath:
        {
          fval = mFileName;
          break;
        }
      }
      ECOaddParam(eci, &fval);
      return 1L;
    }
  }
  return 0L; // No property found or message was wrong
}
// caa2042 end

// caa0334 start
qlong FileOpsWorker::propertySupport(LPARAM pMessage, EXTCompInfo *eci)
{
  qlong propId = ECOgetId(eci);
  qbool rtnStatus = qfalse;
  switch (pMessage)
  {
    case ECM_PROPERTYCANASSIGN: {
      switch (propId) {
        case cPropertyWorker_callprogress:
        case cPropertyWorker_progressinterval: // caa1545
        { rtnStatus = qtrue; }
      }
      break;
    }
      
    case ECM_GETPROPERTY: {
      EXTfldval fval;
      rtnStatus = qtrue;
      switch (propId)
      {
        case cPropertyWorker_state: {
          fval.setConstant(preWorkerStateF, preWorkerStateL, getState());
          break;
        }
        case cPropertyWorker_errortext: {
          getNativeErrorText(fval);
          break;
        }
        case cPropertyWorker_errorcode: {
          fval.setLong(getErrorCode());
          break;
        }
        case cPropertyWorker_callprogress: {
          fval.setLong(mCallProgress);
          break;
        }
        case cPropertyWorker_progressinterval: { // caa1545
          fval.setLong(mProgressInterval);
          break;
        }
        case cPropertyWorker_threadcount: {
          switch (getWorkerObjectId()) {
            case cObject_FileOpsCopyWorker: { fval.setLong(FileOpsCopyWorker::smThreadCount); break; }
            case cObject_FileOpsMoveWorker: { fval.setLong(FileOpsMoveWorker::smThreadCount); break; }
            case cObject_FileOpsDeleteWorker: { fval.setLong(FileOpsDeleteWorker::smThreadCount); break; }
          }
          break;
        }
        default: { rtnStatus = qfalse; }
      }
      
      if (rtnStatus) ECOaddParam(eci,&fval);
      break;
    }
      
    case ECM_SETPROPERTY: {
      EXTParamInfo *param = ECOfindParamNum(eci, 1);
      // caa1545 start
      if (!param) break;
      EXTfldval fval((qfldval)param->mData);
      
      switch (propId) {
        case cPropertyWorker_callprogress:
        {
          mCallProgress = (fval.getLong() != 0);
          rtnStatus = qtrue;
          break;
        }
          
        case cPropertyWorker_progressinterval:
        {
          mProgressInterval = fval.getLong();
          if (mProgressInterval < 1) mProgressInterval = 1;
          rtnStatus = qtrue;
          break;
        }
      }
      // caa1545 end
      
      break;
    }
  }
  
  return rtnStatus;
}
// caa0334 end

#if defined(isunix) || defined(ismach_o) // MHn0470
	#include <sys/stat.h> 
#endif

// PK6727
#if defined(isunix) || defined(isCOCOA)	// CR0270
	#define FILE_SEPR_CHR_EX '/'
#elif defined(ismac)
	#define FILE_SEPR_CHR_EX ':'
#elif defined(iswin32) && !defined(isunix)
	#define FILE_SEPR_CHR_EX '\\'
#endif

// PK6727
qbool upToParentDIR(EXTfldval* pSrc,EXTfldval* pDst)
{
	if ( !pSrc || !pDst ) return qfalse;
	// rmm9025: Reworked
	qlong lastSep = pSrc->lastPos(FILE_SEPR_CHR_EX);
	qlong newLen = lastSep ? (lastSep - 1) : 0;
	if (newLen)
	{
		*pDst = *pSrc;
		pDst->reduceCharLen(newLen);
	}
	else
		pDst->setEmpty(fftCharacter, dpDefault);
	return qtrue;
}

#define MAX_PARAMS 	 9
qbool tqfFileOpsObj::staticMethod(EXTCompInfo* pEci)
{
	qlong funcId = ECOgetId(pEci);
	EXTParamInfo* param[MAX_PARAMS];
	for ( qshort n=1; n<=MAX_PARAMS; n++ )
	{
		param[n-1] = ECOfindParamNum(pEci,n);
	}
	qshort paramCount = ECOgetParamCount(pEci); 
	OpsErr err = kFileOpsParamError; 
	qbool methodFound = qtrue;
	// Start rmm8840: Cocoa objective C methods (e.g. moveItemAtPath) can throw an exception if there is a bad (e.g. empty) parameter
	#ifdef isCOCOA
		@try
		{
	#endif
	// End rmm8840
	switch ( funcId )
	{
		case cSMethodId_SetUnixPerm:	// AE6692
		{
			#if defined(isunix) || defined(ismach_o) // MHn0470
				if ( param[0] && param[1] )
				{
					EXTfldval name((qfldval)param[0]->mData); // rmm9025
					EXTfldval fval2( (qfldval)param[1]->mData );
					str255 permissions; fval2.getChar( permissions );
					struct stat statv;
					#ifdef ismach_o
						err = ECOconvertHFSToPosix(name, name); // MHn0470
						CHRconvToUtf8 cto(name);	// rmm9025
					#else
  					CHRconvToOs cto(name, qtrue);	// rmm9025
					#endif
					if (permissions.length()==10 && stat((const char *) cto.dataPtr(),&statv)!=-1 )
					{
						int newmode = 0; int slots[3];
						permissions.convcase(qfalse);
						for (qshort n=0; n<3; n++ )
						{
							slots[n] = 0;
							if ( permissions[permissions.length()]=='x' )
								slots[n] |= 1;
							permissions[0]--;
							if ( permissions[permissions.length()]=='w' )
								slots[n] |= 2;
							permissions[0]--;
							if ( permissions[permissions.length()]=='r' )
								slots[n] |= 4;
							permissions[0]--;
						}
						newmode = (slots[2] << 6) | (slots[1] << 3) | (slots[0]);
						if ( permissions[1]=='d' )
							newmode |= 0x4000;
						if ( chmod((const char *) cto.dataPtr(),newmode)!=-1 )
							err = kFileOpsOK;
					}
				}
			#endif
			break;
		}
		case cSMethodId_GetUnixPerm:		// AE5106
		{
		  EXTfldval fval; str255 s;
			#if defined(isunix) || defined(ismach_o) // MHn0470
				if ( param[0] )
				{
					EXTfldval name((qfldval)param[0]->mData); // rmm9025
					struct stat statv;
					#ifdef ismach_o
						err = ECOconvertHFSToPosix(name, name); // MHn0470
						CHRconvToUtf8 cto(name);	// rmm9025
					#else
  					CHRconvToOs cto(name, qtrue); // rmm9025
					#endif
					if (stat((const char *) cto.dataPtr(),&statv) != -1)
					{
						int cmode = statv.st_mode;
						for (qshort n=1; n<=3; n++ )
						{
							if ( cmode & 1 ) s.insert('x',1); else s.insert('-',1);
							if ( cmode & 2 ) s.insert('w',1); else s.insert('-',1);
							if ( cmode & 4 ) s.insert('r',1); else s.insert('-',1);
							cmode = cmode >> 3;
						}
						if (statv.st_mode & 0x4000) s.insert('d',1); else s.insert('-',1);
					}
				}
			#endif
	  	fval.setChar(s);
	  	ECOaddParam(pEci, &fval);
	    methodFound = qfalse;
	    break;
		}
		case cSMethodId_DeleteFile:
		case cSMethodId_CreateDir:
		case cSMethodId_DoesFileExist:
		case cSMethodId_CopyFile:
		case cSMethodId_MoveFile:
		{
			if ( (funcId==cSMethodId_MoveFile || funcId==cSMethodId_CopyFile) && paramCount !=2 )
				break; // Mandatory two parameters for movefile, copyFile
			if ( param[0] )
			{
				EXTfldval name((qfldval)param[0]->mData); // rmm9025
				switch ( funcId )
				{
					// caa0002 START
					case cSMethodId_DeleteFile:
					{
						qbool deleteContents = qfalse, recursive = qfalse;

						if (param[1] && param[1]->mData)
						{
							EXTfldval param2((qfldval)param[1]->mData);
							deleteContents = param2.getLong();
						}

						if (param[2] && param[2]->mData)
						{
							EXTfldval param3((qfldval)param[2]->mData);
							recursive = param3.getLong();
						}

						err = deleteFile(name, deleteContents, recursive);

						break;
					}
					// caa0002 END
					// caa0121 start
					case cSMethodId_CreateDir:
					{
						qbool createParentFolders = qfalse;
						if (param[1] && param[1]->mData)
						{
							EXTfldval param2((qfldval)param[1]->mData);
							createParentFolders = (qbool) (param2.getLong() != 0);
						}
						err = createDir(name, createParentFolders);
						break;
					}
					// caa0121 end
					case cSMethodId_DoesFileExist:
					{
						qbool exists;
						doesFileExist(name, exists);
						err = exists;
						break;
					}
					case cSMethodId_CopyFile:
					case cSMethodId_MoveFile:
					{
						// Start rmm9025
						EXTfldval name2((qfldval)param[1]->mData);
						if (cSMethodId_CopyFile == funcId)
							err = copyFile(name, name2);
						else
							err = moveFile(name, name2);
						// End rmm9025
						break;
					}
				}
			}
			break;
		}
		case cSMethodId_ChangeCDir:
		{
			if (param[0])
			{
				EXTfldval path((qfldval)param[0]->mData); // rmm9025
				err = changeCDir(path);
			}
			break;
		}
		case cSMethodId_GetCDir:
		{
			EXTfldval workingDir;
			getCDir(workingDir); // rmm9025
			ECOaddParam(pEci,&workingDir);
			methodFound = qfalse;
			break;
		}
		case cSMethodId_PutFileName:
		case cSMethodId_GetFileName:
		case cSMethodId_SelectDirectory:
		{
			if ( !paramCount ) break;
			EXTfldval filename( (qfldval)param[0]->mData );
			str255  title, filter;
			// Start rmm9025
			EXTfldval initDir;
			EXTfldval *initDirPtr = 0;
			// End rmm9025
			strxxx  &titleRef=title, &filterRef=filter ;
			qlong lFlags = 0; // MHn0334 // rmm11553: Renamed

			if ( param[1] && param[1]->mData )
			{
				EXTfldval fval1( (qfldval)param[1]->mData );
				titleRef = fval1.getChar();

				if ( param[2] && param[2]->mData )
				{
					EXTfldval fval2( (qfldval)param[2]->mData );
					if (funcId == cSMethodId_SelectDirectory)
					{
						// Start rmm9025
						if (fval2.getCharLen())
						{
							initDir = fval2;
							initDirPtr = &initDir;
						}
						// End rmm9025
					}
					else
						filterRef = fval2.getChar();
				}
				if ( param[3] && param[3]->mData  )
				{
					EXTfldval fval3( (qfldval)param[3]->mData );
					if (funcId == cSMethodId_SelectDirectory)	// rmm6248
						lFlags = fval3.getLong();
					else
					{
						// Start rmm9025
						if (fval3.getCharLen())
						{
							initDir = fval3;
							initDirPtr = &initDir;
						}
						// End rmm9025
					}
				}
				// MHn0334 begins
				if ( funcId != cSMethodId_SelectDirectory && param[4] && param[4]->mData  ) // rmm6248
				{
					EXTfldval fval4( (qfldval)param[4]->mData );
					lFlags = fval4.getLong();
				}
				// MHn0334 ends
			}
			// MHn0082 begins.
			#if defined(ismac)
				if ( funcId == cSMethodId_GetFileName )
				{
					err = ECOloadFileDialog(gInstLib, NULL, titleRef, filterRef, filename, initDirPtr, lFlags); // MHn0334
				}
				else if ( funcId ==  cSMethodId_PutFileName )
				{
					err = ECOsaveFileDialog(gInstLib, NULL, titleRef, filterRef, filename, initDirPtr, lFlags); // MHn0334
				}
				else
				{
					err = ECOgetDirectoryDialog(gInstLib, NULL, titleRef, filename, initDirPtr, lFlags); // MHn0334
				}
			#elif !defined(isheadless) // rmmheadless
				if ( funcId == cSMethodId_GetFileName )
				{
					HWND lHwnd = GetForegroundWindow();
					err = ECOloadFileDialog(gInstLib, lHwnd, titleRef, filterRef, filename, initDirPtr);
				}
				else if ( funcId ==  cSMethodId_PutFileName )
				{
					HWND lHwnd = GetForegroundWindow();
					err = ECOsaveFileDialog(gInstLib, lHwnd, titleRef, filterRef, filename, initDirPtr, lFlags); // rmm11553
				}
				else
				{
					HWND lHwnd = GetForegroundWindow();
					err = ECOgetDirectoryDialog(gInstLib, lHwnd, titleRef, filename, initDirPtr);
				}
			#else
				// rmmheadless: Headless server
				err = 0;
			#endif
			// MHn0082 ends.
			if ( err )
			{
				ECOsetParameterChanged(pEci,1);
			}
			break;
		}
		case cSMethodId_SplitPath:
		{
			// Start rmm10137
			if (paramCount < 2)
				break;
			// End rmm10137
			if (param[0])
			{
				// Start rmm9025
				EXTfldval path((qfldval)param[0]->mData);
				str255 drive, fileName, extension;
				EXTfldval dir;	// rmm1037
				err = splitPath(path, drive, dir, fileName, extension);

				EXTfldval driveFval((qfldval)param[1]->mData);
				driveFval.setChar(drive);
				ECOsetParameterChanged(pEci, 2);

				// Start rmm10137
				if (paramCount >= 3)
				{
					EXTfldval dirFval((qfldval)param[2]->mData);
					dirFval.takeFrom(dir);
					ECOsetParameterChanged(pEci, 3);
				}
				// End rmm10137

				if (paramCount >= 4) // rmm10137
				{
					EXTfldval fileNameFval((qfldval)param[3]->mData);
					fileNameFval.setChar(fileName);
					ECOsetParameterChanged(pEci, 4);
				}

				if (paramCount >= 5)	// rmm10137
				{
					EXTfldval extensionFval((qfldval)param[4]->mData);
					extensionFval.setChar(extension);
					ECOsetParameterChanged(pEci, 5);
				}
				// End rmm9025
			}
			break;
		}
		case cSMethodId_FileList:
		{
			if ( param[0] )
			{
				str255 filter(QTEXT("*.*")), errorText; // jc0010
				strxxx& filterRef=filter;
				
				qulong retInfo = cInfoName;
				EXTfldval fldval,include( (qfldval)param[0]->mData );
				
				EXTfldval path;	// rmm9025
				if ( param[1] && param[1]->mData )
				{
					EXTfldval fval1((qfldval)param[1]->mData);
					path = fval1; // rmm9025

					// begins jc0010
					if (fval1.isEmpty())
					{
						if ( param[4] )
						{
							RESloadString(gInstLib,20039,errorText);
							qshort i = errorText.pos(':');
							while (i != 0)
							{
								errorText.deleet(1,i);
								i = errorText.pos(':');
							}
								EXTfldval fval4( (qfldval)param[4]->mData );
							fval4.setChar(errorText);
							ECOsetParameterChanged(pEci,5);
						}
						break;
					}
					// end jc0010

					if ( param[2] && param[2]->mData )
					{
						EXTfldval fval2( (qfldval)param[2]->mData );
						retInfo = fval2.getLong();
					}

					if ( param[3] && param[3]->mData )
					{
						EXTfldval fval3( (qfldval)param[3]->mData );
						filter = fval3.getChar();
					}
				}
				EXTqlist *fileList = NULL;

				HCURSOR oldCursor = WNDgetCursor();
				WNDsetCursor(WND_CURS_WATCH);
				getFileList (include.getLong(), path, retInfo, filterRef,fileList); // rmm9025
				WNDsetCursor(oldCursor);

				if ( fileList )
				{
					fldval.setList( fileList, qtrue);
					delete fileList;
				}
				ECOaddParam(pEci,&fldval);
				methodFound = qfalse;
			}
			break;
		}
		case cSMethodId_GetFileInfo:
		{
			if ( param[0] )
			{
				qulong retInfo = cInfoName;
				EXTfldval result,path((qfldval)param[0]->mData); // rmm9025

				if (param[1] && param[1]->mData)
				{
					EXTfldval fval1( (qfldval)param[1]->mData );
					retInfo = fval1.getLong();
				}
				EXTqlist *fileInfo = NULL;
				getFileInfo(path, retInfo, fileInfo);	// rmm9025
				if (fileInfo)
				{
					result.setList( fileInfo, qtrue);
					delete fileInfo;
				}
				ECOaddParam(pEci,&result);
				methodFound = qfalse;
			}
			break;
		}
		case cSMethodId_SetFileInfo:
		{
			if ( param[0] )
			{
				EXTfldval path( (qfldval)param[0]->mData );
				err = setFileInfo(path, pEci, 2); // rmm9025
			}
			break;
		}
		case cSMethodId_Rename:
		{
			if ( param[0] && param[1])
			{
				EXTfldval oldName( (qfldval)param[0]->mData ),
									newName( (qfldval)param[1]->mData );
				err = rename(oldName, newName); // rmm9025
			}
			break;
		}
		case cSMethodId_ReadEntire: // mpmCarbon begins
		case cSMethodId_WriteEntire:
		{
			if ( param[0] && param[1])
			{
				EXTfldval path( (qfldval)param[0]->mData ),
									data( (qfldval)param[1]->mData );
				if ( funcId == cSMethodId_ReadEntire )
				{
					qHandle han;
					err = readEntireFile(path, han); // rmm9025
					if ( err == kFileOpsOK )
					{
						data.setHandle( fftBinary, han, qfalse );
						ECOsetParameterChanged(pEci,2);
					}
				}
				else
				{
					qHandle han = data.getHandle(qfalse);
					err = writeEntireFile(path, han); // rmm9025
				}
			}
			break;
		}// mpmCarbon ends

		// PK6727
		case cSMethodId_ParentDIR:
		{
			if ( param[0] )
			{
				EXTfldval result,srcpath( (qfldval)param[0]->mData );
				if ( upToParentDIR(&srcpath,&result) )
				{
					ECOaddParam(pEci,&result);
					methodFound = qfalse;
				}
			}
			break;
		}

		// MHn0201 begins
		case cSMethodId_convertHFSToPosix: // mpmCarbon begins
		case cSMethodId_convertPosixToHFS:
		{
			if ( param[0] && param[1])
			{
				EXTfldval srcpath( (qfldval)param[0]->mData ),
									dstpath( (qfldval)param[1]->mData );
				#ifdef ismacosx
					if ( funcId == cSMethodId_convertHFSToPosix )
					{
						//<ijt218>
						err = ECOconvertHFSToPosix(srcpath, dstpath); // rmm9025
					}
					else
					{
						// rmmuni_osx6:
						CHRconvToUtf8 ctu(srcpath); // rmm9025
						err = ECOconvertPosixToHFS(ctu.dataPtr(), kCFStringEncodingUTF8, dstpath);
					}
				#else
					err = kFileOpsOK; // MHn0318
					dstpath = srcpath;
				#endif
				if (err == kFileOpsOK)
					ECOsetParameterChanged(pEci,2);
			}
			break;
		}// MHn0201 ends
		// Start rmm_mobile
		#if defined(iswin32) && !defined(isunix) && !defined(is64bit)
			case cSMethodId_MakeCAB:
			case cSMethodId_ExtractCAB:
			{
				if (paramCount >= 3)
				{
					err = kFileOpsOK;
					str255 errorText;
					EXTfldval cabPathFval((qfldval) param[0]->mData);
					str255 cabPath;
					cabPathFval.getChar(cabPath);
					if (!cabPath.length())
					{
						RESloadString(gInstLib, 10003, errorText);
					}
					else
					{
						qbool exists;
						doesFileExist(cabPathFval, exists); // rmm9025
						if (cSMethodId_MakeCAB == funcId && exists)
						{
							RESloadString(gInstLib, 10001, errorText);
						}
						else if (cSMethodId_ExtractCAB == funcId && !exists)
						{
							RESloadString(gInstLib, 10002, errorText);
						}
						else
						{
							EXTfldval p2Fval((qfldval) param[1]->mData);
							if (cSMethodId_MakeCAB == funcId)
							{
								EXTqlist *fileList = p2Fval.getList(qfalse);
								makeCab(cabPath, fileList, errorText);
								delete fileList;
							}
							else
							{
								str255 extractDir;
								p2Fval.getChar(extractDir);
								if (!extractDir.length())
								{
									RESloadString(gInstLib, 10003, errorText);
								}
								else
								{
									doesFileExist(p2Fval, exists); // rmm9025
									if (!exists)
									{
										RESloadString(gInstLib, 10004, errorText);
									}
									else
									{
										extractCab(cabPath, extractDir, errorText);
									}
								}
							}
						}
					}
					// Set returned error text
					EXTfldval errorTextFval((qfldval) param[2]->mData);
					errorTextFval.setChar(errorText);
					ECOsetParameterChanged(pEci, 3);
				}
				break;
			}
		#endif
		// End rmm_mobile
		// Start rmm10372
		case cSMethodId_SelectFilesInSystemViewer:
		{
			if (!paramCount)
			{
				err = kFileOpsParamError;
				break;
			}
			if (1 == paramCount)
				err = selectFilesInSystemViewer(param[0]);
			else
				err = selectFilesInSystemViewer(param[0], param[1]);
			break;
		}
		// End rmm10372
		// Start rmm11014
		case cSMethodId_ReadFile:
		{
			err = staticReadFile(pEci, paramCount, param[0], param[1], param[2]);
			break;
		}
		case cSMethodId_WriteFile:
		{
			err = staticWriteFile(pEci, paramCount, param[0], param[1], param[2], param[3], param[4], param[5]);
			break;
		}
		// End rmm11014
		// Start rmm11962
		case cSMethodId_SpaceInfo:
		{
			err = qfalse;
			if (param[0])
			{
				EXTfldval pathFval((qfldval)param[0]->mData);
				qbool exists;
				doesFileExist(pathFval, exists);
				err = exists;
				if (exists)
				{
					std::string pathString;
					pathFval.getchar(pathString);
					std::filesystem::path path = std::filesystem::u8path(pathString);
					std::filesystem::space_info info = std::filesystem::space(path);

					auto returnInfo = [pEci, &param](qshort pParamNum, qlong64 pValue)
					{
						if (param[pParamNum])
						{
							EXTfldval infoFval((qfldval)param[pParamNum]->mData);
							infoFval.setLong64(pValue);
							ECOsetParameterChanged(pEci, pParamNum + 1);
						}
					};
					// pParamNum is zero-based
					returnInfo(1, info.capacity);
					returnInfo(2, info.free);
					returnInfo(3, info.available);
				}
			}
			break;
		}
		// caa2581 start
		case cSMethodId_IsDirectory:
		case cSMethodId_IsFile:
		{
			err = qfalse;

			if (!paramCount)
        break;
			if (!param[0] || !param[0]->mData)
        break;

			EXTfldval path((qfldval)param[0]->mData);
			
			qbool exists = qfalse;
			doesFileExist(path, exists);

			if (!exists) {
				err = exists;
				break;
			}

			if (isDirectory(path))
				err = (funcId == cSMethodId_IsDirectory);
			else
				err = (funcId == cSMethodId_IsFile);

			break;
		}
		// caa2581 end
    // caa2616 start
    case cSMethodId_JoinPath:
    {
      if (!paramCount)
        break;
      
      err = kFileOpsOK;
      
      auto join = [](std::string& pPath, std::string& pNewSegment)
      {
#if defined(iswin32) && !defined(isunix)
        if (!pPath.empty() && pNewSegment[0] == '\\')
          pNewSegment.erase(0, 1);
#else
        if (!pPath.empty() && pNewSegment[0] == '/')
          pNewSegment.erase(0, 1);
#endif
       
        std::filesystem::path base = pPath;
        std::filesystem::path segment = pNewSegment;
        std::filesystem::path newpath = base / segment;
        pPath = newpath.string();
      };
      
      std::string path;
      for ( qshort n=1; n<=paramCount; n++ )
      {
        EXTParamInfo* param = ECOfindParamNum(pEci, n);
        if (param && param->mData) {
          EXTfldval segment((qfldval)param->mData);
          std::string str;
          segment.getchar(str);
          if (!str.empty())
            join(path, str);
        }
      }

#if defined(iswin32) && !defined(isunix)
			std::replace(path.begin(), path.end(), '/', '\\');
#endif

      EXTfldval rtnPath; rtnPath.setchar(path);
      ECOaddParam(pEci, &rtnPath);
      
      break;
    }
    // caa2616 end
		// caa2670 start
		case cSMethodId_GetFileID:
		{
			if (paramCount < 1) {
				err = kFileOpsParamError;
				break;
			}

			err = kFileOpsOK;

			EXTfldval path((qfldval)param[0]->mData);

			qbool exists = qfalse;
			doesFileExist(path, exists);

			if (!exists) {
				err = exists;
				break;
			}

			std::string tmp; path.getchar(tmp);
			tmp = getFileID(tmp);

			EXTfldval rtnFileID; rtnFileID.setchar(tmp);
			ECOaddParam(pEci, &rtnFileID);

			break;
		}
		// caa2670 end
    // caa2634 start
    case cSMethodId_CreateTmpDir:
    {
      if (paramCount < 1) {
        err = kFileOpsParamError;
        break;
      }
      
      err = kFileOpsOK;
      
      EXTfldval rtnVal((qfldval)param[0]->mData);
      
#if defined(iswin32) && !defined(isunix)
			try {
				std::filesystem::path tempDir = std::filesystem::temp_directory_path();
					
				EXTfldval unique; generateUniqueFolderName(unique);
				std::string sunique; unique.getchar(sunique);

				std::filesystem::path tempFolder = tempDir / sunique;
				std::filesystem::create_directory(tempFolder);

				rtnVal.setchar(tempFolder.string());
			}
			catch (const std::filesystem::filesystem_error& e) {
				err = e.code().value();
				break;
			}
#else
      char tempFolderTemplate[] = "/tmp/omnisFileOpsTmpDir.XXXXXX";
      std::string path = mkdtemp(tempFolderTemplate);
      if (path.empty()) {
        err = kFileOpsUnknownError;
        break;
      }
      
      rtnVal.setchar(path);
#endif
      
      ECOsetParameterChanged(pEci, 1);
      
      break;
    }
    // caa2634 end
    // caa2630 start
    case cSMethodId_Copy:
    case cSMethodId_Move:
    {
      
      if (paramCount < 2)
        break;

      EXTfldval from((qfldval)param[0]->mData);
      EXTfldval to((qfldval)param[1]->mData);
      
      qbool createPath, replace, merge;
      createPath = replace = merge = qfalse;
      
      if (paramCount >= 3 && param[2] && param[2]->mData) {
        EXTfldval fld((qfldval)param[2]->mData);
        if (!fld.isNull())
          createPath = fld.getLong() != 0;
      }

      if (paramCount >= 4 && param[3] && param[3]->mData) {
        EXTfldval fld((qfldval)param[3]->mData);
        if (!fld.isNull())
          replace = fld.getLong() != 0;
      }

      if (paramCount >= 5 && param[4] && param[4]->mData) {
        EXTfldval fld((qfldval)param[4]->mData);
        if (!fld.isNull())
          merge = fld.getLong() != 0;
      }

      err = copyOrMove(from, to, createPath, replace, merge, funcId);
      break;

    }
    // caa2630 end
		// End rmm11962
		default:
			methodFound = qfalse;
			break;
	}
	// Start rmm8840
	#ifdef isCOCOA
		}
		@catch (NSException *exception)
		{
			err = kFileOpsUnknownError;
		}
	#endif
	// End rmm8840
	if ( methodFound )
	{ // Push error code
		EXTfldval result;
		result.setLong(err);
    ECOaddParam(pEci,&result);
	}
	return methodFound;
}

// caa0334 start
void FileOpsWorker::setResultRow(qlong pErrorCode) {
  if (mCancelled) return;
  
  static str15 sErrorCode("errorCode");
  static str15 sErrorText("errorText");
	static str15 sTag("__tag"); // caa2593
  clearResultRow(); mResultRow = new EXTqlist(listVlen);
  mResultRow->addCol(fftInteger, 0, 0, &sErrorCode);
  mResultRow->addCol(fftCharacter, dpDefault, maxcharlen, &sErrorText);
	// caa2593 start
	if (mHasTag)
	{
		ffttype fft; qshort fdp;
		mTag.getType(fft, &fdp);
		qlong flen = 0;
		if (fftCharacter == fft)
			flen = maxcharlen;
		else if (fftBinary == fft)
			flen = maxbinlen;
		mResultRow->addCol(fft, fdp, flen, &sTag);
	}
	// caa2593 end
  mResultRow->insertRow();
  
  EXTfldval fval, errorText;
  ECOgetConstantDescriptionByValue(pErrorCode, 20001, 20049, errorText);
  
  mResultRow->getColValRef(1, 1, fval, qtrue);
  fval.setLong(pErrorCode);
  
  mResultRow->getColValRef(1, 2, fval, qtrue);
  fval.takeFrom(errorText);

	// caa2593 start
	if (mHasTag)
	{
		mResultRow->getColValRef(1, 3, fval, qtrue);
		fval.takeFrom(mTag);
	}
	// caa2593 end
}

void FileOpsCopyWorker::execute()
{
  setResultRow(tqfFileOpsObj::copyOrMove(mFrom, mTo, mCreatePath, mReplace, mMerge, cSMethodId_Copy));
}

void FileOpsMoveWorker::execute()
{
  setResultRow(tqfFileOpsObj::copyOrMove(mFrom, mTo, mCreatePath, mReplace, mMerge, cSMethodId_Move));
}

void FileOpsDeleteWorker::execute()
{
  setResultRow(tqfFileOpsObj::deleteFile(mPath,
                                         mDeleteContents.getBool()-1,
                                         mRecursive.getBool()-1));
}

void FileOpsWorker::dCancel()
{
  if (!getThreadHandle() && mProgressThreadCleared == qtrue) return;
  if (getThreadHandle() || mProgressThreadCleared == qfalse)
    mCancelled = qtrue; // caa2595 : don't block main thread for fileops workers getting cancelled, no need
}

// caa1545 start
void FileOpsWorker::notifyProgress(qshort pProgress)
{
  if (mCancelled || !mCallProgress || mDetached) return; // caa2595
  
  static str15 sProgressMethod(QTEXT("$progress"));
  static str15 sColumnName(QTEXT("progress"));
  
  threadCritical tdc;
  
  clearResultRow(); mResultRow = new EXTqlist(listVlen);
  mResultRow->addCol(fftInteger, 0, 0, &sColumnName);
  mResultRow->insertRow();
  
  EXTfldval fval;
  mResultRow->getColValRef(1, 1, fval, qtrue);
  fval.setLong(pProgress > 100 ? 100 : pProgress);
  
  pushWorkerCallback((ECOpushWorkerCallback_Ptr)gECOcallbacks[ECOpushWorkerCallback_CallBkID],
                     cPushWorkerCallbackFlag_AlwaysCallComplete | cPushWorkerCallbackFlag_cDoNotCreateNewResultRow,
                     &sProgressMethod);
}
// caa1545 end

worker::eWorkerState FileOpsWorker::dRun()
{
  incThreadCount();
  
  setErrorCode(kWorkerNoError);
  do
  {
    if (getState() != worker::kStateRunning) { break; }
		if (mCancelled) { break; } // caa2595 - last second check!
    mCancelled = qfalse;
    mProgressThreadCleared = qfalse; // caa2540
    startProgressThread();
    execute();
    if (!mResultRow || mCancelled || mDetached) { break; } // caa2595
  } only_once;
    
  decThreadCount();
  
  threadCritical tdc;
  clearThreadHandle();
  return mCancelled || mDetached ? worker::kStateCancelled : worker::kStateComplete; // caa2595
}

void FileOpsWorker::dGetNativeError()
{
  qlong errorCode = mErrorInfo.getErrorCode();
  switch (errorCode)
  {
    case kWorkerMissingParams: { errorCode = 9900; break; }
    case kWorkerNotInitialized: { errorCode = 9901; break; }
    default: { errorCode = 0; }
  }
  EXTfldval errorText; errorText.setEmpty(fftCharacter, dpDefault);
  if (errorCode) {
    str255 s;
    RESloadString(gInstLib, errorCode, s);
    errorText.setChar(s);
  }
  mErrorInfo.setNativeErrorText(errorText);
}

const qshort xnFileOpsWorkerParamFrom = 1,
             xnFileOpsWorkerParamTo   = 2,
             xnFileOpsWorkerParamTag  = 3, // caa2593
             xnFileOpsWorkerParamCreatePath = 4, // caa2630
             xnFileOpsWorkerParamReplace = 5, // caa2630
             xnFileOpsWorkerParamMerge = 6; // caa2630

const qshort xnFileOpsDeleteWorkerParamDeleteContents = 2,
             xnFileOpsDeleteWorkerParamRecursive      = 3,
             xnFileOpsDeleteWorkerParamTag            = 4; // caa2593

qbool FileOpsCopyWorker::dPrepareParams(EXTCompInfo* pEci, eWorkerError &pErrorCode)
{
  qshort paramCount = ECOgetParamCount(pEci);
  if (paramCount < xnFileOpsWorkerParamTo) {
    pErrorCode = (eWorkerError) kWorkerMissingParams;
    return qfalse;
  }
  
  EXTParamInfo* paramFrom = ECOfindParamNum(pEci, xnFileOpsWorkerParamFrom);
  EXTParamInfo* paramTo = ECOfindParamNum(pEci, xnFileOpsWorkerParamTo);
  
  if (paramFrom && paramTo) {
    mFrom.getFromOmnis((qfldval)paramFrom->mData);
    mTo.getFromOmnis((qfldval)paramTo->mData);
  } else {
    mFrom.setEmpty(fftCharacter, dpDefault);
    mTo.setEmpty(fftCharacter, dpDefault);
  }

	// caa2593 start
	mHasTag = qfalse;
	if (paramCount >= xnFileOpsWorkerParamTag) {
		EXTParamInfo* paramTag = ECOfindParamNum(pEci, xnFileOpsWorkerParamTag);
		if (paramTag) {
			mTag.getFromOmnis((qfldval)paramTag->mData);
			mHasTag = qtrue;
		}
	}
	// caa2593 end
  
  // caa2630 start
  mCreatePath = qfalse;
  if (paramCount >= xnFileOpsWorkerParamCreatePath) {
    EXTParamInfo* paramCreatePath = ECOfindParamNum(pEci, xnFileOpsWorkerParamCreatePath);
    if (paramCreatePath) {
      EXTfldval createPath = (qfldval)paramCreatePath->mData; // caa2645
      mCreatePath = createPath.getBool() == preBoolTrue;
    }
  }
  
  mReplace = qfalse;
  if (paramCount >= xnFileOpsWorkerParamReplace) {
    EXTParamInfo* paramReplace = ECOfindParamNum(pEci, xnFileOpsWorkerParamReplace);
    if (paramReplace) {
      EXTfldval replace = (qfldval)paramReplace->mData; // caa2645
      mReplace = replace.getBool() == preBoolTrue;
    }
  }
  
  mMerge = qfalse;
  if (paramCount >= xnFileOpsWorkerParamMerge) {
    EXTParamInfo* paramMerge = ECOfindParamNum(pEci, xnFileOpsWorkerParamMerge);
    if (paramMerge) {
      EXTfldval merge = (qfldval)paramMerge->mData; // caa2645
      mMerge = merge.getBool() == preBoolTrue;
    }
  }
  // caa2630 end

  return qtrue;
}

qbool FileOpsMoveWorker::dPrepareParams(EXTCompInfo* pEci, eWorkerError &pErrorCode)
{
  qshort paramCount = ECOgetParamCount(pEci);
  if (paramCount < xnFileOpsWorkerParamTo) {
    pErrorCode = (eWorkerError) kWorkerMissingParams;
    return qfalse;
  }
  
  EXTParamInfo* paramFrom = ECOfindParamNum(pEci, xnFileOpsWorkerParamFrom);
  EXTParamInfo* paramTo = ECOfindParamNum(pEci, xnFileOpsWorkerParamTo);
  
  if (paramFrom && paramTo) {
    mFrom.getFromOmnis((qfldval)paramFrom->mData);
    mTo.getFromOmnis((qfldval)paramTo->mData);
  } else {
    mFrom.setEmpty(fftCharacter, dpDefault);
    mTo.setEmpty(fftCharacter, dpDefault);
  }

	// caa2593 start
	mHasTag = qfalse;
	if (paramCount >= xnFileOpsWorkerParamTag) {
		EXTParamInfo* paramTag = ECOfindParamNum(pEci, xnFileOpsWorkerParamTag);
		if (paramTag) {
			mTag.getFromOmnis((qfldval)paramTag->mData);
			mHasTag = qtrue;
		}
	}
	// caa2593 end

  // caa2630 start
  mCreatePath = qfalse;
  if (paramCount >= xnFileOpsWorkerParamCreatePath) {
    EXTParamInfo* paramCreatePath = ECOfindParamNum(pEci, xnFileOpsWorkerParamCreatePath);
    if (paramCreatePath) {
      EXTfldval createPath = (qfldval)paramCreatePath->mData; // caa2645
      mCreatePath = createPath.getBool() == preBoolTrue;
    }
  }

  mReplace = qfalse;
  if (paramCount >= xnFileOpsWorkerParamReplace) {
    EXTParamInfo* paramReplace = ECOfindParamNum(pEci, xnFileOpsWorkerParamReplace);
    if (paramReplace) {
      EXTfldval replace = (qfldval)paramReplace->mData; // caa2645
      mReplace = replace.getBool() == preBoolTrue;
    }
  }

  mMerge = qfalse;
  if (paramCount >= xnFileOpsWorkerParamMerge) {
    EXTParamInfo* paramMerge = ECOfindParamNum(pEci, xnFileOpsWorkerParamMerge);
    if (paramMerge) {
      EXTfldval merge = (qfldval)paramMerge->mData; // caa2645
      mMerge = merge.getBool() == preBoolTrue;
    }
  }
  // caa2630 end
  
  return qtrue;
}

qbool FileOpsDeleteWorker::dPrepareParams(EXTCompInfo* pEci, eWorkerError &pErrorCode)
{
  qshort paramCount = ECOgetParamCount(pEci);
  if (paramCount < xnFileOpsWorkerParamFrom) {
    pErrorCode = (eWorkerError) kWorkerMissingParams;
    return qfalse;
  }
  
  EXTParamInfo* paramPath = ECOfindParamNum(pEci, xnFileOpsWorkerParamFrom);
  if (paramPath)
    mPath.getFromOmnis((qfldval)paramPath->mData);
  else
    mPath.setEmpty(fftCharacter, dpDefault);
  
  if (paramCount >= xnFileOpsDeleteWorkerParamDeleteContents) {
    EXTParamInfo* paramDeleteContents = ECOfindParamNum(pEci, xnFileOpsDeleteWorkerParamDeleteContents);
    if (paramDeleteContents)
      mDeleteContents.getFromOmnis((qfldval)paramDeleteContents->mData);
		else
      mDeleteContents.setBool(qfalse);
  }
  
  if (paramCount >= xnFileOpsDeleteWorkerParamRecursive) {
    EXTParamInfo* paramRecursive = ECOfindParamNum(pEci, xnFileOpsDeleteWorkerParamRecursive);
    if (paramRecursive)
      mRecursive.getFromOmnis((qfldval)paramRecursive->mData);
		else
      mRecursive.setBool(qfalse);
  }

	// caa2593 start
	mHasTag = qfalse;
	if (paramCount >= xnFileOpsWorkerParamTag) {
		EXTParamInfo* paramTag = ECOfindParamNum(pEci, xnFileOpsDeleteWorkerParamTag);
		if (paramTag) {
			mTag.getFromOmnis((qfldval)paramTag->mData);
			mHasTag = qtrue;
		}
	}
	// caa2593 end
  
  return qtrue;
}

qbool FileOpsWorker::methodCall(EXTCompInfo *pEci)
{
  qlong methodId = ECOgetId(pEci);
  qbool hasRtnVal = qtrue;
  qbool rtnCode = qfalse;
  switch (methodId)
  {
    case cMethod_initcopy:
    case cMethod_initmove:
    case cMethod_initdelete: {
      rtnCode = init(pEci);
      break;
    }
    case cMethod_run: {
      rtnCode = run(pEci);
      pushWorkerCallbackFromRunMethod();
      break;
    }
    case cMethod_start: {
      rtnCode = start(pEci);
      break;
    }
    case cMethod_cancel: {
      cancel();
      rtnCode = qtrue;
      break;
    }
    default: { hasRtnVal = qfalse; }
  }
  if (hasRtnVal) {
    EXTfldval rtnVal;
    rtnVal.setLong(rtnCode);
    ECOaddParam(pEci, &rtnVal);
  }
  
  return qtrue;
}

// caa1545 start
qbool tqfFileOpsObj::isDirectory(EXTfldval &pPath) // jmg1529
{
  EXTqlist *fileInfo = NULL;

	// caa2656 start: if ends with filepath separator, it fails to find the folder
	if (pPath.getCharLen() == pPath.lastPos(EXTfile::pathSeparator())) {
		EXTfldval copyPath; copyPath.concat(pPath);
		copyPath.reduceCharLen(copyPath.getCharLen() - 1);
		tqfFileOpsObj::getFileInfo(copyPath, cInfoDirectory, fileInfo);
	}
	else
		tqfFileOpsObj::getFileInfo(pPath, cInfoDirectory, fileInfo);
	// caa2656 end

  if (fileInfo)
  {
    EXTfldval fval; fileInfo->getColVal(1, 1, fval);
    // caa2641 start
    qbool rtnVal = qfalse;
    if (!fval.isEmpty())
      rtnVal = fval.getBool() - 1;
    // caa2641 end
    *fileInfo = qnil;
    delete fileInfo;
    return rtnVal;
  }
  return qfalse;
}

static qlong64 getDirectorySize(EXTfldval &pPath)
{
  EXTqlist *fileInfo = NULL;
  tqfFileOpsObj::getFileInfo(pPath, cInfoCalcDirectorySize, fileInfo);
  if (fileInfo)
  {
    EXTfldval fval; fileInfo->getColVal(1, 1, fval);
    qlong64 rtnVal = fval.getLong64();
    *fileInfo = qnil;
    delete fileInfo;
    return rtnVal;
  }
  return 0L;
}

static qlong64 getFileSize(EXTfldval &pPath)
{
  EXTqlist *fileInfo = NULL;
  tqfFileOpsObj::getFileInfo(pPath, cInfoSize, fileInfo);
  if (fileInfo)
  {
    EXTfldval fval; fileInfo->getColVal(1, 1, fval);
    qlong64 rtnVal = fval.getLong64();
    *fileInfo = qnil;
    delete fileInfo;
    return rtnVal;
  }
  return 0L;
}

static qlong64 getSize(EXTfldval &pPath)
{
  if (tqfFileOpsObj::isDirectory(pPath)) return getDirectorySize(pPath); // jmg1529
  else return getFileSize(pPath);
}

void FileOpsCopyWorker::progressThreadProc()
{
  if (mFrom.isEmpty() || mTo.isEmpty()) return;
  
  qulong64 fromSize = getSize(mFrom);
  do {
		if (mCancelled || mDetached || !isRunning()) break; // caa2595
    notifyProgress(qshort(std::round((getSize(mTo)*100.0)/fromSize)));
    ECOsleep(mProgressInterval*1000);
  } while ((!mCancelled || !mDetached) && isRunning()); // caa2595
}

void FileOpsMoveWorker::progressThreadProc()
{
  if (mFrom.isEmpty() || mTo.isEmpty()) return;
  qulong64 fromSize = getSize(mFrom);
  do {
		if (mCancelled || mDetached || !isRunning()) break; // caa2595
    notifyProgress(qshort(std::round((getSize(mTo)*100.0)/fromSize)));
    ECOsleep(mProgressInterval*1000);
	} while ((!mCancelled || !mDetached) && isRunning()); // caa2595
}

void FileOpsDeleteWorker::progressThreadProc()
{
  return;
}

EXTPROC sProgressEntryProc(void* pParams)
{
  void** param = (void**)pParams;
  FileOpsWorker* fow = (FileOpsWorker*)param;
  if (fow) {
    fow->progressThreadProc();
    fow->clearProgressThread();
  }
  ECOexitThread(0);
}

void FileOpsWorker::startProgressThread()
{
  if ((mProgressThread && mProgressThreadCleared) || mCancelled || !isRunning()) // caa2540
    return;
  
  ECOcreateThread(mProgressThread, NULL, (EXTstartRoutine) sProgressEntryProc, this);
}
// caa1545 end
// caa0334 end

// caa2595 start
void FileOpsWorker::detach()
{
	if (!smDetachedWorkers.size())
		smCheckDetachedWorkersTimerId = WNDsetTimerEx(1000, (FARPROC)FileOpsWorker::checkDetachedWorkers);
	
	mDetached = qtrue;

	smDetachedWorkers.push_back(this);
}

void FileOpsWorker::eciDisconnect()
{
	{
		threadCritical tdc;
		if (smDetachedWorkers.empty())
			return;
	}

	while (!smDetachedWorkers.empty()) {
		FileOpsWorker* fileops;
		{
			threadCritical tdc;
			fileops = smDetachedWorkers.front();
		}

		if (fileops->isRunning())
			fileops->cancel();

		{
			threadCritical tdc;
			if (fileops == smDetachedWorkers.front()) {
				smDetachedWorkers.erase(smDetachedWorkers.begin());
				delete fileops;
			}
		}
	}

	WNDkillTimerEx(smCheckDetachedWorkersTimerId);
	smCheckDetachedWorkersTimerId = 0;
}

void FileOpsWorker::checkDetachedWorkers()
{
	threadCritical tdc;

	for (qlong i = static_cast<qlong>(smDetachedWorkers.size() - 1); i >= 0; --i) {
		FileOpsWorker* fileops = smDetachedWorkers[i];

		if (!fileops->isRunning()) {
			delete fileops;
			smDetachedWorkers.erase(smDetachedWorkers.begin() + i);
		}
	}

	if (smDetachedWorkers.empty()) {
		WNDkillTimerEx(smCheckDetachedWorkersTimerId);
		smCheckDetachedWorkersTimerId = 0;
	}
}
// caa2595 end

qbool tqfFileOpsObj::methodCall(EXTCompInfo* pEci)
{
	qlong funcId = ECOgetId(pEci);
	qbool rtnCode = qfalse; EXTfldval rtnVal; qbool hasRtnVal=qfalse;
	// Start rmm9010
	if (cIMethodId_GetLastError != funcId)
		mLastError = e_ok;
	// End rmm9010
	switch (funcId)
	{
		case cIMethodId_OpenFile:			
		case cIMethodId_OpenResources: // mpmCarbon
		{
			close(); // Close any file
			EXTParamInfo* param = ECOfindParamNum( pEci, 1 ); 
			if (param)
			{
				EXTfldval fname((qfldval)param->mData); // rmm9025
				qbool readOnly = qfalse;
				EXTParamInfo* param2 = ECOfindParamNum(pEci, 2);
				if (param2)
				{
					EXTfldval fval((qfldval)param2->mData);
					readOnly = (qbool)fval.getLong();
				}
				mLastError = (funcId == cIMethodId_OpenFile) ? open(fname, readOnly, qfalse) : openResources(fname, readOnly, qfalse); // mpmCarbon // rmm9010
				if (e_ok == mLastError) // mpmCarbon // rmm9010
				{
					mIsOpen = rtnCode = qtrue;
					mIsOpenReadonly = readOnly; // AE5184
					mFileName = fname;
				}
			}
			else
				mLastError = e_toofewparameters;	// rmm9010
			rtnVal.setLong( rtnCode ); rtnCode = hasRtnVal = qtrue;
			break;
		}
		case cIMethodId_CreateFile:			
		{
			close(); // Close any file
			EXTParamInfo* param = ECOfindParamNum( pEci, 1 ); 
			if (param)
			{
				EXTfldval fname( (qfldval)param->mData ); // rmm9025
				#ifdef ismac
					//<ijt239> mac can handle extra parameters
					str255 ftype, ctype;
					EXTParamInfo* param2 = ECOfindParamNum( pEci, 2 ); 
					if ( param2 )
					{
						EXTfldval fval2( (qfldval)param2->mData );
						fval2.getChar(ftype);
					}
					EXTParamInfo* param3 = ECOfindParamNum( pEci, 3 ); 
					if ( param3 )
					{
						EXTfldval fval3( (qfldval)param3->mData );
						fval3.getChar(ctype);
					}
					// rmmuni_osx6:added call to open the file, since create also opens the file on other platforms
					// rmm8939a: Removed createres parameter
					// Start rmm9010
					mLastError = createFile(fname, &ftype, &ctype);
					if (mLastError != kFileOpsOK)
						mLastError += cFILEOPSerrCodeBase;
					else
						mLastError = open(fname, qfalse, qfalse);
				#else				
					mLastError = create(fname, qfalse);
				#endif
					if (e_ok == mLastError)
					// End rmm9010
					{
						mIsOpen = rtnCode = qtrue;
						mIsOpenReadonly = qfalse; // AE5184
						mFileName = fname;
					}
			}
			else
				mLastError = e_toofewparameters; // rmm9010
			rtnVal.setLong( rtnCode ); rtnCode = hasRtnVal = qtrue;
			break;
		}
		case cIMethodId_CreateFileTemp:			
		{
			close(); // Close any file
			// Start rmm9010
			mLastError = createTemp();
			if (e_ok == mLastError)
			{
				// End rmm9010
				mIsOpen = rtnCode = qtrue;
				mIsOpenReadonly = qfalse; // AE5184
				getName( mFileName,  qtrue ); // AE6765
			}
			rtnVal.setLong( rtnCode ); rtnCode = hasRtnVal = qtrue;
			break;
		}
		case cIMethodId_ReadFile:			
		{
			// caa2578 start: reorganised this a little
			rtnVal.setLong(qfalse);
			hasRtnVal = rtnCode = qtrue;

			EXTParamInfo* param = ECOfindParamNum(pEci, 1);

			if (!param || !mIsOpen) {
				mLastError = mIsOpen ? e_toofewparameters : e_filenotopen;	
				break;
			}

			EXTfldval fval((qfldval)param->mData);
			ffttype fftType; fval.getType(fftType);
			//PK1796 - this function is supposed to read binary by default, if #null, fftType returns fftCharacter which upsets below. null is now assumed to be binary
			if ( fval.isNull() )
				fftType = fftBinary;
			if (!(fftType == fftBinary || fftType == fftPicture))
				fftType = fftCharacter;	// CR0671 : fftPicture

			// Reading start position, use current position if none is supplied
			qlong64 startPos = -1; // rmm9010
			EXTParamInfo* param2 = ECOfindParamNum(pEci, 2);

			if (param2) {
				EXTfldval fval2((qfldval)param2->mData);
				startPos = fval2.getLong64(); // rmm9010
			}
			if (startPos < 0)
				startPos = getPosition();

			// Length to read (in bytes), read until EOF if none is supplied
			qlong bytesToRead = -1;
			EXTParamInfo* param3 = ECOfindParamNum(pEci, 3);

			if (param3) {
				EXTfldval fval3((qfldval)param3->mData);
				bytesToRead = fval3.getLong();
			}

			// Note: if user requests more than what can fit in 
			// qlong, it will overflow on purpose, so we can
			// use bytes to EOF instead
			if (bytesToRead < 0) {
				qlong64 bytesToEOF = getLength(qfalse) - startPos;
				if (bytesToEOF > maxbinlen) {
					mLastError = e_readtoolarge;
					break;
				}
				else bytesToRead = (qlong)bytesToEOF;
			}

			ECOsetParameterChanged(pEci, 1);

			if (bytesToRead <= 0) {
				mLastError = e_nodatatoread;
				fval.setEmpty(fftType, dpDefault); // rmm9010: Use fftType
				break;
			}

			// Read file
			qHandle dataHan = HANglobalAlloc(bytesToRead);
			qHandlePtr dhan(dataHan, 0);
			qlong bytesRead = 0;
			mLastError = read(*dhan, startPos, bytesToRead, bytesRead);
			if (!bytesRead)
				mLastError = e_nodatatoread;

			if (e_ok == mLastError) {
				rtnVal.setLong(qtrue);
				dhan.dataLen(bytesRead);
			}

			fval.setHandle(fftType, dataHan, qfalse);
			dhan.setNull();
			
			break;
			// caa2578 end
		}
		case cIMethodId_WriteFile:		
		{
			EXTParamInfo* param = ECOfindParamNum( pEci, 1 ); 
			if ( param && mIsOpen )
			{
				// Obtain all necessary information
				EXTfldval fval( (qfldval)param->mData );
				ffttype fftType; fval.getType(fftType);				
				if ( fftType!=fftBinary ) fftType=fftCharacter; 
				// Start position (to read from; if none supplied then use current position)
				EXTParamInfo* param2 = ECOfindParamNum( pEci, 2 ); 
				qlong64 startPos = -1; // rmm9010
				if (param2)
				{
					EXTfldval fval2( (qfldval)param2->mData );
					startPos = fval2.getLong64(); // rmm9010
				}
				if (startPos < 0)
					startPos = getPosition(qfalse); // rmm9010
				qHandle dataHan = fval.getHandle(qfalse); // Don't take a copy
				qHandlePtr dhan(dataHan,0);
				qbyte* dptr = NULL; qlong dlen = 0; // AE6342
				if ( dataHan ) { dptr = (qbyte*)*dhan; dlen = dhan.dataLen(); }	// AE6342
				// Start rmm9010
				if (mIsOpenReadonly)
					mLastError = e_readonly;
				else
				{
					mLastError = write(dptr, startPos, dlen); // AE5184 // AE6342
					if (e_ok == mLastError)
					{
						// End rmm9010
						rtnCode = qtrue;
					}
				}
				dhan.setNull(); // Not our data to delete
			}
			// Start rmm9010
			else
				mLastError = mIsOpen ? e_toofewparameters : e_filenotopen;
			// End rmm9010
			rtnVal.setLong( rtnCode ); hasRtnVal = rtnCode = qtrue;
			break;
		}
		case cIMethodId_GetFileSize:
		{
			hasRtnVal = qtrue; qlong64 length = -1; // rmm9010
			if (mIsOpen)
				length = getLength(qfalse); // rmm9010
			else
				mLastError = e_filenotopen;	// rmm9010
			rtnVal.setLong64(length); rtnCode = qtrue; // rmm9010
			break;
		}
		case cIMethodId_SetFileSize:	
		{
			EXTParamInfo* param = ECOfindParamNum( pEci, 1 );
			if ( param && mIsOpen )
			{
				EXTfldval fval( (qfldval)param->mData );
				// Start rmm9010
				qlong64 newLength = fval.getLong64(); 
				if (newLength < 0)
					mLastError = e_invalidparameters;
				else
				{
					mLastError = setLength(newLength);
					if (e_ok == mLastError)
					{
						// End rmm9010
						rtnCode = qtrue;
					}
				}
			}
			// Start rmm9010
			else
				mLastError = mIsOpen ? e_toofewparameters : e_filenotopen;
			// End rmm9010
			rtnVal.setLong( rtnCode ); hasRtnVal = rtnCode = qtrue;
			break;
		}
		case cIMethodId_CloseFile:		{	close(); rtnCode = qtrue; break; }
		case cIMethodId_IsOpen:				{ hasRtnVal=rtnCode=qtrue; rtnVal.setLong(mIsOpen); break; }
		case cIMethodId_GetPosition:	
		{ 
			hasRtnVal = rtnCode = qtrue; qlong64 position = -1; // rmm9010
			if (mIsOpen)
				position = getPosition();
			else
				mLastError = e_filenotopen;	// rmm9010
			rtnVal.setLong64(position);	 // rmm9010
			break;
		}
		case cIMethodId_SetPosition:
		{
			EXTParamInfo* param = ECOfindParamNum( pEci, 1 ); 
			if (param && mIsOpen)
			{
				EXTfldval fval( (qfldval)param->mData );
				// Start rmm9010
				qlong64 newPos = fval.getLong64(); 
				if (newPos < 0)
					mLastError = e_invalidparameters;
				else
				{
					mLastError = setPosition(newPos);
					if (e_ok == mLastError)
					{
						// End rmm9010
						rtnCode = qtrue;
					}
				}
			}
			// Start rmm9010
			else
				mLastError = mIsOpen ? e_toofewparameters : e_filenotopen;
			// End rmm9010
			rtnVal.setLong( rtnCode ); hasRtnVal = rtnCode = qtrue;
			break;
		}
		case cIMethodId_GetFileInfo:
		{
			if (mIsOpen)
			{
				qulong retInfo = cInfoName;
				EXTParamInfo* param = ECOfindParamNum( pEci, 1 ); 
				if (param)
				{
					EXTfldval fval( (qfldval)param->mData );
					retInfo = fval.getLong();
				}
				EXTqlist *fileInfo = NULL;
				getFileInfo( mFileName, retInfo, fileInfo );
				if ( fileInfo )
				{
					rtnVal.setList( fileInfo, qtrue);
					delete fileInfo;
				}
				hasRtnVal = rtnCode = qtrue;
			}
			else mLastError = e_filenotopen;	// rmm9010
			break;
		}
		case cIMethodId_SetFileInfo:
		{
			if (mIsOpen)
			{
				// Start rmm4067
				OpsErr err = setFileInfo( mFileName, pEci, 1 );
				rtnVal.setLong(err);
				rtnCode = qtrue;
				// End rmm4067
				hasRtnVal = qtrue;
				mLastError = cFILEOPSerrCodeBase + err;	// rmm9010
			}
			else mLastError = e_filenotopen;	// rmm9010
			break;
		}
		// Start rmm5093
		case cIMethodId_ReadCharacter:
		{
			rtnCode = readCharacter(pEci);
			rtnVal.setLong(rtnCode);
			hasRtnVal = rtnCode = qtrue;
			break;
		}
		case cIMethodId_WriteCharacter:
		{
			rtnCode = writeCharacter(pEci);
			rtnVal.setLong(rtnCode);
			hasRtnVal = rtnCode = qtrue;
			break;
		}
		// End rmm5093
		// Start rmm9010
		case cIMethodId_GetLastError:
		{
			// Omnis errors generate a code of 100000 + omnis error code
			// Fileops errors generate just their code
			if (!mLastError)
				rtnVal.setLong(0);
			else if (mLastError < ( cFILEOPSerrCodeBase + kFileOpsLastErrorCode) )	// CR0525 : factor in negative error codes
				rtnVal.setLong(100000 + mLastError);
			else
				rtnVal.setLong(mLastError - cFILEOPSerrCodeBase);
			hasRtnVal = rtnCode = qtrue;
			EXTParamInfo* param = ECOfindParamNum(pEci, 1);
			if (param)
			{
				EXTfldval fval((qfldval)param->mData);
				if (mLastError < ( cFILEOPSerrCodeBase + kFileOpsLastErrorCode) )	// CR0525 : factor in negative error codes
					ECOgetOmnisErrorText(mLastError, fval);
				else
					ECOgetConstantDescriptionByValue(mLastError - cFILEOPSerrCodeBase, 20001, 20049, fval);
				ECOsetParameterChanged(pEci, 1);
			}
			break;
		}
		// End rmm9010
	}
	if ( hasRtnVal )
    ECOaddParam(pEci,&rtnVal);
	return rtnCode;
}

// Start rmm5093
qbool tqfFileOpsObj::readCharacter(EXTCompInfo* pEci)
{
	// Make sure file is open
	if (!mIsOpen)
	{
		mLastError = e_filenotopen;	// rmm9010
		return qfalse;
	}
	// Get the read encoding
	preconst encoding; // rmm5334
	if (!getEncodingParameter(pEci, qtrue, encoding))
	{
		mLastError = e_invalidparameters;	// rmm9010
		return qfalse;
	}
	// Get the parameter to receive the data
	EXTParamInfo *param = ECOfindParamNum(pEci, 2); 
	if (!param)
	{
		mLastError = e_toofewparameters;	// rmm9010
		return qfalse;
	}
	// Start rmm6921
	qlong64 offset = 0; // rmm9010
	qlong64 maxlen = 0; // rmm9010
	if (ECOgetParamCount(pEci) >= 3)
	{
		EXTParamInfo *param = ECOfindParamNum(pEci, 3); 
		if (!param)
		{
			mLastError = e_toofewparameters;	// rmm9010
			return qfalse;
		}
		EXTfldval fvalOffset((qfldval) param->mData);
		offset = fvalOffset.getLong64(); // rmm9010
	}
	if (ECOgetParamCount(pEci) >= 4)
	{
		EXTParamInfo *param = ECOfindParamNum(pEci, 4); 
		if (!param)
		{
			mLastError = e_toofewparameters;	// rmm9010
			return qfalse;
		}
		EXTfldval fvalMaxlen((qfldval) param->mData);
		// Start rmm9010
		maxlen = fvalMaxlen.getLong64(); 
		if (maxlen > maxbinlen)
		{
			mLastError = e_readtoolarge;
			return qfalse;
		}
		// End rmm9010
	}
	// Start rmm9434
	qbool checkUTF8 = qfalse;
	if (ECOgetParamCount(pEci) >= 5)
	{
		EXTParamInfo *param = ECOfindParamNum(pEci, 5);
		if (!param)
		{
			mLastError = e_toofewparameters;
			return qfalse;
		}
		EXTfldval fvalCheckUTF8((qfldval)param->mData);
		checkUTF8 = (fvalCheckUTF8.getLong() != 0);
		if (checkUTF8 && (maxlen || offset))
		{
			mLastError = kFileOpsErrorCheckUTF8 + cFILEOPSerrCodeBase;
			return qfalse;
		}
	}
	// End rmm9434
	if (offset > 0 && preUniTypeAuto == encoding)
	{
		// Use the encoding from a previous read if automatic detection is specified
		encoding = mFileEncoding;
	}	
	// End rmm6921
	// Set file position to start of file
	// Start rmm9010
	mLastError = setPosition(offset); // rmm6921: Now use the offset
	if (e_ok != mLastError)
		return qfalse;
	// End rmm9010
	// Get the file size
	qlong64 fileLength = getLength(qfalse); // rmm9010	// caa0001 added qfalse
	if (fileLength < 0)
	{
		mLastError = e_nodatatoread;
		return qfalse;
	}
			
	// Set up output fval
	qlong64 nextOffset = 0;	// rmm6921 // rmm9010
	EXTfldval fval((qfldval) param->mData);
	fval.setEmpty(fftCharacter, dpDefault);
	if (fileLength > 0)
	{
		// Start rmm6921: calculate how much to read
		qlong64 maxBytesPerChar = 1; // rmm9010
		if (preUniTypeAuto == encoding || preUniTypeUTF8 == encoding || preUniTypeUTF32BE == encoding || preUniTypeUTF32LE == encoding || preUniTypeUTF32 == encoding)
			maxBytesPerChar = 4;
		else if (preUniTypeUTF16BE == encoding || preUniTypeUTF16LE == encoding || preUniTypeUTF16 == encoding)
			maxBytesPerChar = 4;	// Allows for surrogate pairs
		qlong64 bytesToRead = fileLength; // rmm9010
		if (maxlen > 0)
			bytesToRead = qlong64(CHR_UTF32_SIGNATURE_LEN) + maxBytesPerChar*maxlen; // rmm9010
		if ((bytesToRead + offset) > fileLength) bytesToRead = fileLength - offset;
		if (bytesToRead <= 0 || bytesToRead > maxbinlen) // rmm9010
		{
			mLastError = (bytesToRead <= 0) ? e_nodatatoread : e_readtoolarge; // rmm9010
			return qfalse;
		}
		// End rmm6921

		// Read file contents into temporary memory
		qbyte *tempMem = (qbyte *)malloc((qlong)bytesToRead); // rmm6921 // rmm9010
		// Start rmm9010
		mLastError = read(tempMem, offset, (qlong)bytesToRead); // rmm6921 // rmm9010
		if (e_ok == mLastError)
		{
			// End rmm9010
			// Start rmm9434
			if (preUniTypeAuto == encoding && checkUTF8)
				encoding = doCheckUtf8(tempMem, (qlong)bytesToRead);
			// End rmm9434
			// Convert to character
			CHRconvFromUnicodeEncoding cfu(encoding, tempMem, (qlong)bytesToRead); // rmm6921 // rmm9010
			qchar *dataPtr = cfu.charDataPtr();
			qlong len = cfu.charLen();
			// Start rmm6921: if maxlen is specified, we need to restrict the return to maxlen
			mFileEncoding = cfu.getActualEncoding();	// Encoding for next time, if auto is specified
			nextOffset = offset + bytesToRead;
			if (maxlen > 0 && len > maxlen)
			{
				// ctu will contain the encoded data corresponding to how much data we wish to return, with no BOM
				CHRconvToUnicodeEncoding ctu(mFileEncoding, (qbyte *)dataPtr, (qlong)(QBYTELEN(maxlen)), qfalse); // rmm9010
				// Set next offset according to the encoding of the maxlen characters
				nextOffset = offset + ctu.len();
				// Check for a BOM at the start of the temp data - allow for that in the offset
				if (preUniTypeUTF8 == mFileEncoding && bytesToRead >= CHR_UTF_SIGNATURE_LEN && !memcmp(gUtf8Signature, tempMem, CHR_UTF_SIGNATURE_LEN))
				{
					nextOffset += CHR_UTF_SIGNATURE_LEN;
				}
				else if (preUniTypeUTF16BE == mFileEncoding && bytesToRead >= 2 && !memcmp(tempMem, gUtf16BESignature, 2))
				{
					nextOffset += 2;
				}
				else if (bytesToRead >= 2 && !memcmp(tempMem, gUtf16LESignature, 2))
				{
					if (preUniTypeUTF16LE == mFileEncoding && bytesToRead >= 4 && tempMem[2] == '\x00' && tempMem[3] == '\x00')
					{
						nextOffset += 4;
					}
					else if (preUniTypeUTF16LE == mFileEncoding)
					{
						nextOffset += 2;
					}
				}
				else if (bytesToRead >= 4 && !memcmp(tempMem, gUtf32BESignature, 4))
				{
					nextOffset += 4;
				}
				len = (qlong)maxlen; // rmm9010
			}
			// End rmm6921
			fval.setChar(dataPtr, len);
		}
		else
		{
			free(tempMem); // rmm9010
			return qfalse;
		}
		free(tempMem); // rmm9010
	}
	else mLastError = e_nodatatoread; // rmm9010
	// Success
	// Mark output parameter as changed
	ECOsetParameterChanged(pEci, 2);
	// Start rmm6921: set next offset if required
	if (maxlen > 0 && ECOgetParamCount(pEci) >= 3)
	{
		EXTParamInfo *param = ECOfindParamNum(pEci, 3); 
		if (param)
		{
			EXTfldval fvalOffset((qfldval)param->mData);
			fvalOffset.setLong64(nextOffset); // rmm9010
			ECOsetParameterChanged(pEci, 3);
		}
	}
	// End rmm6921
	return qtrue;
}

// rmm9434: Called when encoding is preUniTypeAuto, to detect possible UTF-8 data
preconst tqfFileOpsObj::doCheckUtf8(qbyte *pData, qlong pLen)
{
	// Check for a BOM - if present, leave encoding as it was specified
	if (pLen >= CHR_UTF_SIGNATURE_LEN && !memcmp(gUtf8Signature, pData, CHR_UTF_SIGNATURE_LEN))
		return preUniTypeAuto;
	if (pLen >= 2 && !memcmp(pData, gUtf16BESignature, 2))
		return preUniTypeAuto;
	if (pLen >= 2 && !memcmp(pData, gUtf16LESignature, 2))
		return preUniTypeAuto;
	if (pLen >= 4 && !memcmp(pData, gUtf32BESignature, 4))
		return preUniTypeAuto;
	// Note - gUtf32LESignature has the same start as gUtf16LESignature, so no need to check for that here
	return CHRunicode::isUtf8Data(pData, pLen) ? preUniTypeUTF8 : preUniTypeAuto;
}

qbool tqfFileOpsObj::writeCharacter(EXTCompInfo* pEci)
{
	// Make sure file is open
	if (!mIsOpen || mIsOpenReadonly)
	{
		mLastError = !mIsOpen ? e_filenotopen : e_readonly;	// rmm9010
		return qfalse;
	}
	// Get the write encoding
	preconst encoding; // rmm5334
	if (!getEncodingParameter(pEci, qfalse, encoding))
	{
		mLastError = e_invalidparameters;	// rmm9010
		return qfalse;
	}
	// Start rmm6920: get the append option
	qbool append = qfalse;
	if (ECOgetParamCount(pEci) >= 3)
	{
		EXTParamInfo *param = ECOfindParamNum(pEci, 3); 
		if (!param)
		{
			mLastError = e_toofewparameters;	// rmm9010
			return qfalse;
		}
		EXTfldval fvalAppend((qfldval) param->mData);
		append = fvalAppend.getLong() != 0;
	}
	// End rmm6920
	// Start rmm10234: Get the BOM option
	qbool addBOM = qtrue;
	if (ECOgetParamCount(pEci) >= 4)
	{
		EXTParamInfo* param = ECOfindParamNum(pEci, 4);
		if (!param)
		{
			mLastError = e_toofewparameters;
			return qfalse;
		}
		EXTfldval fvalAddBOM((qfldval)param->mData);
		addBOM = fvalAddBOM.getLong() != 0;
	}
	// End rmm10234
	// Get the data to write to the file
	EXTParamInfo *param = ECOfindParamNum(pEci, 2); 
	if (!param)
	{
		mLastError = e_toofewparameters;	// rmm9010
		return qfalse;
	}
	EXTfldval fval((qfldval) param->mData);
	fval.conv(fftCharacter);
	qHandle han = fval.getHandle(qfalse);
	qHandlePtr hp(han, 0);
	// Encode the data
	qlong64 fileLength = getLength(qfalse);	// rmm6920 // rmm9010
	CHRconvToUnicodeEncoding ctu(encoding, *hp, hp.dataLen(), addBOM && (!append || !fileLength)); // rmm6920 // rmm10234
	// Start rmm6920
	qlong64 offset; // rmm9010
	if (append)
	{
		offset = fileLength;
	}
	else
	{
		offset = 0;
		// End rmm6920
		// Make sure the file is empty
		// Start rmm9010
		mLastError = setLength(0);
		if (e_ok != mLastError)
		{
			// End rmm9010
			return qfalse;
		}
	}
	// Write the data
	// Start rmm9010
	mLastError = write(ctu.dataPtr(), offset, ctu.len()); // rmm6920
	return e_ok == mLastError;
	// End rmm9010
}

// rmm5334: old encoding values, here for conversion purposes only
enum FFencoding
{
	FFencodingBinary = 0,
	FFencodingNativeCharacters = 1,
	FFencodingUtf8 = 2,
	FFencodingUtf16BE = 3,
	FFencodingUtf16LE = 4,
	FFencodingDetect = 5,			// Data is character; formfile attempts to determine the encoding of the file, when reading from disk
	FFencodingUtf16 = 6,			// Data is UTF-16BE or LE, depending on the machine's endian type
	FFencodingUtf32BE = 7,		
	FFencodingUtf32LE = 8,
	FFencodingUtf32 = 9				// Data is UTF-32BE or LE, depending on the machine's endian type
};	

// rmm5334: used to convert old encoding values (now deprecated) to the kUniType... values we now use.
static preconst sGetFileEncoding(qlong pEncoding)
{
	switch (pEncoding)
	{
		case FFencodingBinary:						return preUniTypeBinary;
		case FFencodingNativeCharacters:	return preUniTypeNativeCharacters;
		case FFencodingUtf8:							return preUniTypeUTF8;
		case FFencodingUtf16BE:						return preUniTypeUTF16BE;
		case FFencodingUtf16LE:						return preUniTypeUTF16LE;
		case FFencodingDetect:						return preUniTypeAuto;
		case FFencodingUtf16:							return preUniTypeUTF16;
		case FFencodingUtf32BE:						return preUniTypeUTF32BE;
		case FFencodingUtf32LE:						return preUniTypeUTF32LE;
		case FFencodingUtf32:							return preUniTypeUTF32;
	}
	return (preconst) pEncoding;
}

qbool tqfFileOpsObj::getEncodingParameter(EXTCompInfo* pEci, qbool pReading, preconst &pEncoding) // rmm5334
{
	EXTParamInfo *param = ECOfindParamNum(pEci, 1); 
	if (!param)
		return qfalse;

	EXTfldval fval((qfldval) param->mData);
	pEncoding = sGetFileEncoding(fval.getLong());	// rmm5334: get parameter value, convert if necessary

	if (!pReading && preUniTypeAuto == pEncoding) // rmm5334
		return qfalse;
		
	// rmm5334: changed validation
	if (pEncoding >= preUniTypeF && pEncoding < preUniTypeBinary)
	{
		return qtrue;
	}
	return qfalse;
}
// End rmm5093

void tqfFileOpsObj::close()
{
	mFileName.setEmpty(fftCharacter, dpDefault);	// rmm9025
	if ( mIsOpen )
		EXTfile::close();	
	mIsOpen = qfalse;
}

// Component library entry point (name as declared in resource 31000 )
extern "C" LRESULT OMNISWNDPROC FileOpsProc(HWND hwnd, UINT Msg,WPARAM wParam,LPARAM lParam,EXTCompInfo* eci)
{
	 // Initialize callback tables - THIS MUST BE DONE 
   ECOsetupCallbacks(hwnd, eci);		
	 switch (Msg)
	 {
			// ECM_OBJCONSTRUCT - this is a message to create a new object.
			case ECM_OBJCONSTRUCT:				
			{
        // caa0334 start
				if ( eci->mCompId >= cObject_FIRST  && eci->mCompId <= cObject_LAST )
				{
          
          OmnisObjectContainer* object = (OmnisObjectContainer*)ECOfindNVObject( eci->mOmnisInstance, lParam );
          if (object)
            return qtrue;
          
          OmnisObjectPtr oo = 0;
          switch (eci->mCompId)
          {
            case cObject_FileOps: { oo = new tqfFileOpsObj((qobjinst)lParam); break; }
            case cObject_FileOpsCopyWorker: { oo = new FileOpsCopyWorker((qobjinst)lParam); break; }
            case cObject_FileOpsMoveWorker: { oo = new FileOpsMoveWorker((qobjinst)lParam); break; }
            case cObject_FileOpsDeleteWorker: { oo = new FileOpsDeleteWorker((qobjinst)lParam); break; }
          }
          OmnisObjectContainer* obj = new OmnisObjectContainer((qobjinst)lParam, oo);
          ECOinsertNVObject( eci->mOmnisInstance, lParam, (void*)obj );
          
					return qtrue;
				}
				return qfalse;
        // caa0334 end
			}    
			
			// ECM_OBJDESTRUCT - this is a message to inform you to delete the object
			case ECM_OBJDESTRUCT:					
			{
				if ( wParam==ECM_WPARAM_OBJINFO )
				{	
					void* object = ECOremoveNVObject( eci->mOmnisInstance, lParam );
					// Object found now figure out which object type it is
					if ( eci->mCompId >= cObject_FIRST  && eci->mCompId <= cObject_LAST ) // caa0334
					{
						OmnisObjectContainer* fileOps = (OmnisObjectContainer*)object; // caa0334
						// caa2595 start
						switch (eci->mCompId)
						{
							case cObject_FileOpsCopyWorker:
							case cObject_FileOpsDeleteWorker:
							case cObject_FileOpsMoveWorker: {
								FileOpsWorker* worker = static_cast<FileOpsWorker*>(fileOps->mObject);
								if (worker->isRunning()) {
									worker->detach();
									break;
								}
								[[fallthrough]]; // If not running, we can go ahead and delete it
							}
							default:
								delete fileOps;
						}
						// caa2595 end
					}
				}	
				return qtrue;
			}
			case ECM_OBJECT_COPY:
			{
				objCopyInfo* copyInfo = (objCopyInfo*)lParam;
				OmnisObjectContainer* srcobj = (OmnisObjectContainer*)ECOfindNVObject(eci->mOmnisInstance, copyInfo->mSourceObject ); // caa0334
				if ( srcobj )
				{
					OmnisObjectContainer* destObj = (OmnisObjectContainer*)ECOfindNVObject(eci->mOmnisInstance, copyInfo->mDestinationObject ); // caa0334
					if ( !destObj )
					{
						destObj = new OmnisObjectContainer((qobjinst)copyInfo->mDestinationObject,srcobj); // caa0334
						ECOinsertNVObject( eci->mOmnisInstance, copyInfo->mDestinationObject, (void*)destObj );
					}
					else
						destObj->setObject( (qobjinst)copyInfo->mDestinationObject,srcobj );
				}			
				break;
			}
	 		case ECM_CONNECT:
      {
				// Start rmm10372
				#if defined(iswin32) && !defined(isunix)
					CoInitialize(0);
				#endif
				// End rmm10372
				// rmm5009: we use the EXT_FLAG_SESSION flag for this component (makes multithreading more effective)
        return ECOmakeConnectFlags(EXT_FLAG_LOADED|EXT_FLAG_ALWAYS_USABLE|EXT_FLAG_REMAINLOADED|EXT_FLAG_NVOBJECTS|EXT_FLAG_SESSION); // Return external flags // rmm5009 // rmm11466
      } 
      case ECM_DISCONNECT:
      { 
				// Start rmm10372
				#if defined(iswin32) && !defined(isunix)
					CoUninitialize();
				#endif
				// End rmm10372
        // caa0334 start
				FileOpsWorker::eciDisconnect(); // caa2595
        FileOpsCopyWorker::eciDisconnect();
        FileOpsMoveWorker::eciDisconnect();
        FileOpsDeleteWorker::eciDisconnect();
        // caa0334 end
        return qtrue;
      }
      // ECM_GETCOMPLIBINFO - this is sent by OMNIS to find out the name of the library, and
      // the number of components this library supports
      case ECM_GETCOMPLIBINFO:
      {
      	return ECOreturnCompInfo( gInstLib, eci, RES_LIB_NAME, 0 );
      }
			case ECM_GETSTATICOBJECT:
			{
				return ECOreturnMethods( gInstLib, eci, &fileStaticFuncs[0], cSMethod_Count );
			}
			case ECM_GETOBJECT:
			{
				return ECOreturnObjects(gInstLib,eci, &fileObjects[0], cObject_Count);
			}
			case ECM_GETMETHODNAME:
			{
        // caa0334 start
        qbool ok = qfalse;
        switch (eci->mCompId)
        {
          case cObject_FileOps: {
            ok = ECOreturnMethods( gInstLib, eci, &fileObjFuncs[0], cIMethod_Count );
            break;
          }
          case cObject_FileOpsCopyWorker: {
            ok = ECOreturnMethods( gInstLib, eci, &FileOpsWorkerMethods[0], FileOpsWorkerMethods_Count);
            if (!ok) break;
            ok = ECOreturnMethods( gInstLib, eci, &FileOpsCopyWorkerMethods[0], FileOpsCopyWorkerMethods_Count );
            break;
          }
          case cObject_FileOpsMoveWorker: {
            ok = ECOreturnMethods( gInstLib, eci, &FileOpsWorkerMethods[0], FileOpsWorkerMethods_Count);
            if (!ok) break;
            ok = ECOreturnMethods( gInstLib, eci, &FileOpsMoveWorkerMethods[0], FileOpsMoveWorkerMethods_Count );
            break;
          }
          case cObject_FileOpsDeleteWorker: {
            ok = ECOreturnMethods( gInstLib, eci, &FileOpsWorkerMethods[0], FileOpsWorkerMethods_Count);
            if (!ok) break;
            ok = ECOreturnMethods( gInstLib, eci, &FileOpsDeleteWorkerMethods[0], FileOpsDeleteWorkerMethods_Count );
            break;
          }
        }
        if (ok) return ok;
        break;
        // caa0334 end
			}
      case ECM_GETPROPNAME:
     {
       // caa0334 start
       qbool ok = qfalse;
       switch (eci->mCompId)
       {
         case cObject_FileOps: {
           ok = ECOreturnProperties( gInstLib, eci, &fileObjProps[0], cIProp_Count);
           break;
         }
         case cObject_FileOpsCopyWorker:
         case cObject_FileOpsMoveWorker:
         case cObject_FileOpsDeleteWorker: {
           ok = ECOreturnProperties(gInstLib, eci, &FileOpsWorkerProperties[0], FileOpsWorkerProperties_Count);
           break;
         }
       }
       if (ok) return ok;
       break;
       // caa0334 end
     }
			case ECM_PROPERTYCANASSIGN:  	/* Object: Is the property assignable (ie readonly?) */
			case ECM_SETPROPERTY: 				/* Object: Assignment to a property */
			case ECM_GETPROPERTY:					/* Object: Retrieve value from property */
			{
				void* object = (void*)ECOfindNVObject( eci->mOmnisInstance, lParam );
				if ( object )
				{ // Object found now figure out which object type it is
          // caa0334 start
					if ( eci->mCompId >= cObject_FIRST  && eci->mCompId <= cObject_LAST )
					{
						OmnisObjectContainer* c = (OmnisObjectContainer*)object;
						if ( c->mObject )
            {
              switch (eci->mCompId)
              {
                case cObject_FileOps: {
                  return static_cast<tqfFileOpsObj*>(c->mObject)->propertySupport(Msg, eci);
                }
                case cObject_FileOpsCopyWorker:
                case cObject_FileOpsMoveWorker:
                case cObject_FileOpsDeleteWorker: {
                  return static_cast<FileOpsWorker*>(c->mObject)->propertySupport(Msg, eci);
                }
              }
            }
					}
          // caa0334 end
				}
				return 0L;
			}
			case ECM_METHODCALL:
			{
				void* object = (void*)ECOfindNVObject( eci->mOmnisInstance, lParam );
				if ( object )
				{ // Object found now figure out which object type it is
          // caa0334 start
					if ( eci->mCompId >= cObject_FIRST  && eci->mCompId <= cObject_LAST )
					{
						OmnisObjectContainer* c = (OmnisObjectContainer*)object;
						if ( c->mObject )
            {
              switch (eci->mCompId)
              {
                case cObject_FileOps: {
                  return static_cast<tqfFileOpsObj*>(c->mObject)->methodCall(eci);
                }
                case cObject_FileOpsCopyWorker:
                case cObject_FileOpsMoveWorker:
                case cObject_FileOpsDeleteWorker: {
                  return static_cast<FileOpsWorker*>(c->mObject)->methodCall(eci);
                }
              }
            }
					}
          // caa0334 end
				}
				else
					return tqfFileOpsObj::staticMethod(eci);
				return qfalse;
			}
			// MHn0407 begins
			case ECM_SQLOBJECT_COPY: 
			{
				// Note: we use the EXT_FLAG_SESSION flag for this component (makes multithreading better), so we need to implement this method
				objCopyInfo *copyInfo = (objCopyInfo *) lParam;
				if (wParam)
				{ // Remove
					ECOremoveNVObject(eci->mOmnisInstance, copyInfo->mSourceObject);
				}
				else
				{ // Add
					void *srcobj = (void *) ECOfindNVObject(eci->mOmnisInstance, copyInfo->mDestinationObject);
					if (srcobj)
						ECOinsertNVObject(eci->mOmnisInstance, copyInfo->mSourceObject, (void *) srcobj);
				}
				break;
			}				
			// MHn0407 ends
	 		case ECM_CONSTPREFIX:
			{
				EXTfldval exfldval;
				ECOaddParam(eci,&exfldval);
				str80 conPrefix; RESloadString(gInstLib,RES_CONST_PREFIX,conPrefix);
				exfldval.setChar(conPrefix);
				return qtrue;
			}
			case ECM_GETCONSTNAME:
			{
				return ECOreturnConstants( gInstLib, eci, RES_CONST_START, RES_CONST_END );
			}
			case ECM_GETVERSION:
			{
				return ECOreturnVersion(VERSION_MAJOR,VERSION_MINOR);
			}                                                                 
	 }

	 // As a final result this must ALWAYS be called. It handles all other messages that this component
	 // decides to ignore.
	 return WNDdefWindowProc(hwnd,Msg,wParam,lParam,eci);
}


qbool tqfFileOpsObj::matchSearchStr(str255 pSource, str255 &pSearch, FILEfilterMap &pFilterMap) // rmm9025 // rmm11107
{
	static str15 showAllFilter(3,QTEXT("*.*"));
	if ( showAllFilter == pSearch )	
		return qtrue;
	// Start rmm11107: Note - should not get any error text
	str255 errorText;
	return pFilterMap.match(pSource, pSearch, errorText);	
	// End rmm11107
}

#ifdef ismac
	extern void gGetTypeOrCreatorFromString(qchar *pSource, qshort pSourceLen, OSType &pDest); // rmm5941
#endif

qbool	tqfFileOpsObj::canAddFile(str255& pFileName, strxxx* pFilter, void* pExtraInfo, FILEfilterMap *pFilterMap) // rmm11107
{
  if (!pFilter) return qtrue;
  str255 filter = *pFilter;
  if (!filter.length()) return qtrue;
  
  qbool canAdd = qfalse;
  str255 ext;
  
  while ( !!filter )
  {
    qshort p = filter.pos(';');
    if ( p==0 ) p = qshort(filter[0]+1);
    ext.copy( filter, 1, p-1 );
    filter.deleet( 1, p );
    str255 upperExt; upperExt = ext; upperExt.upps();		// rmm3994: must not upper case creator and type on Mac

    qbool isExclude = upperExt[1] == '!' && upperExt[2] == '!';
    if (isExclude)
      upperExt.copy(upperExt, 3, upperExt[1]); // Remove !! character that indicate an exclude filter
    
    if (matchSearchStr(pFileName, upperExt, *pFilterMap) != 0) {
      if (isExclude)
        return qfalse; // No point testing for other filters if we should exclude this file
      canAdd = qtrue;
    }

#ifdef ismac
    if (canAdd || ext[1] != '\'')
      continue;
    
    // Second part: check for macOS old HSF creator and type codes. Perhaps we can move this into a separate function in the future?

    OSType filterCreator, filterType;
    qbool hasCreatorCode = qfalse, hasTypeCode = qfalse;	// rmm3994
    
    // Get the file information
    #ifdef isCOCOA  // caa2554 implemented CR0205 here
      // Check newer style creator and type codes: I believe the new types are stored in a com.apple.FinderInfo as extended attributes
      NSURL* url = (__bridge NSURL*)pExtraInfo;
      ResType fileType = 0, fileCreator = 0;
      if(url)
      {
        NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[[url filePathURL] path] error:NULL];
        if(attributes)
        {
          NSNumber* tyCode = [attributes objectForKey:NSFileHFSTypeCode];
          NSNumber* creatorCode = [attributes objectForKey:NSFileHFSCreatorCode];
          if(tyCode)
            fileType = [tyCode intValue];
          if(creatorCode)
            fileCreator = [creatorCode intValue];
        }
      }
    
      // caa2554 start
      // Just in case, check even older style which I think might be in a ResourceFork?
      FInfo *finderInfo = (FInfo *) &((FSCatalogInfo *) pExtraInfo)->finderInfo[0];
      if (fileType == 0)
        fileType = finderInfo->fdType;
      if (fileCreator == 0)
        fileCreator = finderInfo->fdCreator;
      // caa2554 end
    #elif defined(ismacosx) /* isCOCOA */
      // rmmuni_osx6:
      FInfo *finderInfo = (FInfo *) &((FSCatalogInfo *) pExtraInfo)->finderInfo[0];
      ResType fileType = finderInfo->fdType;
      ResType fileCreator = finderInfo->fdCreator;
    #else
      ResType fileType = ((CInfoPBRec*)pExtraInfo)->hFileInfo.ioFlFndrInfo.fdType;
      ResType fileCreator = ((CInfoPBRec*)pExtraInfo)->hFileInfo.ioFlFndrInfo.fdCreator;
    #endif /* ismacosx */
    // Extract filter information
    ext.deleet( 1, 1 );
    // Start rmm3994: on the Mac the filter has the format 'type' (including quotes) or 'type,creator' or ',creator' (including single quotes)
    // where both creator and type MUST be 4 character strings.
    if (ext[0] && ext[ext[0]] == '\'')
    {
      ext[0] = ext[0] - 1;	// String now has no quotes
      if (ext[0] == 4)
      {
        hasTypeCode = qtrue;
        gGetTypeOrCreatorFromString(&ext[1], 4, filterType);	// rmmuni_osx6
      }
      else if (ext[0] == 5 && ext[1] == ',')
      {
        hasCreatorCode = qtrue;
        gGetTypeOrCreatorFromString(&ext[2], 4, filterCreator);	// rmmuni_osx6
      }
      else if (ext[0] == 9 && ext[5] == ',')
      {
        hasCreatorCode = hasTypeCode = qtrue;
        gGetTypeOrCreatorFromString(&ext[1], 4, filterType);			// rmmuni_osx6
        gGetTypeOrCreatorFromString(&ext[6], 4, filterCreator);	// rmmuni_osx6
      }
    }
    // End rmm3994
    
    // Finally, compare!
    if ( hasCreatorCode && hasTypeCode )
      canAdd = ( fileCreator == filterCreator) && ( fileType == filterType);
    else if ( hasCreatorCode )
      canAdd = ( fileCreator == filterCreator);
    else if ( hasTypeCode )
      canAdd = ( fileType == filterType);
    
#endif /* ismac */
  }
	return canAdd;
}

void tqfFileOpsObj::defineList ( EXTqlist *pList, qlong pRetInfo )
{
	qshort col=1; str255 colName;
	for ( qlong loop=1; loop <= cInfoCalcDirectorySize ; loop<<=1, col++ ) // MHn0032 Changed cInfoTypeCode to cInfoActualResSize // MHn0483 // rmmvs8: qlong rather than qint
	{
		if ( pRetInfo & loop )
		{
			ffttype fft; qshort	fdp= 0; qlong	fln = 0;
			switch ( loop )
			{
				case cInfoName:
				case cInfoName83:
				case cInfoFullName:
				case cInfoCreatorCode:
				case cInfoResolvedFileName:  // AE6723
				case cInfoTypeCode:
				{
					fft = fftCharacter; fln = 256; 
					break;
				}
				case cInfoReadOnly:			
				case cInfoHidden:	
				case cInfoDirectory: // MHn0483		
				{
					fft = fftBoolean;
					break;
				}
				case cInfoSize:
				case cInfoActualSize:
				case cInfoResSize: // MHn0032
				case cInfoActualResSize: // MHn0032
				case cInfoCalcDirectorySize: // caa2019
				{
					fft = fftInteger;
					fdp = dpF64bitinteger; // rmm9010
					break;
				}
				case cInfoCreated:
				case cInfoModified:
				{
					fft = fftDate;
					fdp = dpFdtime2000;
					fln = sizeof(datestamptype); 
					break;
				}
			}
			RESloadString( gInstLib, 20499+col,colName ); 
			pList->addCol( col, fft, fdp, fln, NULL, &colName);
		}
	}
}

// rmm9025: Stub for openResources (no longer supported)
qret tqfFileOpsObj::openResources(EXTfldval& pName, qbool pReadOnly, qbool pExclusive)
{
	str255 name;
	pName.getChar(name);
	return EXTfile::openResources(name, pReadOnly, pExclusive);
}

// Start rmm10372
OpsErr tqfFileOpsObj::selectFilesInSystemViewer(EXTParamInfo* pPath, EXTParamInfo* pFileList)
{
	// If pFileList is null, pPath must be the pathname of a file to select in the system viewer
	
	// If pFileList is not null, pPath must be the pathname of a folder to open in the system viewer, with
	// each file in pFileList selected
	EXTfldval path((qfldval)pPath->mData);
	if (!EXTfile::exists(path, pFileList != 0))
		return kFileOpsFileNotFound;

	EXTfldval fileList((qfldval)(pFileList ? pFileList->mData : 0));
	if (pFileList)
	{
		EXTqlist *fl = fileList.getList(qfalse);
		qlong fileCount = fl->rowCnt();
		delete fl;
		if (!fileCount)
			return kFileOpsNoPath;
	}
	else
	{
		// Get the file name from the path, and set the fileList to a single line list containing the file name
		// Then modify the path to be the folder path
		qlong sepPos = path.lastPos(EXTfile::pathSeparator());
		if (sepPos <= 1)
			return kFileOpsNoPath;
		qHandle pathHan = path.getHandle(qfalse);
		qHandleTextPtr hp(pathHan, 0);
		qlong pathLen = hp.charLen();
		if (sepPos == pathLen)
			return kFileOpsNoPath;
		qchar* pathPtr = *hp;

		EXTqlist fl(listVlen);
		fl.addCol(fftCharacter, dpDefault, maxcharlen);
		fl.insertRow();
		EXTfldval fval;
		fl.getColValRef(1, 1, fval, qtrue);
		fval.setChar(&pathPtr[sepPos], pathLen - sepPos);

		fileList.setList(&fl, qtrue);

		// Modify the path
		path.reduceCharLen(sepPos - 1);
	}
	return xSelectFilesInSystemViewer(path, fileList);
}
// End rmm10372
// Start rmm11014
OpsErr tqfFileOpsObj::staticReadFile(EXTCompInfo* pEci, qshort pParamCount, EXTParamInfo* pPath, EXTParamInfo* pData, EXTParamInfo* pEncoding)
{
	OpsErr err;
	do
	{
		if (pParamCount < 2)
		{
			err = kFileOpsParamError;
			break;
		}

		// Get path
		EXTfldval path(qfldval(pPath->mData));
		// Variable to receive data
		EXTparamTypeInfo info;
		if (!ECOgetParamInfo(pData, info) || (info.mType != fftBinary && info.mType != fftCharacter))
		{
			err = kFileOpsVariableMustBeBinaryOrCharacter;
			break;
		}

		// Encoding
		preconst encoding = preUniTypeAuto;
		if (fftCharacter == info.mType && pParamCount >= 3)
		{
			EXTfldval encodingFval(qfldval(pEncoding->mData));
			encoding = (preconst)encodingFval.getLong();
			if (encoding < preUniTypeAuto || encoding >= preUniTypeBinary)
			{
				err = kFileOpsInvalidEncoding;
				break;
			}
		}

		// caa2579 start
		qlong64 fileSize = getFileSize(path);
		if (fileSize > maxbinlen) {
			err = kFileOpsFileTooLarge;
			break;
		}
		// caa2579 end

		// Read the data, converting to character if required
		EXTfldval data(qfldval(pData->mData));
		if (fftBinary == info.mType)
			err = FILEreadFile(path, data);
		else
		{
			EXTfldval binFval;
			err = FILEreadFile(path, binFval);
			if (kFileOpsOK == err)
			{
				qHandle han = binFval.getHandle(qfalse);
				qHandlePtr hp(han, 0);
				CHRconvFromUnicodeEncoding cfu(encoding, *hp, hp.dataLen());
				data.setChar(cfu.charDataPtr(), cfu.charLen());
			}
		}

		if (kFileOpsOK == err)
			ECOsetParameterChanged(pEci, 2);
	} while (0);

	return err;
}

OpsErr tqfFileOpsObj::staticWriteFile(EXTCompInfo* pEci, qshort pParamCount, EXTParamInfo* pPath, EXTParamInfo* pData, EXTParamInfo* pEncoding, EXTParamInfo *pBOM, EXTParamInfo* pReplace, EXTParamInfo* pCreateMissingDirectories) // caa2631
{
	OpsErr err;
	do
	{
		if (pParamCount < 2)
		{
			err = kFileOpsParamError;
			break;
		}
		// Get path
		EXTfldval path(qfldval(pPath->mData));
		// Variable containing data
		EXTparamTypeInfo info;
		if (!ECOgetParamInfo(pData, info) || (info.mType != fftBinary && info.mType != fftCharacter)) 
		{
			err = kFileOpsVariableMustBeBinaryOrCharacter;
			break;
		}
		EXTfldval data(qfldval(pData->mData));
		// Encoding
		preconst encoding = preUniTypeUTF8;
		if (fftCharacter == info.mType && pParamCount >= 3 && pEncoding)
		{
			EXTfldval encodingFval(qfldval(pEncoding->mData));
      if (!encodingFval.isNull()) { // caa2632
        encoding = (preconst)encodingFval.getLong();
        if (encoding <= preUniTypeAuto || encoding >= preUniTypeBinary)
        {
          err = kFileOpsInvalidEncoding;
          break;
        }
      }
		}
		// BOMparameter
		qbool BOM = qtrue;
		if (fftCharacter == info.mType && pParamCount >= 4 && pBOM)
    {
      EXTfldval bomFval(qfldval(pBOM->mData));
      if (!bomFval.isNull()) { // caa2632
        BOM = (bomFval.getLong() != 0);
      }
		}

		// Replace parameter
		qbool replace = qtrue;
		if (pParamCount >= 5 && pReplace)
		{
			EXTfldval replaceFval(qfldval(pReplace->mData));
      if (!replaceFval.isNull()){ // caa2632
        replace = (replaceFval.getLong() != 0);
      }
			if (!replace && EXTfile::exists(path))
			{
				err = kFileOpsAlreadyExists;
				break;
			}
		}
    
    // caa2631 start
    // Create missing directories parameter
    if (pParamCount >= 6 && pCreateMissingDirectories) {
      EXTfldval createMissingDirectories(qfldval(pCreateMissingDirectories->mData));
      if (createMissingDirectories.getLong()) {
        err = tqfFileOpsObj::createMissingDirectories(path);
        if (kFileOpsOK != err)
          break;
      }
    }
    // caa2631 end
    
		// Write the data, converting to the desired encoding if required
		qHandle han = data.getHandle(qfalse);
		qHandlePtr hp(han, 0);
		if (fftBinary == info.mType)
		{
			err = FILEwriteFile(path, *hp, hp.dataLen());
		}
		else
		{
			CHRconvToUnicodeEncoding ctu(encoding, *hp, hp.dataLen(), BOM);
			err = FILEwriteFile(path, ctu.dataPtr(), ctu.len());
		}
	} while (0);

	return err;
}
// End rmm11014

// caa2630 start
OpsErr tqfFileOpsObj::deletePathIfExists(EXTfldval &pPath)
{
  qret ret = kFileOpsOK;
  qbool exists = qfalse;
  doesFileExist(pPath, exists);
  if (exists)
    ret = deleteFile(pPath, qtrue, qtrue);
  
  return ret;
}

/**
 * Returns the filename portion of a given path.
 * If the path is a folder (ends in path separator), returns empty.
 */ // jmg1531
void	tqfFileOpsObj::filename(EXTfldval& pPath, EXTfldval& pOut)
{
	std::string pathstr;
	pPath.getchar(pathstr);
	std::filesystem::path p(pathstr);
	const std::string filename = p.filename().string();
	pOut.setchar(filename);
}


OpsErr tqfFileOpsObj::copyOrMove(EXTfldval& pFrom, EXTfldval& pTo, qbool pCreatePath, qbool pReplace, qbool pMerge, qshort pMethodId)
{
  if ((cSMethodId_Copy != pMethodId) && (cSMethodId_Move != pMethodId))
    return kFileOpsParamError;

  if (!doesFileExist(pFrom))
    // 'from' file/folder does not exist
    return kFileOpsFileNotFound;

  if (!doesFileExist(pTo)) {
    // 'to' file/folder does not exist
    if (cSMethodId_Move == pMethodId) {
      qbool toIsFolder = pTo.lastPos(EXTfile::pathSeparator()) == pTo.getCharLen();
      if (toIsFolder && !pCreatePath)
        return kFileOpsDirectoryNotFound;
    }
  }
  
  OpsErr err = kFileOpsOK;

  if (pCreatePath) {
    err = createMissingDirectories(pTo);
    if (kFileOpsOK != err)
      return err;
  }

	qbool fromIsFile = !isDirectory(pFrom);

	// Start jmg1531
	EXTfldval destPath;
	destPath = pTo;

	// If from file, to dir then append filename to dest:
	if (fromIsFile && isDirectory(destPath)) {
		EXTfldval fname; filename(pFrom, fname);

		// Add path separator if needed:
		if (destPath.lastPos(EXTfile::pathSeparator()) != destPath.getCharLen()) {
			destPath.concat(EXTfile::pathSeparator());
		}

		destPath.concat(fname);
	}


  if (doesFileExist(destPath)) // caa2641
  {
		if (pMerge || pReplace)
		{
			if (fromIsFile && !isDirectory(destPath))
			{
				// file to file
				if (pReplace)
					err = deletePathIfExists(destPath); // replace is dominant over merge
				else if (pMerge) {

					err = appendFileContentsFrom(pFrom, destPath);

					if (kFileOpsOK != err)
						return err;

					if (cSMethodId_Move == pMethodId)
						err = deleteFile(pFrom);

					return err;
				}

			}
			else if (!fromIsFile && isDirectory(destPath))
			{
				// folder to folder
				if (pMerge)
					return mergeFolders(pFrom, destPath, pReplace);
				else if (pReplace)
					err = deleteFile(destPath, qtrue, qtrue); // jmg1527
			}
		}
		else // jmg1531
			err = kFileOpsAlreadyExists; // jmg1531

    if (kFileOpsOK != err)
      return err;
  }

  if (cSMethodId_Copy == pMethodId)
    return copyFile(pFrom, destPath);
  else
    return moveFile(pFrom, destPath);
	// End jmg1531
}

void tqfFileOpsObj::generateUniqueFolderName(EXTfldval& pRtnVal)
{
	auto now = std::chrono::system_clock::now();
	auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();

	// Random number generator
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<int> dist(1000, 9999);

	std::ostringstream oss;
	oss << "temp_folder_" << now_ms << "_" << dist(gen);
	pRtnVal.setchar(oss.str());
}

OpsErr tqfFileOpsObj::createMissingDirectories(EXTfldval &pPath)
{
  OpsErr ret = kFileOpsOK;
  str255 drive, fileName, extension;
  EXTfldval dir;
  ret = splitPath(pPath, drive, dir, fileName, extension);
  if (kFileOpsOK != ret)
    return ret;

  // caa2641 start
  #if defined(iswin32) && !defined(isunix)
    EXTfldval parentDir;
    parentDir.concat(drive);
    parentDir.concat(dir);
    if (!doesFileExist(parentDir))
      ret = createDir(parentDir, qtrue);
  #else
    if (!doesFileExist(dir))
      ret = createDir(dir, qtrue);
  #endif
   // caa2641 end

  return ret;
}

OpsErr tqfFileOpsObj::appendFileContentsFrom(EXTfldval &pFrom, EXTfldval &pTo)
{
  OpsErr err = kFileOpsOK;
  
  auto destFile = FILEcreateInst();
  err = FILEopen(destFile, pTo, qfalse, qtrue);
  
  if (kFileOpsOK != err)
    return err;
  
  EXTfldval dataFrom;
  err = FILEreadFile(pFrom, dataFrom);
  
  if (kFileOpsOK != err)
    return err;
  
  qHandle han = dataFrom.getHandle(qfalse);
  qHandlePtr hp(han, 0);
  err = FILEwrite(destFile, *hp, FILEgetLength(destFile), hp.dataLen());
  
  FILEclose(destFile);
  
  return err;
}

OpsErr tqfFileOpsObj::mergeFolders(EXTfldval &pFrom, EXTfldval &pTo, qbool pReplace)
{
    std::string fromStr, toStr;
    pFrom.getchar(fromStr);
    pTo.getchar(toStr);

    std::filesystem::path fromPath(fromStr);
    std::filesystem::path toPath(toStr);

    if (!std::filesystem::exists(fromPath) || !std::filesystem::is_directory(fromPath))
        return kFileOpsFileNotFound;

    if (!std::filesystem::exists(toPath)) {
        std::filesystem::create_directories(toPath);
    }

    try {
        auto options = std::filesystem::copy_options::recursive;
        if (pReplace) {
            options |= std::filesystem::copy_options::overwrite_existing;
        } else {
            options |= std::filesystem::copy_options::skip_existing;
        }

        std::filesystem::copy(fromPath, toPath, options);
    }
    catch (const std::filesystem::filesystem_error &e) {
        return e.code().value();
    }

    return kFileOpsOK;
}

// caa2630 end
// End of file
