<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_FileSystem" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
REM ===			The ScriptForge library and its associated libraries are part of the LibreOffice project.				===
REM ===					Full documentation is available on https://help.libreoffice.org/								===
REM =======================================================================================================================

Option Compatible
Option Explicit

&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
&apos;&apos;&apos;	SF_FileSystem
&apos;&apos;&apos;	=============
&apos;&apos;&apos;		Class implementing the file system service
&apos;&apos;&apos;		for common file and folder handling routines
&apos;&apos;&apos;		Including copy and move of files and folders, with or without wildcards
&apos;&apos;&apos;		The design choices are largely inspired by
&apos;&apos;&apos;			https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/filesystemobject-object
&apos;&apos;&apos;			The File and Folder classes have been found redundant with the current class and have not been implemented
&apos;&apos;&apos;		The implementation is mainly based on the XSimpleFileAccess UNO interface
&apos;&apos;&apos;			https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1ucb_1_1XSimpleFileAccess.html
&apos;&apos;&apos;
&apos;&apos;&apos;		Subclasses:
&apos;&apos;&apos;			SF_TextStream
&apos;&apos;&apos;
&apos;&apos;&apos;		Definitions:
&apos;&apos;&apos;			File and folder names may be expressed either in the (preferable because portable) URL form
&apos;&apos;&apos;			or in the more usual operating system notation (e.g. C:\... for Windows)
&apos;&apos;&apos;			The notation, both for arguments and for returned values
&apos;&apos;&apos;			is determined by the FileNaming property: either &quot;ANY&quot; (default), &quot;URL&quot; or &quot;SYS&quot;
&apos;&apos;&apos;
&apos;&apos;&apos;				FileName: the full name of the file including the path without any ending path separator
&apos;&apos;&apos;				FolderName: the full name of the folder including the path and the ending path separator
&apos;&apos;&apos;				Name: the last component of the File- or FolderName including its extension
&apos;&apos;&apos;				BaseName: the last component of the File- or FolderName without its extension
&apos;&apos;&apos;				NamePattern: any of the above names containing wildcards in its last component
&apos;&apos;&apos;					Admitted wildcards are:	the &quot;?&quot; represents any single character
&apos;&apos;&apos;											the &quot;*&quot; represents zero, one, or multiple characters
&apos;&apos;&apos;
&apos;&apos;&apos;		Disk file systems and document&apos;s internal file systems
&apos;&apos;&apos;			All the implemented properties and methods are applicable on usual disk file systems.
&apos;&apos;&apos;				Root is usually something like &quot;C:\&quot; or &quot;/&quot; or their URL equivalents
&apos;&apos;&apos;			Now, Libreoffice documents have an internal file system as well. Many of the proposed methods
&apos;&apos;&apos;			support document&apos;s file systems too, however, for some of them, with restrictions.
&apos;&apos;&apos;			Read the comments in the individual methods below.
&apos;&apos;&apos;			It makes browsing folders and files, adding, replacing files possible. Updates will be
&apos;&apos;&apos;			saved with the document.
&apos;&apos;&apos;			VERY POWERFUL but KNOW WHAT YOU&apos;RE DOING !!
&apos;&apos;&apos;				The root of a document&apos;s file system is obtained from the &quot;FileSystem&quot; property of a document instance, like in:
&apos;&apos;&apos;					Dim root As String, doc As Object, ui As Object
&apos;&apos;&apos;					Set ui = CreateScriptService(&quot;ui&quot;)
&apos;&apos;&apos;					Set doc = ui.GetDocument(ThisComponent)
&apos;&apos;&apos;					root = doc.FileSystem
&apos;&apos;&apos;				The file manifest.xml is managed automatically.
&apos;&apos;&apos;				The FileNaming setting is ignored.
&apos;&apos;&apos;
&apos;&apos;&apos;		Service invocation example:
&apos;&apos;&apos;			Dim FSO As Variant
&apos;&apos;&apos;			Set FSO = CreateScriptService(&quot;FileSystem&quot;)
&apos;&apos;&apos;
&apos;&apos;&apos;		Detailed user documentation:
&apos;&apos;&apos;			https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_filesystem.html?DbPAR=BASIC
&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;

REM ================================================================== EXCEPTIONS

Const UNKNOWNFILEERROR			=	&quot;UNKNOWNFILEERROR&quot;		&apos;	Source file does not exist
Const UNKNOWNFOLDERERROR		=	&quot;UNKNOWNFOLDERERROR&quot;	&apos;	Source folder or Destination folder does not exist
Const NOTAFILEERROR				=	&quot;NOTAFILEERROR&quot;			&apos;	Destination is a folder, not a file
Const NOTAFOLDERERROR			=	&quot;NOTAFOLDERERROR&quot;		&apos;	Destination is a file, not a folder
Const OVERWRITEERROR			=	&quot;OVERWRITEERROR&quot;		&apos;	Destination can not be overwritten
Const READONLYERROR				=	&quot;READONLYERROR&quot;			&apos;	Destination has its read-only attribute set
Const NOFILEMATCHERROR			=	&quot;NOFILEMATCHFOUND&quot;		&apos;	No file matches Source containing wildcards
Const FOLDERCREATIONERROR		=	&quot;FOLDERCREATIONERROR&quot;	&apos;	FolderName is an existing folder or file
Const FILESYSTEMERROR			=	&quot;FILESYSTEMERROR&quot;		&apos;	The method is not applicable on document&apos;s file systems

REM ============================================================ MODULE CONSTANTS

&apos;&apos;&apos;	TextStream open modes
Const cstForReading				=	1
Const cstForWriting				=	2
Const cstForAppending			=	8

&apos;&apos;&apos;	Document file system
Const DOCFILESYSTEM				=	&quot;vnd.sun.star.tdoc:/&quot;

&apos;&apos;&apos;	Folders and files scanning
Const cstSEPARATOR				=	&quot;//;&quot;	&apos;	Separates folders or files in the accumulators
Const cstFILES					=	1		&apos;	Caler = Files()
Const cstFOLDERS				=	2		&apos;	Caller = SubFolders()

REM ===================================================== CONSTRUCTOR/DESTRUCTOR

REM -----------------------------------------------------------------------------
Public Function Dispose() As Variant
	Set Dispose = Nothing
End Function	&apos;	ScriptForge.SF_FileSystem Explicit destructor

REM ================================================================== PROPERTIES

REM -----------------------------------------------------------------------------
Property Get ConfigFolder() As String
&apos;&apos;&apos;	Return the configuration folder of LibreOffice

Const cstThisSub = &quot;FileSystem.getConfigFolder&quot;

	SF_Utils._EnterFunction(cstThisSub)
	ConfigFolder = SF_FileSystem._GetConfigFolder(&quot;user&quot;)
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.ConfigFolder

REM -----------------------------------------------------------------------------
Property Get ExtensionsFolder() As String
&apos;&apos;&apos;	Return the folder containing the extensions installed for the current user

Dim oMacro As Object		&apos;	/singletons/com.sun.star.util.theMacroExpander
Const cstThisSub = &quot;FileSystem.getExtensionsFolder&quot;

	SF_Utils._EnterFunction(cstThisSub)
	Set oMacro = SF_Utils._GetUNOService(&quot;MacroExpander&quot;)
	ExtensionsFolder = SF_FileSystem._ConvertFromUrl(oMacro.ExpandMacros(&quot;$UNO_USER_PACKAGES_CACHE&quot;) &amp; &quot;/&quot;)
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.ExtensionsFolder

REM -----------------------------------------------------------------------------
Property Get FileNaming() As Variant
&apos;&apos;&apos;	Return the current files and folder notation, either &quot;ANY&quot;, &quot;URL&quot; or &quot;SYS&quot;
&apos;&apos;&apos;		&quot;ANY&quot;: methods receive either URL or native file names, but always return URL file names
&apos;&apos;&apos;		&quot;URL&quot;: methods expect URL arguments and return URL strings (when relevant)
&apos;&apos;&apos;		&quot;SYS&quot;: idem but operating system notation

Const cstThisSub = &quot;FileSystem.getFileNaming&quot;
	SF_Utils._EnterFunction(cstThisSub)
	FileNaming = _SF_.FileSystemNaming
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.FileNaming (get)

REM -----------------------------------------------------------------------------
Property Let FileNaming(ByVal pvNotation As Variant)
&apos;&apos;&apos;	Set the files and folders notation: &quot;ANY&quot;, &quot;URL&quot; or &quot;SYS&quot;

Const cstThisSub = &quot;FileSystem.setFileNaming&quot;
	SF_Utils._EnterFunction(cstThisSub)
	If VarType(pvNotation) = V_STRING Then
		Select Case UCase(pvNotation)
			Case &quot;ANY&quot;, &quot;URL&quot;, &quot;SYS&quot;	:	_SF_.FileSystemNaming = UCase(pvNotation)
			Case Else	&apos;	Unchanged
		End Select
	End If
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.FileNaming (let)

REM -----------------------------------------------------------------------------
Property Get ForAppending As Integer
&apos;&apos;&apos;	Convenient constant (see documentation)
	ForAppending = cstForAppending
End Property	&apos;	ScriptForge.SF_FileSystem.ForAppending

REM -----------------------------------------------------------------------------
Property Get ForReading As Integer
&apos;&apos;&apos;	Convenient constant (see documentation)
	ForReading = cstForReading
End Property	&apos;	ScriptForge.SF_FileSystem.ForReading

REM -----------------------------------------------------------------------------
Property Get ForWriting As Integer
&apos;&apos;&apos;	Convenient constant (see documentation)
	ForWriting = cstForWriting
End Property	&apos;	ScriptForge.SF_FileSystem.ForWriting

REM -----------------------------------------------------------------------------
Property Get HomeFolder() As String
&apos;&apos;&apos;	Return the user home folder

Const cstThisSub = &quot;FileSystem.getHomeFolder&quot;

	SF_Utils._EnterFunction(cstThisSub)
	HomeFolder = SF_FileSystem._GetConfigFolder(&quot;home&quot;)
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.HomeFolder

REM -----------------------------------------------------------------------------
Property Get InstallFolder() As String
&apos;&apos;&apos;	Return the installation folder of LibreOffice

Const cstThisSub = &quot;FileSystem.getInstallFolder&quot;

	SF_Utils._EnterFunction(cstThisSub)
	InstallFolder = SF_FileSystem._GetConfigFolder(&quot;inst&quot;)
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.InstallFolder

REM -----------------------------------------------------------------------------
Property Get ObjectType As String
&apos;&apos;&apos;	Only to enable object representation
	ObjectType = &quot;SF_FileSystem&quot;
End Property	&apos;	ScriptForge.SF_FileSystem.ObjectType

REM -----------------------------------------------------------------------------
Property Get ServiceName As String
&apos;&apos;&apos;	Internal use
	ServiceName = &quot;ScriptForge.FileSystem&quot;
End Property	&apos;	ScriptForge.SF_FileSystem.ServiceName

REM -----------------------------------------------------------------------------
Property Get TemplatesFolder() As String
&apos;&apos;&apos;	Return the folder defined in the LibreOffice paths options as intended for templates files

Dim sPath As String			&apos;	Template property of com.sun.star.util.PathSettings
Const cstThisSub = &quot;FileSystem.getTemplatesFolder&quot;

	SF_Utils._EnterFunction(cstThisSub)
	sPath = SF_Utils._GetUNOService(&quot;PathSettings&quot;).Template
	TemplatesFolder = SF_FileSystem._ConvertFromUrl(Split(sPath, &quot;;&quot;)(0) &amp; &quot;/&quot;)
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.TemplatesFolder

REM -----------------------------------------------------------------------------
Property Get TemporaryFolder() As String
&apos;&apos;&apos;	Return the folder defined in the LibreOffice paths options as intended for temporary files

Const cstThisSub = &quot;FileSystem.getTemporaryFolder&quot;

	SF_Utils._EnterFunction(cstThisSub)
	TemporaryFolder = SF_FileSystem._GetConfigFolder(&quot;temp&quot;)
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.TemporaryFolder

REM -----------------------------------------------------------------------------
Property Get UserTemplatesFolder() As String
&apos;&apos;&apos;	Return the folder defined in the LibreOffice paths options as intended for User templates files

Dim sPath As String			&apos;	Template_writable property of com.sun.star.util.PathSettings
Const cstThisSub = &quot;FileSystem.getUserTemplatesFolder&quot;

	SF_Utils._EnterFunction(cstThisSub)
	sPath = SF_Utils._GetUNOService(&quot;PathSettings&quot;).Template_writable
	UserTemplatesFolder = SF_FileSystem._ConvertFromUrl(sPath &amp; &quot;/&quot;)
	SF_Utils._ExitFunction(cstThisSub)

End Property	&apos;	ScriptForge.SF_FileSystem.UserTemplatesFolder

REM ===================================================================== METHODS

REM -----------------------------------------------------------------------------
Public Function BuildPath(Optional ByVal FolderName As Variant _
							, Optional ByVal Name As Variant _
							) As String
&apos;&apos;&apos;	Combines a folder path and the name of a file and returns the combination with a valid path separator
&apos;&apos;&apos;	Inserts an additional path separator between the foldername and the name, only if necessary
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FolderName: Path with which Name is combined. Path need not specify an existing folder
&apos;&apos;&apos;		Name: To be appended to the existing path.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The path concatenated with the file name after insertion of a path separator, if necessary
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As String
&apos;&apos;&apos;		    FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.BuildPath(&quot;C:\Windows&quot;, &quot;Notepad.exe&quot;) returns C:\Windows\Notepad.exe

Dim sBuild As String			&apos;	Return value
Dim sFile As String				&apos;	Alias for Name
Const cstFileProtocol = &quot;file:///&quot;
Const cstThisSub = &quot;FileSystem.BuildPath&quot;
Const cstSubArgs = &quot;FolderName, Name&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sBuild = &quot;&quot;

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FolderName, &quot;FolderName&quot;) Then GoTo Finally
		If Not SF_Utils._Validate(Name, &quot;Name&quot;, V_STRING) Then GoTo Finally
	End If
	FolderName = SF_FileSystem._ConvertToUrl(FolderName)

Try:
	&apos;	Add separator if necessary. FolderName is now in URL notation
	If Len(FolderName) &gt; 0 Then
		If Right(FolderName, 1) &lt;&gt; &quot;/&quot; Then sBuild = FolderName &amp; &quot;/&quot; Else sBuild = FolderName
	Else
		sBuild = cstFileProtocol
	End If
	&apos;	Encode the file name
	sFile = ConvertToUrl(Name)
	&apos;	Some file names produce http://file.name.suffix/
	If Left(sFile, 7) = &quot;http://&quot; Then sFile = cstFileProtocol &amp; Mid(sFile, 8, Len(sFile) - 8)
	&apos;	Combine both parts
	If Left(sFile, Len(cstFileProtocol)) = cstFileProtocol Then sBuild = sBuild &amp; Mid(sFile, Len(cstFileProtocol) + 1) Else sBuild = sBuild &amp; sFile
 
Finally:
	BuildPath = SF_FileSystem._ConvertFromUrl(sBuild)
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.BuildPath

REM -----------------------------------------------------------------------------
Public Function CompareFiles(Optional ByVal FileName1 As Variant _
								, Optional ByVal FileName2 As Variant _
								, Optional ByVal CompareContents As Variant _
								)
&apos;&apos;&apos;	Compare 2 files and return True if they seem identical
&apos;&apos;&apos;	The comparison may be based on the file attributes, like modification time,
&apos;&apos;&apos;	or on their contents.
&apos;&apos;&apos;	The method is not supported for document&apos;s internal file systems.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName1: The 1st file to compare
&apos;&apos;&apos;		FileName2: The 2nd file to compare
&apos;&apos;&apos;		CompareContents: When True, the contents of the files are compared. Default = False
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True when the files seem identical
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR		One of the files does not exist
&apos;&apos;&apos;		FILESYSTEMERROR			The method is not applicable on document&apos;s file systems
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		MsgBox FSO.CompareFiles(&quot;C:\myFile1.txt&quot;, &quot;C:\myFile2.txt&quot;, CompareContents := True)

Dim bCompare As Boolean			&apos;	Return value
Dim sFile As String				&apos;	Alias of FileName1 and 2
Dim iFile As Integer			&apos;	1 or 2
Const cstPyHelper = &quot;$&quot; &amp; &quot;_SF_FileSystem__CompareFiles&quot;

Const cstThisSub = &quot;FileSystem.CompareFiles&quot;
Const cstSubArgs = &quot;FileName1, FileName2, [CompareContents=False]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bCompare = False

Check: 
	If IsMissing(CompareContents) Or IsEmpty(CompareContents) Then CompareContents = False
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName1, &quot;FileName1&quot;, False) Then GoTo Finally
		If Not SF_Utils._ValidateFile(FileName2, &quot;FileName2&quot;, False) Then GoTo Finally
		If Not SF_Utils._Validate(CompareContents, &quot;CompareContents&quot;, V_BOOLEAN) Then GoTo Finally
	End If
	&apos;	Do the files exist ? Otherwise raise error
	sFile = FileName1	:	iFile = 1
	If Not SF_FileSystem.FileExists(sFile) Then GoTo CatchNotExists
	sFile = FileName2	:	iFile = 2
	If Not SF_FileSystem.FileExists(sFile) Then GoTo CatchNotExists

	sFile = FileName1	:	iFile = 1
	If SF_FileSystem._IsDocFileSystem(sFile) Then GoTo CatchNotSupported
	sFile = FileName2	:	iFile = 2
	If SF_FileSystem._IsDocFileSystem(sFile) Then GoTo CatchNotSupported

Try:
	With ScriptForge.SF_Session
		bCompare = .ExecutePythonScript(.SCRIPTISSHARED, _SF_.PythonHelper &amp; cstPyHelper _
							, _ConvertFromUrl(FileName1) _
							, _ConvertFromUrl(FileName2) _
							, CompareContents)
	End With

Finally:
	CompareFiles = bCompare
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot; &amp; iFile, sFile)
	GoTo Finally
CatchNotSupported:
	SF_Exception.RaiseFatal(FILESYSTEMERROR,  &quot;FileName&quot; &amp; iFile, Split(cstThisSub, &quot;.&quot;)(1), sFile)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.CompareFiles

REM -----------------------------------------------------------------------------
Public Function CopyFile(Optional ByVal Source As Variant _
							, Optional ByVal Destination As Variant _
							, Optional ByVal Overwrite As Variant _
							) As Boolean
&apos;&apos;&apos;	Copies one or more files from one location to another
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Source: FileName or NamePattern which can include wildcard characters, for one or more files to be copied
&apos;&apos;&apos;		Destination:	FileName where the single Source file is to be copied
&apos;&apos;&apos;					or	FolderName where the multiple files from Source are to be copied
&apos;&apos;&apos;							If FolderName does not exist, it is created
&apos;&apos;&apos;					Anyway, wildcard characters are not allowed in Destination
&apos;&apos;&apos;		Overwrite: If True (default), files may be overwritten
&apos;&apos;&apos;			CopyFile will fail if Destination has the read-only attribute set, regardless of the value of Overwrite.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if at least one file has been copied
&apos;&apos;&apos;		False if an error occurred
&apos;&apos;&apos;			An error also occurs if a source using wildcard characters doesn&apos;t match any files.
&apos;&apos;&apos;			The method stops on the first error it encounters
&apos;&apos;&apos;			No attempt is made to roll back or undo any changes made before an error occurs
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR			Source does not exist
&apos;&apos;&apos;		UNKNOWNFOLDERERROR			Source folder or Destination folder does not exist
&apos;&apos;&apos;		NOFILEMATCHERROR		No file matches Source containing wildcards
&apos;&apos;&apos;		NOTAFOLDERERROR				Destination is a file, not a folder
&apos;&apos;&apos;		NOTAFILEERROR				Destination is a folder, not a file
&apos;&apos;&apos;		OVERWRITEERROR				Destination can not be overwritten
&apos;&apos;&apos;		READONLYERROR				Destination has its read-only attribute set
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.CopyFile(&quot;C:\Windows\*.*&quot;, &quot;C:\Temp\&quot;, Overwrite := False)		&apos;	Only files are copied, subfolders are not

Dim bCopy As Boolean			&apos;	Return value

Const cstThisSub = &quot;FileSystem.CopyFile&quot;
Const cstSubArgs = &quot;Source, Destination, [Overwrite=True]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bCopy = False

Check: 
	If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = True
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(Source, &quot;Source&quot;, True) Then GoTo Finally
		If Not SF_Utils._ValidateFile(Destination, &quot;Destination&quot;, False) Then GoTo Finally
		If Not SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, V_BOOLEAN) Then GoTo Finally
	End If

Try:
	bCopy = SF_FileSystem._CopyMove(&quot;CopyFile&quot;, Source, Destination, Overwrite)

Finally:
	CopyFile = bCopy
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.CopyFile

REM -----------------------------------------------------------------------------
Public Function CopyFolder(Optional ByVal Source As Variant _
							, Optional ByVal Destination As Variant _
							, Optional ByVal Overwrite As Variant _
							) As Boolean
&apos;&apos;&apos;	Copies one or more folders from one location to another
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Source: FolderName or NamePattern which can include wildcard characters, for one or more folders to be copied
&apos;&apos;&apos;		Destination:	FolderName where the single Source folder is to be copied
&apos;&apos;&apos;					or	FolderName where the multiple folders from Source are to be copied
&apos;&apos;&apos;							If FolderName does not exist, it is created
&apos;&apos;&apos;					Anyway, wildcard characters are not allowed in Destination
&apos;&apos;&apos;		Overwrite: If True (default), folders and their content may be overwritten
&apos;&apos;&apos;			CopyFile will fail if Destination has the read-only attribute set, regardless of the value of Overwrite.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if at least one folder has been copied
&apos;&apos;&apos;		False if an error occurred
&apos;&apos;&apos;			An error also occurs if a source using wildcard characters doesn&apos;t match any folders.
&apos;&apos;&apos;			The method stops on the first error it encounters
&apos;&apos;&apos;			No attempt is made to roll back or undo any changes made before an error occurs
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR			Source does not exist
&apos;&apos;&apos;		UNKNOWNFOLDERERROR			Source folder or Destination folder does not exist
&apos;&apos;&apos;		NOFILEMATCHERROR		No file matches Source containing wildcards
&apos;&apos;&apos;		NOTAFOLDERERROR				Destination is a file, not a folder
&apos;&apos;&apos;		OVERWRITEERROR				Destination can not be overwritten
&apos;&apos;&apos;		READONLYERROR				Destination has its read-only attribute set
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.CopyFolder(&quot;C:\Windows\*&quot;, &quot;C:\Temp\&quot;, Overwrite := False)

Dim bCopy As Boolean			&apos;	Return value

Const cstThisSub = &quot;FileSystem.CopyFolder&quot;
Const cstSubArgs = &quot;Source, Destination, [Overwrite=True]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bCopy = False

Check: 
	If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = True
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(Source, &quot;Source&quot;, True) Then GoTo Finally
		If Not SF_Utils._ValidateFile(Destination, &quot;Destination&quot;, False) Then GoTo Finally
		If Not SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, V_BOOLEAN) Then GoTo Finally
	End If

Try:
	bCopy = SF_FileSystem._CopyMove(&quot;CopyFolder&quot;, Source, Destination, Overwrite)

Finally:
	CopyFolder = bCopy
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.CopyFolder

REM -----------------------------------------------------------------------------
Public Function CreateFolder(Optional ByVal FolderName As Variant) As Boolean
&apos;&apos;&apos;	Return True if the given folder name could be created successfully
&apos;&apos;&apos;	The parent folder does not need to exist beforehand
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FolderName: a string representing the folder to create. It must not exist
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if FolderName is a valid folder name, does not exist and creation was successful
&apos;&apos;&apos;		False otherwise including when FolderName is a file
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		FOLDERCREATIONERROR		FolderName is an existing folder or file
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.CreateFolder(&quot;C:\NewFolder\&quot;)

Dim bCreate As Boolean			&apos;	Return value
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess

Const cstThisSub = &quot;FileSystem.CreateFolder&quot;
Const cstSubArgs = &quot;FolderName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bCreate = False

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FolderName, &quot;FolderName&quot;) Then GoTo Finally
	End If

Try:
	Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
	If SF_FileSystem.FolderExists(FolderName) Then GoTo CatchExists
	If SF_FileSystem.FileExists(FolderName) Then GoTo CatchExists
	oSfa.createFolder(SF_FileSystem._ConvertToUrl(FolderName))
	bCreate = True

Finally:
	CreateFolder = bCreate
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchExists:
	SF_Exception.RaiseFatal(FOLDERCREATIONERROR, &quot;FolderName&quot;, FolderName)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.CreateFolder

REM -----------------------------------------------------------------------------
Public Function CreateTextFile(Optional ByVal FileName As Variant _
								, Optional ByVal Overwrite As Variant _
								, Optional ByVal Encoding As Variant _
								) As Object
&apos;&apos;&apos;	Creates a specified file and returns a TextStream object that can be used to write to the file
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Identifies the file to create
&apos;&apos;&apos;		Overwrite: Boolean value that indicates if an existing file can be overwritten (default = True)
&apos;&apos;&apos;		Encoding: The character set that should be used
&apos;&apos;&apos;				Use one of the Names listed in https://www.iana.org/assignments/character-sets/character-sets.xhtml
&apos;&apos;&apos;				Note that LibreOffice does not implement all existing sets
&apos;&apos;&apos;				Default = UTF-8
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		An instance of the SF_TextStream class representing the opened file or a Null object if an error occurred
&apos;&apos;&apos;		It doesn&apos;t check either if the given encoding is implemented in LibreOffice
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		OVERWRITEERROR			File exists, creation impossible
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim myFile As Object
&apos;&apos;&apos;		    FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			Set myFile = FSO.CreateTextFile(&quot;C:\Temp\ThisFile.txt&quot;, Overwrite := True)

Dim oTextStream As Object		&apos;	Return value
Const cstThisSub = &quot;FileSystem.CreateTextFile&quot;
Const cstSubArgs = &quot;FileName, [Overwrite=True], [Encoding=&quot;&quot;UTF-8&quot;&quot;]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	Set oTextStream = Nothing

Check:
	If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = True
	If IsMissing(Encoding) Or IsEmpty(Encoding) Then Encoding = &quot;UTF-8&quot;
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
		If Not SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, V_BOOLEAN) Then GoTo Finally
		If Not SF_Utils._Validate(Encoding, &quot;Encoding&quot;, V_STRING) Then GoTo Finally
	End If

	With SF_FileSystem
		If .FileExists(FileName) Then
			If Overwrite Then .DeleteFile(FileName) Else GoTo CatchOverWrite
		End If

Try:
		Set oTextStream = .OpenTextFile(FileName, .ForWriting, Create := True, Encoding := Encoding)
	End With

Finally:
	Set CreateTextFile = oTextStream
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchOverWrite:
	SF_Exception.RaiseFatal(OVERWRITEERROR, &quot;FileName&quot;, FileName)
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.CreateTextFile

REM -----------------------------------------------------------------------------
Public Function DeleteFile(Optional ByVal FileName As Variant) As Boolean
&apos;&apos;&apos;	Deletes one or more files
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: FileName or NamePattern which can include wildcard characters, for one or more files to be deleted
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if at least one file has been deleted
&apos;&apos;&apos;		False if an error occurred
&apos;&apos;&apos;			An error also occurs if a FileName using wildcard characters doesn&apos;t match any files.
&apos;&apos;&apos;			The method stops on the first error it encounters
&apos;&apos;&apos;			No attempt is made to roll back or undo any changes made before an error occurs
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR			FileName does not exist
&apos;&apos;&apos;		NOFILEMATCHERROR		No file matches FileName containing wildcards
&apos;&apos;&apos;		NOTAFILEERROR				Argument is a folder, not a file
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.DeleteFile(&quot;C:\Temp\*.*&quot;)		&apos;	Only files are deleted, subfolders are not

Dim bDelete As Boolean			&apos;	Return value

Const cstThisSub = &quot;FileSystem.DeleteFile&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bDelete = False

Check: 
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;, True) Then GoTo Finally
	End If

Try:
	bDelete = SF_FileSystem._Delete(&quot;DeleteFile&quot;, FileName)

Finally:
	DeleteFile = bDelete
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.DeleteFile

REM -----------------------------------------------------------------------------
Public Function DeleteFolder(Optional ByVal FolderName As Variant) As Boolean
&apos;&apos;&apos;	Deletes one or more Folders
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FolderName: FolderName or NamePattern which can include wildcard characters, for one or more Folders to be deleted
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if at least one folder has been deleted
&apos;&apos;&apos;		False if an error occurred
&apos;&apos;&apos;			An error also occurs if a FolderName using wildcard characters doesn&apos;t match any folders.
&apos;&apos;&apos;			The method stops on the first error it encounters
&apos;&apos;&apos;			No attempt is made to roll back or undo any changes made before an error occurs
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFOLDERERROR			FolderName does not exist
&apos;&apos;&apos;		NOFILEMATCHERROR		No folder matches FolderName containing wildcards
&apos;&apos;&apos;		NOTAFOLDERERROR				Argument is a file, not a folder
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.DeleteFolder(&quot;C:\Temp\*&quot;)		&apos;	Only folders are deleted, files in the parent folder are not

Dim bDelete As Boolean			&apos;	Return value

Const cstThisSub = &quot;FileSystem.DeleteFolder&quot;
Const cstSubArgs = &quot;FolderName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bDelete = False

Check: 
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FolderName, &quot;FolderName&quot;, True) Then GoTo Finally
	End If

Try:
	bDelete = SF_FileSystem._Delete(&quot;DeleteFolder&quot;, FolderName)

Finally:
	DeleteFolder = bDelete
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.DeleteFolder

REM -----------------------------------------------------------------------------
Public Function ExtensionFolder(Optional ByVal Extension As Variant) As String
&apos;&apos;&apos;	Return the folder where the given extension is installed. The argument must
&apos;&apos;&apos;	be in the list of extensions provided by the SF_Platform.Extensions property
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Extension: a valid extension name
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The requested folder using the FileNaming notation
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		MsgBox FSO.ExtensionFolder(&quot;apso.python.script.organizer&quot;)

Dim sFolder As String			&apos;	Return value
Static vExtensions As Variant	&apos;	Cached list of existing extension names
Dim oPackage As Object			&apos;	/singletons/com.sun.star.deployment.PackageInformationProvider
Const cstThisSub = &quot;FileSystem.ExtensionFolder&quot;
Const cstSubArgs = &quot;Extension&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sFolder = &quot;&quot;

Check:
	If IsEmpty(vExtensions) Then vExtensions = SF_Platform.Extensions
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._Validate(Extension, &quot;Extension&quot;, V_STRING, vExtensions, True) Then GoTo Finally
	End If

Try:
	&apos;	Search an individual folder
	Set oPackage = SF_Utils._GetUnoService(&quot;PackageInformationProvider&quot;)
	sFolder = oPackage.getPackageLocation(Extension)
 
Finally:
	ExtensionFolder = SF_FileSystem._ConvertFromUrl(sFolder)
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.ExtensionFolder

REM -----------------------------------------------------------------------------
Public Function FileExists(Optional ByVal FileName As Variant) As Boolean
&apos;&apos;&apos;	Return True if the given file exists
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: a string representing a file
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if FileName is a valid File name and it exists
&apos;&apos;&apos;		False otherwise including when FileName is a folder
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		If FSO.FileExists(&quot;C:\Notepad.exe&quot;) Then ...

Dim bExists As Boolean			&apos;	Return value
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess

Const cstThisSub = &quot;FileSystem.FileExists&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bExists = False

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
	End If
	FileName = SF_FileSystem._ConvertToUrl(FileName)

Try:
	Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
	bExists = oSfa.exists(FileName) And Not oSfa.isFolder(FileName)

Finally:
	FileExists = bExists
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.FileExists

REM -----------------------------------------------------------------------------
Public Function Files(Optional ByVal FolderName As Variant _
						, Optional ByVal Filter As Variant _
						, Optional ByVal IncludeSubfolders As Variant _
						) As Variant
&apos;&apos;&apos;	Return an array of the FileNames stored in the given folder. The folder must exist
&apos;&apos;&apos;	Subfolders may be optionally explored too.
&apos;&apos;&apos;	If the number of files exceeds a reasonable amount (&gt; 1000 ?), the process time may become long.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FolderName: the folder to explore
&apos;&apos;&apos;		Filter: contains wildcards (&quot;?&quot; and &quot;*&quot;) to limit the list to the relevant files (default = &quot;&quot;)
&apos;&apos;&apos;		IncludeSubfolders: when True (default = False), subfolders are explored too.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		An array of strings, each entry is the FileName of an existing file
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFOLDERERROR		Folder does not exist
&apos;&apos;&apos;		NOTAFOLDERERROR			FolderName is a file, not a folder
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As Variant
&apos;&apos;&apos;			FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.Files(&quot;C:\Windows\&quot;, IncludeSubfolders := True)

Dim vFiles As Variant			&apos;	Return value
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess
Dim sFilesColl As String		&apos;	cstSEPARATOR delimited string of list of files (FileNaming notation)
Dim i As Long

Const cstThisSub = &quot;FileSystem.Files&quot;
Const cstSubArgs = &quot;FolderName, [Filter=&quot;&quot;&quot;&quot;], [IncludeSubfolders=False]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	vFiles = Array()

Check:
	If IsMissing(Filter) Or IsEmpty(Filter) Then Filter = &quot;&quot;
	If IsMissing(IncludeSubfolders) Or IsEmpty(IncludeSubfolders) Then IncludeSubfolders = False
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FolderName, &quot;FolderName&quot;) Then GoTo Finally
		If Not SF_Utils._Validate(Filter, &quot;Filter&quot;, V_STRING) Then GoTo Finally
		If Not SF_Utils._Validate(IncludeSubfolders, &quot;IncludeSubfolders&quot;, V_BOOLEAN) Then GoTo Finally
	End If
	If SF_FileSystem.FileExists(FolderName) Then GoTo CatchFile				&apos; Must not be a file
	If Not SF_FileSystem.FolderExists(FolderName) Then GoTo CatchFolder		&apos; Folder must exist

Try:
	sFilesColl = &quot;&quot;
	Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
	SF_FileSystem._ScanFolder(cstFiles, sFilesColl, FolderName, oSfa, Filter, IncludeSubfolders)

	If Len(sFilesColl) &gt; Len(cstSEPARATOR) Then vFiles() = Split(Mid(sFilesColl, Len(cstSEPARATOR) + 1), cstSEPARATOR)

Finally:
	Files = vFiles
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchFile:
	SF_Exception.RaiseFatal(NOTAFOLDERERROR, &quot;FolderName&quot;, FolderName)
	GoTo Finally
CatchFolder:
	SF_Exception.RaiseFatal(UNKNOWNFOLDERERROR, &quot;FolderName&quot;, FolderName)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.Files

REM -----------------------------------------------------------------------------
Public Function FolderExists(Optional ByVal FolderName As Variant) As Boolean
&apos;&apos;&apos;	Return True if the given folder name exists
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FolderName: a string representing a folder
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if FolderName is a valid folder name and it exists
&apos;&apos;&apos;		False otherwise including when FolderName is a file
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		If FSO.FolderExists(&quot;C:\&quot;) Then ...

Dim bExists As Boolean			&apos;	Return value
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess

Const cstThisSub = &quot;FileSystem.FolderExists&quot;
Const cstSubArgs = &quot;FolderName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bExists = False

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FolderName, &quot;FolderName&quot;) Then GoTo Finally
	End If
	FolderName = SF_FileSystem._ConvertToUrl(FolderName)

Try:
	Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
	bExists = oSfa.isFolder(FolderName)

Finally:
	FolderExists = bExists
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.FolderExists

REM -----------------------------------------------------------------------------
Public Function GetBaseName(Optional ByVal FileName As Variant) As String
&apos;&apos;&apos;	Returns the BaseName part of the last component of a File- or FolderName, without its extension
&apos;&apos;&apos;	The method does not check for the existence of the specified file or folder
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Path and file name
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The BaseName of the given argument in native operating system format. May be empty
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As String
&apos;&apos;&apos;		    FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.GetBaseName(&quot;C:\Windows\Notepad.exe&quot;) returns Notepad

Dim sBase As String				&apos;	Return value
Dim sExt As String				&apos;	Extension
Dim sName As String				&apos;	Last component of FileName
Dim vName As Variant			&apos;	Array of trunks of sName
Const cstThisSub = &quot;FileSystem.GetBaseName&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sBase = &quot;&quot;

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
	End If

Try:
	sName = SF_FileSystem.GetName(FileName)
	If Len(sName) &gt; 0 Then
		If InStr(sName, &quot;.&quot;) &gt; 0 Then
			vName = Split(sName, &quot;.&quot;)
			sExt = vName(UBound(vName))
			sBase = Left(sName, Len(sName) - Len(sExt) - 1)
		Else
			sBase = sName
		End If
	End If

Finally:
	GetBaseName = sBase
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.GetBaseName

REM -----------------------------------------------------------------------------
Public Function GetExtension(Optional ByVal FileName As Variant) As String
&apos;&apos;&apos;	Returns the extension part of a File- or FolderName, without the dot (.).
&apos;&apos;&apos;	The method does not check for the existence of the specified file or folder
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Path and file name
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The extension without a leading dot. May be empty
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As String
&apos;&apos;&apos;		    FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.GetExtension(&quot;C:\Windows\Notepad.exe&quot;) returns exe

Dim sExt As String				&apos;	Return value
Dim sName As String				&apos;	Last component of FileName
Dim vName As Variant			&apos;	Array of trunks of sName
Const cstThisSub = &quot;FileSystem.GetExtension&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sExt = &quot;&quot;

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
	End If

Try:
	sName = SF_FileSystem.GetName(FileName)
	If Len(sName) &gt; 0 And InStr(sName, &quot;.&quot;) &gt; 0 Then
		vName = Split(sName, &quot;.&quot;)
		sExt = vName(UBound(vName))
	End If

Finally:
	GetExtension = sExt
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.GetExtension

REM -----------------------------------------------------------------------------
Public Function GetFileLen(Optional ByVal FileName As Variant) As Currency
&apos;&apos;&apos;	Return file size in bytes with four decimals &apos;&apos;&apos;
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: a string representing a file
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		File size if FileName exists
&apos;&apos;&apos;		0 when FileName belongs to a document&apos;s internal file systems.
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR	The file does not exist or is a folder
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Print SF_FileSystem.GetFileLen(&quot;C:\pagefile.sys&quot;)

Dim curSize As Currency			&apos;	Return value
Const cstPyHelper = &quot;$&quot; &amp; &quot;_SF_FileSystem__GetFilelen&quot;
Const cstThisSub = &quot;FileSystem.GetFileLen&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	curSize = 0

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
	End If

Try:
	If SF_FileSystem.FileExists(FileName) Then
		If SF_FileSystem._IsDocFileSystem(FileName) Then
			curSize = 0
		Else
			With ScriptForge.SF_Session
				curSize = .ExecutePythonScript(.SCRIPTISSHARED, _SF_.PythonHelper &amp; cstPyHelper _
								, _ConvertFromUrl(FileName))
			End With
		End If
	Else
		GoTo CatchNotExists
	End If

Finally:
	GetFileLen = curSize
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot;, FileName)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.GetFileLen

REM -----------------------------------------------------------------------------
Public Function GetFileModified(Optional ByVal FileName As Variant) As Variant
&apos;&apos;&apos;	Returns the last modified date for the given file
&apos;&apos;&apos;	The method is not supported for document&apos;s internal file systems.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: a string representing an existing file
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The modification date and time as a Basic Date
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR	The file does not exist or is a folder
&apos;&apos;&apos;		FILESYSTEMERROR		The method is not applicable on document&apos;s file systems
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As Date
&apos;&apos;&apos;			FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.GetFileModified(&quot;C:\Temp\myDoc.odt&quot;)

Dim dModified As Date			&apos;	Return value
Dim oModified As New com.sun.star.util.DateTime
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess

Const cstThisSub = &quot;FileSystem.GetFileModified&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	dModified = 0

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
	End If
	If SF_FileSystem._IsDocFileSystem(FileName) Then GoTo CatchNotSupported

Try:
	Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
	If SF_FileSystem.FileExists(FileName) Then
		FileName = SF_FileSystem._ConvertToUrl(FileName)
		Set oModified = oSfa.getDateTimeModified(FileName)
		dModified = CDateFromUnoDateTime(oModified)
	Else
		GoTo CatchNotExists
	End If

Finally:
	GetFileModified = dModified
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot;, FileName)
	GoTo Finally
CatchNotSupported:
	SF_Exception.RaiseFatal(FILESYSTEMERROR, &quot;FileName&quot;, Split(cstThisSub, &quot;.&quot;)(1), FileName)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.GetFileModified

REM -----------------------------------------------------------------------------
Public Function GetName(Optional ByVal FileName As Variant) As String
&apos;&apos;&apos;	Returns the last component of a File- or FolderName
&apos;&apos;&apos;	The method does not check for the existence of the specified file or folder
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Path and file name
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The last component of the full file name in native operating system format
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As String
&apos;&apos;&apos;		    FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.GetName(&quot;C:\Windows\Notepad.exe&quot;) returns Notepad.exe

Dim sName As String				&apos;	Return value
Dim vFile As Variant			&apos;	Array of components
Const cstThisSub = &quot;FileSystem.GetName&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sName = &quot;&quot;

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
	End If
	FileName = SF_FileSystem._ConvertToUrl(FileName)

Try:
	If Len(FileName) &gt; 0 Then
		If Right(FileName, 1) = &quot;/&quot; Then FileName = Left(FileName, Len(FileName) - 1)
		vFile = Split(FileName, &quot;/&quot;)
		sName = ConvertFromUrl(vFile(UBound(vFile)))	&apos;	Always in SYS format
	End If

Finally:
	GetName = sName
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.GetName

REM -----------------------------------------------------------------------------
Public Function GetParentFolderName(Optional ByVal FileName As Variant) As String
&apos;&apos;&apos;	Returns a string containing the name of the parent folder of the last component in a specified File- or FolderName
&apos;&apos;&apos;	The method does not check for the existence of the specified file or folder
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Path and file name
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A FolderName including its final path separator
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As String
&apos;&apos;&apos;		    FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.GetParentFolderName(&quot;C:\Windows\Notepad.exe&quot;) returns C:\Windows\

Dim sFolder As String			&apos;	Return value
Dim sName As String				&apos;	Last component of FileName
Dim vFile As Variant			&apos;	Array of file components
Const cstThisSub = &quot;FileSystem.GetParentFolderName&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sFolder = &quot;&quot;

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
	End If
	FileName = SF_FileSystem._ConvertToUrl(FileName)

Try:
	If Right(FileName, 1) = &quot;/&quot; Then FileName = Left(FileName, Len(FileName) - 1)
	vFile = Split(FileName, &quot;/&quot;)
	If UBound(vFile) &gt;= 0 Then vFile(UBound(vFile)) = &quot;&quot;
	sFolder = Join(vFile, &quot;/&quot;)
	If sFolder = &quot;&quot; Or Right(sFolder, 1) &lt;&gt; &quot;/&quot; Then sFolder = sFolder &amp; &quot;/&quot;

Finally:
	GetParentFolderName = SF_FileSystem._ConvertFromUrl(sFolder)
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.GetParentFolderName

REM -----------------------------------------------------------------------------
Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
&apos;&apos;&apos;	Return the actual value of the given property
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		PropertyName: the name of the property as a string
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The actual value of the property
&apos;&apos;&apos;	Exceptions
&apos;&apos;&apos;		ARGUMENTERROR		The property does not exist

Const cstThisSub = &quot;FileSystem.GetProperty&quot;
Const cstSubArgs = &quot;PropertyName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	GetProperty = Null

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
	End If

Try:
	Select Case UCase(PropertyName)
		Case UCase(&quot;ConfigFolder&quot;)			:	GetProperty = ConfigFolder
		Case UCase(&quot;ExtensionsFolder&quot;)		:	GetProperty = ExtensionsFolder
		Case UCase(&quot;FileNaming&quot;)			:	GetProperty = FileNaming
		Case UCase(&quot;HomeFolder&quot;)			:	GetProperty = HomeFolder
		Case UCase(&quot;InstallFolder&quot;)			:	GetProperty = InstallFolder
		Case UCase(&quot;TemplatesFolder&quot;)		:	GetProperty = TemplatesFolder
		Case UCase(&quot;TemporaryFolder&quot;)		:	GetProperty = TemporaryFolder
		Case UCase(&quot;UserTemplatesFolder&quot;)	:	GetProperty = UserTemplatesFolder
		Case Else
	End Select

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.GetProperty

REM -----------------------------------------------------------------------------
Public Function GetTempName(Optional ByVal Extension As Variant) As String
&apos;&apos;&apos;	Returns a randomly generated temporary file name that is useful for performing
&apos;&apos;&apos;	operations that require a temporary file : the method does not create any file
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A FileName as a String that can be used f.i. with CreateTextFile()
&apos;&apos;&apos;		The FileName has as suffix the given extension.
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As String
&apos;&apos;&apos;			FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.GetTempName(&quot;txt&quot;)	&apos;	/tmp/SF_123456.txt
&apos;&apos;&apos;			a = FSO.GetTempName()		&apos;	/tmp/SF_234567

Dim sFile As String				&apos;	Return value
Dim sExtension As String		&apos;	The given extension preceded by a dot
Dim lRandom As Long				&apos;	Random integer

Const cstThisSub = &quot;FileSystem.GetTempName&quot;
Const cstSubArgs = &quot;&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sFile = &quot;&quot;

Check:
	If IsMissing(Extension) Or IsEmpty(Extension) Then Extension = &quot;&quot;
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._Validate(Extension, &quot;Extension&quot;, V_STRING) Then GoTo Catch
	End If

Try:
	lRandom = SF_Session.ExecuteCalcFunction(&quot;RANDBETWEEN.NV&quot;, 1, 999999)
	If Len(Extension) &gt; 0 Then sExtension = &quot;.&quot; &amp; Extension Else sExtension = &quot;&quot;
	sFile = SF_FileSystem.TemporaryFolder &amp; &quot;SF_&quot; &amp; Right(&quot;000000&quot; &amp; lRandom, 6) &amp; sExtension

Finally:
	GetTempName = SF_FileSystem._ConvertFromUrl(sFile)
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.GetTempName

REM -----------------------------------------------------------------------------
Public Function HashFile(Optional ByVal FileName As Variant _
							, Optional ByVal Algorithm As Variant _
							) As String
&apos;&apos;&apos;	Return an hexadecimal string representing a checksum of the given file
&apos;&apos;&apos;	Next algorithms are supported: MD5, SHA1, SHA224, SHA256, SHA384 and SHA512
&apos;&apos;&apos;	The method is not supported for document&apos;s internal file systems.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: a string representing a file
&apos;&apos;&apos;		Algorithm: The hashing algorithm to use
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The requested checksum as a string. Hexadecimal digits are lower-cased
&apos;&apos;&apos;		A zero-length string when an error occurred
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR	The file does not exist or is a folder
&apos;&apos;&apos;		FILESYSTEMERROR		The method is not applicable on document&apos;s file systems
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Print SF_FileSystem.HashFile(&quot;C:\pagefile.sys&quot;, &quot;MD5&quot;)

Dim sHash As String				&apos;	Return value
Const cstPyHelper = &quot;$&quot; &amp; &quot;_SF_FileSystem__HashFile&quot;
Const cstThisSub = &quot;FileSystem.HashFile&quot;
Const cstSubArgs = &quot;FileName, Algorithm=&quot;&quot;MD5&quot;&quot;|&quot;&quot;SHA1&quot;&quot;|&quot;&quot;SHA224&quot;&quot;|&quot;&quot;SHA256&quot;&quot;|&quot;&quot;SHA384&quot;&quot;|&quot;&quot;SHA512&quot;&quot;&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sHash = &quot;&quot;

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
		If Not SF_Utils._Validate(Algorithm, &quot;Algorithm&quot;, V_STRING _
					, Array(&quot;MD5&quot;, &quot;SHA1&quot;, &quot;SHA224&quot;, &quot;SHA256&quot;, &quot;SHA384&quot;, &quot;SHA512&quot;)) Then GoTo Finally
	End If
	If SF_FileSystem._IsDocFileSystem(FileName) Then GoTo CatchNotSupported

Try:
	If SF_FileSystem.FileExists(FileName) Then
		With ScriptForge.SF_Session
			sHash = .ExecutePythonScript(.SCRIPTISSHARED, _SF_.PythonHelper &amp; cstPyHelper _
							, _ConvertFromUrl(FileName), LCase(Algorithm))
		End With
	Else
		GoTo CatchNotExists
	End If

Finally:
	HashFile = sHash
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot;, FileName)
	GoTo Finally
CatchNotSupported:
	SF_Exception.RaiseFatal(FILESYSTEMERROR, &quot;FileName&quot;, Split(cstThisSub, &quot;.&quot;)(1), FileName)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.HashFile

REM -----------------------------------------------------------------------------
Public Function Methods() As Variant
&apos;&apos;&apos;	Return the list or methods of the FileSystem service as an array

	Methods = Array(&quot;BuildPath&quot; _
					, &quot;CompareFiles&quot; _
					, &quot;CopyFile&quot; _
					, &quot;CopyFolder&quot; _
					, &quot;CreateFolder&quot; _
					, &quot;CreateTextFile&quot; _
					, &quot;DeleteFile&quot; _
					, &quot;DeleteFolder&quot; _
					, &quot;ExtensionFolder&quot; _
					, &quot;FileExists&quot; _
					, &quot;Files&quot; _
					, &quot;FolderExists&quot; _
					, &quot;GetBaseName&quot; _
					, &quot;GetExtension&quot; _
					, &quot;GetFileLen&quot; _
					, &quot;GetFileModified&quot; _
					, &quot;GetName&quot; _
					, &quot;GetParentFolderName&quot; _
					, &quot;GetTempName&quot; _
					, &quot;HashFile&quot; _
					, &quot;MoveFile&quot; _
					, &quot;MoveFolder&quot; _
					, &quot;Normalize&quot; _
					, &quot;OpenTextFile&quot; _
					, &quot;PickFile&quot; _
					, &quot;PickFolder&quot; _
					, &quot;SubFolders&quot; _
					)

End Function	&apos;	ScriptForge.SF_FileSystem.Methods

REM -----------------------------------------------------------------------------
Public Function MoveFile(Optional ByVal Source As Variant _
							, Optional ByVal Destination As Variant _
							) As Boolean
&apos;&apos;&apos;	Moves one or more files from one location to another
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Source: FileName or NamePattern which can include wildcard characters, for one or more files to be moved
&apos;&apos;&apos;		Destination:	FileName where the single Source file is to be moved
&apos;&apos;&apos;							If Source and Destination have the same parent folder MoveFile amounts to renaming the Source
&apos;&apos;&apos;					or	FolderName where the multiple files from Source are to be moved
&apos;&apos;&apos;							If FolderName does not exist, it is created
&apos;&apos;&apos;					Anyway, wildcard characters are not allowed in Destination
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if at least one file has been moved
&apos;&apos;&apos;		False if an error occurred
&apos;&apos;&apos;			An error also occurs if a source using wildcard characters doesn&apos;t match any files.
&apos;&apos;&apos;			The method stops on the first error it encounters
&apos;&apos;&apos;			No attempt is made to roll back or undo any changes made before an error occurs
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR			Source does not exist
&apos;&apos;&apos;		UNKNOWNFOLDERERROR			Source folder or Destination folder does not exist
&apos;&apos;&apos;		NOFILEMATCHERROR		No file matches Source containing wildcards
&apos;&apos;&apos;		NOTAFOLDERERROR				Destination is a file, not a folder
&apos;&apos;&apos;		NOTAFILEERROR				Destination is a folder, not a file
&apos;&apos;&apos;		OVERWRITEERROR				Destination can not be overwritten
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.MoveFile(&quot;C:\Temp1\*.*&quot;, &quot;C:\Temp2\&quot;)		&apos;	Only files are moved, subfolders are not

Dim bMove As Boolean			&apos;	Return value

Const cstThisSub = &quot;FileSystem.MoveFile&quot;
Const cstSubArgs = &quot;Source, Destination&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bMove = False

Check: 
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(Source, &quot;Source&quot;, True) Then GoTo Finally
		If Not SF_Utils._ValidateFile(Destination, &quot;Destination&quot;, False) Then GoTo Finally
	End If

Try:
	bMove = SF_FileSystem._CopyMove(&quot;MoveFile&quot;, Source, Destination, False)

Finally:
	MoveFile = bMove
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.MoveFile

REM -----------------------------------------------------------------------------
Public Function MoveFolder(Optional ByVal Source As Variant _
							, Optional ByVal Destination As Variant _
							) As Boolean
&apos;&apos;&apos;	Moves one or more folders from one location to another
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Source: FolderName or NamePattern which can include wildcard characters, for one or more folders to be moved
&apos;&apos;&apos;		Destination:	FolderName where the single Source folder is to be moved
&apos;&apos;&apos;							FolderName must not exist
&apos;&apos;&apos;					or	FolderName where the multiple folders from Source are to be moved
&apos;&apos;&apos;							If FolderName does not exist, it is created
&apos;&apos;&apos;					Anyway, wildcard characters are not allowed in Destination
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if at least one folder has been moved
&apos;&apos;&apos;		False if an error occurred
&apos;&apos;&apos;			An error also occurs if a source using wildcard characters doesn&apos;t match any folders.
&apos;&apos;&apos;			The method stops on the first error it encounters
&apos;&apos;&apos;			No attempt is made to roll back or undo any changes made before an error occurs
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR			Source does not exist
&apos;&apos;&apos;		UNKNOWNFOLDERERROR			Source folder or Destination folder does not exist
&apos;&apos;&apos;		NOFILEMATCHERROR		No file matches Source containing wildcards
&apos;&apos;&apos;		NOTAFOLDERERROR				Destination is a file, not a folder
&apos;&apos;&apos;		OVERWRITEERROR				Destination can not be overwritten
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.MoveFolder(&quot;C:\Temp1\*&quot;, &quot;C:\Temp2\&quot;)

Dim bMove As Boolean			&apos;	Return value

Const cstThisSub = &quot;FileSystem.MoveFolder&quot;
Const cstSubArgs = &quot;Source, Destination&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bMove = False

Check: 
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(Source, &quot;Source&quot;, True) Then GoTo Finally
		If Not SF_Utils._ValidateFile(Destination, &quot;Destination&quot;, False) Then GoTo Finally
	End If

Try:
	bMove = SF_FileSystem._CopyMove(&quot;MoveFolder&quot;, Source, Destination, False)

Finally:
	MoveFolder = bMove
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.MoveFolder

REM -----------------------------------------------------------------------------
Public Function Normalize(Optional ByVal FileName As Variant) As String
&apos;&apos;&apos;	Normalize a pathname by collapsing redundant separators and up-level references
&apos;&apos;&apos;	so that A//B, A/B/, A/./B and A/foo/../B all become A/B. 
&apos;&apos;&apos;	On Windows, it converts forward slashes to backward slashes.
&apos;&apos;&apos;	The method returns the input string when the file is from a document&apos;s internal file systems.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: a string representing a file. The file may not exist.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The normalized filename in filenaming notation
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Print SF_FileSystem.Normalize(&quot;A/foo/../B/C/./D//E&quot;)	&apos;	A/B/C/D/E

Dim sNorm As String			&apos;	Return value
Const cstPyHelper = &quot;$&quot; &amp; &quot;_SF_FileSystem__Normalize&quot;
Const cstThisSub = &quot;FileSystem.Normalize&quot;
Const cstSubArgs = &quot;FileName&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sNorm = &quot;&quot;

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
	End If

Try:
	If SF_FileSystem._IsDocFileSystem(FileName) Then
		sNorm = FileName
	Else
		With ScriptForge.SF_Session
			sNorm = .ExecutePythonScript(.SCRIPTISSHARED, _SF_.PythonHelper &amp; cstPyHelper _
							, _ConvertFromUrl(FileName))
			&apos;	The Python os.path expects and returns a file name in os notation
			If SF_FileSystem.FileNaming &lt;&gt; &quot;SYS&quot; Then sNorm = ConvertToUrl(sNorm)
		End With
	End If

Finally:
	Normalize = sNorm
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.Normalize

REM -----------------------------------------------------------------------------
Public Function OpenTextFile(Optional ByVal FileName As Variant _
								, Optional ByVal IOMode As Variant _
								, Optional ByVal Create As Variant _
								, Optional ByVal Encoding As Variant _
								) As Object
&apos;&apos;&apos;	Opens a specified file and returns a TextStream object that can be used to read from, write to, or append to the file
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Identifies the file to open
&apos;&apos;&apos;		IOMode: Indicates input/output mode. Can be one of three constants: ForReading, ForWriting, or ForAppending
&apos;&apos;&apos;		Create: Boolean value that indicates whether a new file can be created if the specified filename doesn&apos;t exist.
&apos;&apos;&apos;				The value is True if a new file and its parent folders may be created; False if they aren&apos;t created (default)
&apos;&apos;&apos;		Encoding: The character set that should be used
&apos;&apos;&apos;				Use one of the Names listed in https://www.iana.org/assignments/character-sets/character-sets.xhtml
&apos;&apos;&apos;				Note that LibreOffice does not implement all existing sets
&apos;&apos;&apos;				Default = UTF-8
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		An instance of the SF_TextStream class representing the opened file or a Null object if an error occurred
&apos;&apos;&apos;		The method does not check if the file is really a text file
&apos;&apos;&apos;		It doesn&apos;t check either if the given encoding is implemented in LibreOffice nor if it is the right one
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFILEERROR		File does not exist
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim myFile As Object
&apos;&apos;&apos;		    FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			Set myFile = FSO.OpenTextFile(&quot;C:\Temp\ThisFile.txt&quot;, FSO.ForReading)
&apos;&apos;&apos;			If Not IsNull(myFile) Then	&apos; ... Go ahead with reading text lines

Dim oTextStream As Object		&apos;	Return value
Dim bExists As Boolean			&apos;	When True, file to open does exist
Dim bEmbeddedFile As Boolean	&apos;	When True, file to open is embedded in a document&apos;s internal file system
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess
Const cstThisSub = &quot;FileSystem.OpenTextFile&quot;
Const cstSubArgs = &quot;FileName, [IOMode=1|2|8], [Create=False], [Encoding=&quot;&quot;UTF-8&quot;&quot;]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	Set oTextStream = Nothing

Check:
	With SF_FileSystem
		If IsMissing(IOMode) Or IsEmpty(IOMode) Then IOMode = cstForReading
		If IsMissing(Create) Or IsEmpty(Create) Then Create = False
		If IsMissing(Encoding) Or IsEmpty(Encoding) Then Encoding = &quot;UTF-8&quot;
		If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
			If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
			If Not SF_Utils._Validate(IOMode, &quot;IOMode&quot;, V_NUMERIC _
					, Array(cstForReading, cstForWriting, cstForAppending)) _
						Then GoTo Finally
			If Not SF_Utils._Validate(Create, &quot;Create&quot;, V_BOOLEAN) Then GoTo Finally
			If Not SF_Utils._Validate(Encoding, &quot;Encoding&quot;, V_STRING) Then GoTo Finally
		End If

		bExists = .FileExists(FileName)
		Select Case IOMode
			Case ForReading		:	If Not bExists Then GoTo CatchNotExists
			Case Else			:	If Not bExists And Not Create Then GoTo CatchNotExists
		End Select

		If IOMode = ForAppending And Not bExists Then IOMode = ForWriting

		bEmbeddedFile = SF_FileSystem._IsDocFileSystem(FileName)
	End With

Try:
	&apos;	Create and initialize TextStream class instance
	Set oTextStream = New SF_TextStream
	With oTextStream
		.[Me] = oTextStream
		.[_Parent] = SF_FileSystem
		._IsEmbeddedFile = bEmbeddedFile
		If bEmbeddedFile And (IOMode = cstForWriting Or IOMode = cstForAppending) Then
			&apos;	Updates of an embedded file are done on a copy
			._EmbeddedFileName = SF_FileSystem._ConvertToUrl(FileName)
			._FileName = SF_FileSystem._ConvertToUrl(SF_FileSystem.GetTempName(SF_FileSystem.GetExtension(FileName)))
			&apos;	Create the copy if relevant
			If bExists Then
				Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
				oSfa.copy(._EmbeddedFileName, ._FileName)
			End If
		Else
			._FileName = SF_FileSystem._ConvertToUrl(FileName)
		End If
		._IOMode = IOMode
		._Encoding = Encoding
		._FileExists = bExists
		._Initialize()
	End With

Finally:
	Set OpenTextFile = oTextStream
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot;, FileName)
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.OpenTextFile

REM -----------------------------------------------------------------------------
Public Function PickFile(Optional ByVal DefaultFile As Variant _
							, Optional ByVal Mode As Variant _
							, Optional ByVal Filter As Variant _
							) As String
&apos;&apos;&apos;	Returns the file selected with a FilePicker dialog box
&apos;&apos;&apos;	The mode, OPEN or SAVE, and the filter may be preset
&apos;&apos;&apos;	If mode = SAVE and the picked file exists, a warning message will be displayed
&apos;&apos;&apos;	Modified from Andrew Pitonyak&apos;s Base Macro Programming §10.4
&apos;&apos;&apos;	The method is not supported for document&apos;s internal file systems.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		DefaultFile:	Folder part: the FolderName from which to start. Default = the last selected folder
&apos;&apos;&apos;						File part: the default file to open or save
&apos;&apos;&apos;		Mode: &quot;OPEN&quot; (input file) or &quot;SAVE&quot; (output file)
&apos;&apos;&apos;		Filter: by default only files having the given suffix will be displayed. Default = all suffixes
&apos;&apos;&apos;			The filter combo box will contain the given suffix filter (if not &quot;*&quot;) and &quot;*.*&quot;
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The selected FileName in FileNaming format or &quot;&quot; if the dialog was cancelled
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		FILESYSTEMERROR		The method is not applicable on document&apos;s file systems
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.PickFile(&quot;C:\&quot;, &quot;OPEN&quot;, &quot;txt&quot;)		&apos; Only *.txt files are displayed

Dim oFileDialog As Object		&apos;	com.sun.star.ui.dialogs.FilePicker
Dim oFileAccess As object		&apos;	com.sun.star.ucb.SimpleFileAccess
Dim oPath As Object				&apos;	com.sun.star.util.PathSettings
Dim iAccept As Integer			&apos;	Result of dialog execution
Dim sInitPath As String			&apos;	Current working directory
Dim sBaseFile As String
Dim iMode As Integer			&apos;	Numeric alias for SelectMode
Dim sFile As String				&apos;	Return value

Const cstThisSub = &quot;FileSystem.PickFile&quot;
Const cstSubArgs = &quot;[DefaultFile=&quot;&quot;&quot;&quot;], [Mode=&quot;&quot;OPEN&quot;&quot;|&quot;&quot;SAVE&quot;&quot;],[Filter=&quot;&quot;&quot;&quot;]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sFile = &quot;&quot;

Check:
	If IsMissing(DefaultFile) Or IsEmpty(DefaultFile) Then DefaultFile = &quot;&quot;
	If IsMissing(Mode) Or IsEmpty(Mode) Then Mode = &quot;OPEN&quot;
	If IsMissing(Filter) Or IsEmpty(Filter) Then Filter = &quot;&quot;
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(DefaultFile, &quot;DefaultFile&quot;, , True) Then GoTo Finally
		If Not SF_Utils._Validate(Mode, &quot;Mode&quot;, V_STRING, Array(&quot;OPEN&quot;, &quot;SAVE&quot;)) Then GoTo Finally
		If Not SF_Utils._Validate(Filter, &quot;Filter&quot;, V_STRING) Then GoTo Finally
	End If
	If SF_FileSystem._IsDocFileSystem(DefaultFile) Then GoTo CatchNotSupported
	DefaultFile = SF_FileSystem._ConvertToUrl(DefaultFile)

Try:
	&apos; Derive numeric equivalent of the Mode argument: https://api.libreoffice.org/docs/idl/ref/TemplateDescription_8idl.html
	With com.sun.star.ui.dialogs.TemplateDescription
		If UCase(Mode) = &quot;OPEN&quot; Then iMode = .FILEOPEN_SIMPLE Else iMode = .FILESAVE_AUTOEXTENSION
	End With

	&apos; Activate the filepicker dialog
	Set oFileDialog = CreateUnoService(&quot;com.sun.star.ui.dialogs.FilePicker&quot;)	&apos;	Do not reuse an existing FilePicker: TDF#154462
	With oFileDialog
		.Initialize(Array(iMode))

		&apos; Set filters
		If Len(Filter) &gt; 0 Then .appendFilter(&quot;*.&quot; &amp; Filter, &quot;*.&quot; &amp; Filter)		&apos; Twice: required by API
		.appendFilter(&quot;*.*&quot;, &quot;*.*&quot;)
		If Len(Filter) &gt; 0 Then .setCurrentFilter(&quot;*.&quot; &amp; Filter) Else .setCurrentFilter(&quot;*.*&quot;)

		&apos; Set initial folder
		If Len(DefaultFile) = 0 Then	&apos; TODO: SF_Session.WorkingFolder
			Set oPath = SF_Utils._GetUNOService(&quot;PathSettings&quot;)
			sInitPath = oPath.Work		&apos;	Probably My Documents
		Else
			sInitPath = SF_FileSystem._ParseUrl(ConvertToUrl(DefaultFile)).Path
		End If

		&apos; Set default values
		Set oFileAccess = SF_Utils._GetUNOService(&quot;FileAccess&quot;)
		If oFileAccess.exists(sInitPath) Then .SetDisplayDirectory(sInitPath)
		sBaseFile = SF_FileSystem.GetName(DefaultFile)
		.setDefaultName(sBaseFile)

		&apos; Get selected file
		iAccept = .Execute()
		If iAccept = com.sun.star.ui.dialogs.ExecutableDialogResults.OK Then sFile = .getSelectedFiles()(0)

		&apos;	Do not reuse a FilePicker, side effects observed (a.o. TDF#154462)
		.dispose()

	End With

Finally:
	PickFile = SF_FileSystem._ConvertFromUrl(sFile)
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotSupported:
	SF_Exception.RaiseFatal(FILESYSTEMERROR, &quot;DefaultFile&quot;, Split(cstThisSub, &quot;.&quot;)(1), DefaultFile)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.PickFile

REM -----------------------------------------------------------------------------
Public Function PickFolder(Optional ByVal DefaultFolder As Variant _
							, Optional ByVal Title As Variant _
							, Optional ByVal FreeText As Variant _
							) As String
&apos;&apos;&apos;	Display a FolderPicker dialog box
&apos;&apos;&apos;	The method is not supported for document&apos;s internal file systems.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		DefaultFolder: the FolderName from which to start. Default = the last selected folder
&apos;&apos;&apos;		Title: text to display in the dialog header. Default is set by the desktop environment.
&apos;&apos;&apos;		FreeText: text to display in the dialog. Default = &quot;&quot;
&apos;&apos;&apos;			The argument is obsolete as from SF 25.8.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The selected FolderName in URL or operating system format
&apos;&apos;&apos;		The zero-length string if the dialog was cancelled
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		FILESYSTEMERROR		The method is not applicable on document&apos;s file systems
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;		FSO.PickFolder(&quot;C:\&quot;, &quot;Choose a folder or press Cancel&quot;)

Dim oFolderDialog As Object		&apos;	com.sun.star.ui.dialogs.FolderPicker
Dim iAccept As Integer			&apos;	Value returned by the dialog (OK, Cancel, ..)
Dim sFolder	As String			&apos;	Return value					&apos;	

Const cstThisSub = &quot;FileSystem.PickFolder&quot;
Const cstSubArgs = &quot;[DefaultFolder=&quot;&quot;&quot;&quot;], [Title=&quot;&quot;&quot;&quot;]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sFolder = &quot;&quot;

Check:
	If IsMissing(DefaultFolder) Or IsEmpty(DefaultFolder) Then DefaultFolder = &quot;&quot;
	If IsMissing(Title) Or IsEmpty(Title) Then Title = &quot;&quot;
	If IsMissing(FreeText) Or IsEmpty(FreeText) Then FreeText = &quot;&quot;
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(DefaultFolder, &quot;DefaultFolder&quot;, , True) Then GoTo Finally
		If Not SF_Utils._Validate(Title, &quot;Title&quot;, V_STRING) Then GoTo Finally
		If Not SF_Utils._Validate(FreeText, &quot;FreeText&quot;, V_STRING) Then GoTo Finally
	End If
	If SF_FileSystem._IsDocFileSystem(DefaultFolder) Then GoTo CatchNotSupported
	DefaultFolder = SF_FileSystem._ConvertToUrl(DefaultFolder)

	&apos;	Only Windows implements the Description property of the FolderPicker by displaying it in the title.
	&apos;	The method harmonizes the behaviour among all environments, including Linux.
	If Len(FreeText) &gt; 0 And Len(Title) = 0 Then Title = FreeText

Try:
	Set oFolderDialog = CreateUnoService(&quot;com.sun.star.ui.dialogs.FolderPicker&quot;)
	If Not IsNull(oFolderDialog) Then
		With oFolderDialog
			If Len(DefaultFolder) &gt; 0 Then .DisplayDirectory = ConvertToUrl(DefaultFolder)
			If Len(Title) &gt; 0 Then .setTitle(Title)
			If Len(FreeText) &gt; 0 Then .setDescription(FreeText)
			iAccept = .Execute()
			&apos; https://api.libreoffice.org/docs/idl/ref/ExecutableDialogResults_8idl.html
			If iAccept = com.sun.star.ui.dialogs.ExecutableDialogResults.OK Then
				.DisplayDirectory = .Directory	&apos;	Set the next default initial folder to the selected one
				sFolder = .Directory &amp; &quot;/&quot;
			End If
		End With
	End If

Finally:
	PickFolder = SF_FileSystem._ConvertFromUrl(sFolder)
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotSupported:
	SF_Exception.RaiseFatal(FILESYSTEMERROR, &quot;DefaultFolder&quot;, Split(cstThisSub, &quot;.&quot;)(1), DefaultFolder)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.PickFolder

REM -----------------------------------------------------------------------------
Public Function Properties() As Variant
&apos;&apos;&apos;	Return the list or properties of the FileSystem module as an array

	Properties = Array( _
					&quot;ConfigFolder&quot; _
					, &quot;ExtensionsFolder&quot; _
					, &quot;FileNaming&quot; _
					, &quot;HomeFolder&quot; _
					, &quot;InstallFolder&quot; _
					, &quot;TemplatesFolder&quot; _
					, &quot;TemporaryFolder&quot; _
					, &quot;UserTemplatesFolder&quot; _
					)

End Function	&apos;	ScriptForge.SF_FileSystem.Properties

REM -----------------------------------------------------------------------------
Public Function SetProperty(Optional ByVal PropertyName As Variant _
								, Optional ByRef Value As Variant _
								) As Boolean
&apos;&apos;&apos;	Set a new value to the given property
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		PropertyName: the name of the property as a string
&apos;&apos;&apos;		Value: its new value
&apos;&apos;&apos;	Exceptions
&apos;&apos;&apos;		ARGUMENTERROR		The property does not exist

Const cstThisSub = &quot;FileSystem.SetProperty&quot;
Const cstSubArgs = &quot;PropertyName, Value&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	SetProperty = False

Check:
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
	End If

Try:
	Select Case UCase(PropertyName)
		Case UCase(&quot;FileNaming&quot;)		:	FileNaming = Value	
		Case Else
	End Select

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem.SetProperty

REM -----------------------------------------------------------------------------
Public Function SubFolders(Optional ByVal FolderName As Variant _
								, Optional ByVal Filter As Variant _
								, Optional ByVal IncludeSubfolders As Variant _
								) As Variant
&apos;&apos;&apos;	Return an array of the FolderNames stored in the given folder. The folder must exist,
&apos;&apos;&apos;	Subfolders may be optionally explored too.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FolderName: the folder to explore
&apos;&apos;&apos;		Filter: contains wildcards (&quot;?&quot; and &quot;*&quot;) to limit the list to the relevant folders (default = &quot;&quot;)
&apos;&apos;&apos;		IncludeSubfolders: when True (default = False), subfolders are explored too.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		An array of strings, each entry is the FolderName of an existing folder
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		UNKNOWNFOLDERERROR		Folder does not exist
&apos;&apos;&apos;		NOTAFOLDERERROR			FolderName is a file, not a folder
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Dim a As Variant
&apos;&apos;&apos;			FSO.FileNaming = &quot;SYS&quot;
&apos;&apos;&apos;			a = FSO.SubFolders(&quot;C:\Windows\&quot;, IncludeSubfolders := True)

Dim vSubFolders As Variant		&apos;	Return value
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess
Dim sFoldersColl As String		&apos;	cstSEPARATOR delimited string of list of folders (FileNaming notation)
Dim i As Long

Const cstThisSub = &quot;FileSystem.SubFolders&quot;
Const cstSubArgs = &quot;FolderName, [Filter=&quot;&quot;&quot;&quot;], [IncludeSubfolders=False]&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	vSubFolders = Array()

Check:
	If IsMissing(Filter) Or IsEmpty(Filter) Then Filter = &quot;&quot;
	If IsMissing(IncludeSubfolders) Or IsEmpty(IncludeSubfolders) Then IncludeSubfolders = False
	If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._ValidateFile(FolderName, &quot;FolderName&quot;) Then GoTo Finally
		If Not SF_Utils._Validate(Filter, &quot;Filter&quot;, V_STRING) Then GoTo Finally
		If Not SF_Utils._Validate(IncludeSubfolders, &quot;IncludeSubfolders&quot;, V_BOOLEAN) Then GoTo Finally
	End If
	If SF_FileSystem.FileExists(FolderName) Then GoTo CatchFile				&apos; Must not be a file
	If Not SF_FileSystem.FolderExists(FolderName) Then GoTo CatchFolder		&apos; Top folder must exist

Try:
	sFoldersColl = &quot;&quot;
	Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
	SF_FileSystem._ScanFolder(cstFolders, sFoldersColl, FolderName, oSfa, Filter, IncludeSubfolders)

	If Len(sFoldersColl) &gt; Len(cstSEPARATOR) Then vSubFolders() = Split(Mid(sFoldersColl, Len(cstSEPARATOR) + 1), cstSEPARATOR)

Finally:
	SubFolders = vSubFolders
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchFile:
	SF_Exception.RaiseFatal(NOTAFOLDERERROR, &quot;FolderName&quot;, FolderName)
	GoTo Finally
CatchFolder:
	SF_Exception.RaiseFatal(UNKNOWNFOLDERERROR, &quot;FolderName&quot;, FolderName)
	GoTo Finally
End Function    &apos;   ScriptForge.SF_FileSystem.SubFolders

REM =========================================================== PRIVATE FUNCTIONS

REM -----------------------------------------------------------------------------
Private Function _ConvertFromUrl(psFile) As String
&apos;&apos;&apos;	Execute the builtin ConvertFromUrl function only when relevant
&apos;&apos;&apos;		i.e. when FileNaming (how arguments and return values are provided) = &quot;SYS&quot;
&apos;&apos;&apos;	Called at the bottom of methods returning file names
&apos;&apos;&apos;	Remarks:	psFile might contain wildcards
&apos;&apos;&apos;				Files from document&apos;s file systems are never converted

Const cstQuestion = &quot;$QUESTION$&quot;, cstStar = &quot;$STAR$&quot;	&apos;	Special tokens to replace wildcards

	If SF_FileSystem.FileNaming = &quot;SYS&quot; And Not SF_FileSystem._IsDocFileSystem(psFile) Then
		_ConvertFromUrl = Replace(Replace( _
							ConvertFromUrl(Replace(Replace(psFile, &quot;?&quot;, cstQuestion), &quot;*&quot;, cstStar)) _
							, cstQuestion, &quot;?&quot;), cstStar, &quot;*&quot;)
	Else
		_ConvertFromUrl = psFile
	End If

End Function	&apos;	ScriptForge.FileSystem._ConvertFromUrl

REM -----------------------------------------------------------------------------
Private Function _ConvertToUrl(psFile) As String
&apos;&apos;&apos;	Execute the builtin ConvertToUrl function only when relevant
&apos;&apos;&apos;		i.e. when FileNaming (how arguments and return values are provided) &lt;&gt; &quot;URL&quot;
&apos;&apos;&apos;	Called at the top of methods receiving file names as arguments
&apos;&apos;&apos;	Remarks:	psFile might contain wildcards
&apos;&apos;&apos;				Files from document&apos;s file systems are never converted

	If SF_FileSystem.FileNaming = &quot;URL&quot; Or SF_FileSystem._IsDocFileSystem(psFile) Then
		_ConvertToUrl = psFile
	Else
		&apos;	ConvertToUrl() encodes &quot;?&quot;
		_ConvertToUrl = Replace(ConvertToUrl(psFile), &quot;%3F&quot;, &quot;?&quot;)
	End If

End Function	&apos;	ScriptForge.FileSystem._ConvertToUrl

REM -----------------------------------------------------------------------------
Private Function _CopyMove(psMethod As String _
								, psSource As String _
								, psDestination As String _
								, pbOverWrite As Boolean _
								) As Boolean
&apos;&apos;&apos;	Checks the arguments and executes the given method
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		psMethod: CopyFile/CopyFolder or MoveFile/MoveFolder
&apos;&apos;&apos;		psSource: Either File/FolderName
&apos;&apos;&apos;					or NamePattern which can include wildcard characters, for one or more files/folders to be copied
&apos;&apos;&apos;		psDestination: FileName  or FolderName for copy/move of a single file/folder
&apos;&apos;&apos;						Otherwise a destination FolderName. If it does not exist, it is created
&apos;&apos;&apos;		pbOverWrite: If True, files/folders may be overwritten
&apos;&apos;&apos;						Must be False for Move operations
&apos;&apos;&apos;	Next checks are done:
&apos;&apos;&apos;		With wildcards (multiple files/folders):
&apos;&apos;&apos;			- Parent folder of source must exist
&apos;&apos;&apos;			- Destination must not be a file
&apos;&apos;&apos;			- Parent folder of Destination must exist
&apos;&apos;&apos;			- If the Destination folder does not exist a new folder is created,
&apos;&apos;&apos;			- At least one file matches the wildcards expression
&apos;&apos;&apos;			- Destination files/folder must not exist if pbOverWrite = False
&apos;&apos;&apos;			- Destination files/folders must not have the read-only attribute set
&apos;&apos;&apos;			- Destination files must not be folders, destination folders must not be files
&apos;&apos;&apos;		Without wildcards (single file/folder):
&apos;&apos;&apos;			- Source file/folder must exist and be a file/folder
&apos;&apos;&apos;			- Parent folder of Destination must exist
&apos;&apos;&apos;			- Destination must not be an existing folder/file
&apos;&apos;&apos;			- Destination file/folder must not exist if pbOverWrite = False
&apos;&apos;&apos;			- Destination file must not have the read-only attribute set

Dim bCopyMove As Boolean		&apos;	Return value
Dim bCopy As Boolean			&apos;	True if Copy, False if Move
Dim bFile As Boolean			&apos;	True if File, False if Folder
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess
Dim bWildCards As Boolean		&apos;	True if wildcards found in Source
Dim bCreateFolder As Boolean	&apos;	True when the destination folder should be created
Dim bDestExists As Boolean		&apos;	True if destination exists
Dim sSourceUrl As String		&apos;	Alias for Source
Dim sDestinationUrl As String	&apos;	Alias for Destination
Dim sDestinationFile As String	&apos;	Destination FileName
Dim sParentFolder As String		&apos;	Parent folder of Source
Dim vFiles As Variant			&apos;	Array of candidates for copy/move
Dim sFile As String				&apos;	Single file/folder
Dim sName As String				&apos;	Name (last component) of file
Dim i As Long

	&apos;	Error handling left to calling routine
	bCopyMove = False
	bCopy = ( Left(psMethod, 4) = &quot;Copy&quot; )
	bFile = ( Right(psMethod, 4) = &quot;File&quot; )
	bWildCards = ( InStr(psSource, &quot;*&quot;) + InStr(psSource, &quot;?&quot;) + InStr(psSource, &quot;%3F&quot;) &gt; 0 )	&apos;ConvertToUrl() converts sometimes &quot;?&quot; to &quot;%3F&quot;
	bDestExists = False

	With SF_FileSystem

Check:
		If bWildCards Then
			sParentFolder = .GetParentFolderName(psSource)
			If Not .FolderExists(sParentFolder) Then GoTo CatchNoMatch
			If .FileExists(psDestination) Then GoTo CatchFileNotFolder
			If Not .FolderExists(.GetParentFolderName(psDestination)) Then GoTo CatchDestFolderNotExists
			bCreateFolder = Not .FolderExists(psDestination)
		Else
			Select Case bFile
				Case True 	&apos; File
					If Not .FileExists(psSource) Then GoTo CatchFileNotExists
					If Not .FolderExists(.GetParentFolderName(psDestination)) Then GoTo CatchDestFolderNotExists
					If .FolderExists(psDestination) Then GoTo CatchFolderNotFile
					bDestExists = .FileExists(psDestination)
					If pbOverWrite = False And bDestExists = True Then GoTo CatchDestinationExists
					bCreateFolder = False
				Case False	&apos; Folder
					If Not .FolderExists(psSource) Then GoTo CatchSourceFolderNotExists
					If Not .FolderExists(.GetParentFolderName(psDestination)) Then GoTo CatchDestFolderNotExists
					If .FileExists(psDestination) Then GoTo CatchFileNotFolder
					bDestExists = .FolderExists(psDestination)
					If pbOverWrite = False And bDestExists Then GoTo CatchDestinationExists
					bCreateFolder = Not bDestExists
			End Select
		End If

Try:
		Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
		If bWildCards Then
			If bFile Then vFiles = .Files(sParentFolder, .GetName(psSource)) Else vFiles = .SubFolders(sParentFolder, .GetName(psSource))
			If UBound(vFiles) &lt; 0 Then GoTo CatchNoMatch
			&apos;	Go through the candidates
			If bCreateFolder Then .CreateFolder(psDestination)
			For i = 0 To UBound(vFiles)
				sFile = vFiles(i)
				sDestinationFile = .BuildPath(psDestination, .GetName(sFile))
				If bFile Then bDestExists = .FileExists(sDestinationFile) Else bDestExists = .FolderExists(sDestinationFile)
				If pbOverWrite = False Then
					If bDestExists Then GoTo CatchDestinationExists
					If .FolderExists(sDestinationFile) Then GoTo CatchDestinationExists
				End If
				sSourceUrl = ._ConvertToUrl(sFile)
				sDestinationUrl = ._ConvertToUrl(sDestinationFile)
				If bDestExists Then
					If oSfa.isReadOnly(sDestinationUrl) Then GoTo CatchDestinationReadOnly
				End If
				Select Case bCopy
					Case True	:	oSfa.copy(sSourceUrl, sDestinationUrl)
					Case False	:	oSfa.move(sSourceUrl, sDestinationUrl)
				End Select
			Next i
		Else
			sSourceUrl = ._ConvertToUrl(psSource)
			sDestinationUrl = ._ConvertToUrl(psDestination)
			If bDestExists Then
				If oSfa.isReadOnly(sDestinationUrl) Then GoTo CatchDestinationReadOnly
			End If
			If bCreateFolder Then .CreateFolder(psDestination)
			Select Case bCopy
				Case True	:	oSfa.copy(sSourceUrl, sDestinationUrl)
				Case False	:	oSfa.move(sSourceUrl, sDestinationUrl)
			End Select
		End If

	End With

	bCopyMove = True

Finally:
	_CopyMove = bCopyMove
	Exit Function
CatchFileNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;Source&quot;, psSource)
	GoTo Finally
CatchSourceFolderNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFOLDERERROR, &quot;Source&quot;, psSource)
	GoTo Finally
CatchDestFolderNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFOLDERERROR, &quot;Destination&quot;, psDestination)
	GoTo Finally
CatchFolderNotFile:
	SF_Exception.RaiseFatal(NOTAFILEERROR, &quot;Destination&quot;, psDestination)
	GoTo Finally
CatchDestinationExists:
	SF_Exception.RaiseFatal(OVERWRITEERROR, &quot;Destination&quot;, psDestination)
	GoTo Finally
CatchNoMatch:
	SF_Exception.RaiseFatal(NOFILEMATCHERROR, &quot;Source&quot;, psSource)
	GoTo Finally
CatchFileNotFolder:
	SF_Exception.RaiseFatal(NOTAFOLDERERROR, &quot;Destination&quot;, psDestination)
	GoTo Finally
CatchDestinationReadOnly:
	SF_Exception.RaiseFatal(READONLYERROR, &quot;Destination&quot;, Iif(bWildCards, sDestinationFile, psDestination))
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem._CopyMove

REM -----------------------------------------------------------------------------
Public Function _CountTextLines(ByVal psFileName As String _
									, Optional ByVal pbIncludeBlanks As Boolean _
									) As Long
&apos;&apos;&apos;	Convenient function to count the number of lines in a textfile
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		psFileName: the file in FileNaming notation
&apos;&apos;&apos;		pbIncludeBlanks: if True (default), zero-length lines are included
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The number of lines, f.i. to ease array sizing. -1 if file reading error

Dim lLines As Long			&apos;	Return value
Dim oFile As Object			&apos;	File handler
Dim sLine As String			&apos;	The last line read

Try:
	lLines = 0
	If IsMissing(pbIncludeBlanks) Then pbIncludeBlanks = True
	Set oFile = SF_FileSystem.OpenTextFile(psFileName, ForReading)
	With oFile
		If Not IsNull(oFile) Then
			Do While Not .AtEndOfStream
				sLine = .ReadLine()
				lLines = lLines + Iif(Len(sLine) &gt; 0 Or pbIncludeBlanks, 1, 0)
			Loop
		End If
		.CloseFile()
		Set oFile = .Dispose()
	End With

Finally:
	_CountTextLines = lLines
	Exit Function
End Function	&apos;	ScriptForge.SF_FileSystem._CountTextLines

REM -----------------------------------------------------------------------------
Private Function _Delete(psMethod As String _
								, psFile As String _
								) As Boolean
&apos;&apos;&apos;	Checks the argument and executes the given psMethod
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		psMethod: CopyFile/CopyFolder or MoveFile/MoveFolder
&apos;&apos;&apos;		psFile: Either File/FolderName
&apos;&apos;&apos;					or NamePattern which can include wildcard characters, for one or more files/folders to be deleted
&apos;&apos;&apos;	Next checks are done:
&apos;&apos;&apos;		With wildcards (multiple files/folders):
&apos;&apos;&apos;			- Parent folder of File must exist
&apos;&apos;&apos;			- At least one file matches the wildcards expression
&apos;&apos;&apos;			- Files or folders to delete must not have the read-only attribute set
&apos;&apos;&apos;		Without wildcards (single file/folder):
&apos;&apos;&apos;			- File/folder must exist and be a file/folder
&apos;&apos;&apos;			- A file or folder to delete must not have the read-only attribute set

Dim bDelete As Boolean			&apos;	Return value
Dim bFile As Boolean			&apos;	True if File, False if Folder
Dim oSfa As Object				&apos;	com.sun.star.ucb.SimpleFileAccess
Dim bWildCards As Boolean		&apos;	True if wildcards found in File
Dim sFileUrl As String			&apos;	Alias for File
Dim sParentFolder As String		&apos;	Parent folder of File
Dim vFiles As Variant			&apos;	Array of candidates for deletion
Dim sFile As String				&apos;	Single file/folder
Dim sName As String				&apos;	Name (last component) of file
Dim i As Long

	&apos;	Error handling left to calling routine
	bDelete = False
	bFile = ( Right(psMethod, 4) = &quot;File&quot; )
	bWildCards = ( InStr(psFile, &quot;*&quot;) + InStr(psFile, &quot;?&quot;) + InStr(psFile, &quot;%3F&quot;) &gt; 0 )	&apos;ConvertToUrl() converts sometimes &quot;?&quot; to &quot;%3F&quot;

	With SF_FileSystem

Check:
		If bWildCards Then
			sParentFolder = .GetParentFolderName(psFile)
			If Not .FolderExists(sParentFolder) Then GoTo CatchNoMatch
		Else
			Select Case bFile
				Case True 	&apos; File
					If .FolderExists(psFile) Then GoTo CatchFolderNotFile
					If Not .FileExists(psFile) Then GoTo CatchFileNotExists
				Case False	&apos; Folder
					If .FileExists(psFile) Then GoTo CatchFileNotFolder
					If Not .FolderExists(psFile) Then GoTo CatchFolderNotExists
			End Select
		End If

Try:
		Set oSfa = SF_Utils._GetUnoService(&quot;FileAccess&quot;)
		If bWildCards Then
			If bFile Then vFiles = .Files(sParentFolder) Else vFiles = .SubFolders(sParentFolder)
			&apos;	Select candidates
			For i = 0 To UBound(vFiles)
				If Not SF_String.IsLike(.GetName(vFiles(i)), .GetName(psFile)) Then vFiles(i) = &quot;&quot;
			Next i
			vFiles = SF_Array.TrimArray(vFiles)
			If UBound(vFiles) &lt; 0 Then GoTo CatchNoMatch
			&apos;	Go through the candidates
			For i = 0 To UBound(vFiles)
				sFile = vFiles(i)
				sFileUrl = ._ConvertToUrl(sFile)
				If oSfa.isReadOnly(sFileUrl) Then GoTo CatchReadOnly
				oSfa.kill(sFileUrl)
			Next i
		Else
			sFileUrl = ._ConvertToUrl(psFile)
			If oSfa.isReadOnly(sFileUrl) Then GoTo CatchReadOnly
			oSfa.kill(sFileUrl)
		End If

	End With

	bDelete = True

Finally:
	_Delete = bDelete
	Exit Function
CatchFolderNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFOLDERERROR, &quot;FolderName&quot;, psFile)
	GoTo Finally
CatchFileNotExists:
	SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot;, psFile)
	GoTo Finally
CatchFolderNotFile:
	SF_Exception.RaiseFatal(NOTAFILEERROR, &quot;FileName&quot;, psFile)
	GoTo Finally
CatchNoMatch:
	SF_Exception.RaiseFatal(NOFILEMATCHERROR, Iif(bFile, &quot;FileName&quot;, &quot;FolderName&quot;), psFile)
	GoTo Finally
CatchFileNotFolder:
	SF_Exception.RaiseFatal(NOTAFOLDERERROR, &quot;FolderName&quot;, psFile)
	GoTo Finally
CatchReadOnly:
	SF_Exception.RaiseFatal(READONLYERROR, Iif(bFile, &quot;FileName&quot;, &quot;FolderName&quot;), Iif(bWildCards, sFile, psFile))
	GoTo Finally
End Function	&apos;	ScriptForge.SF_FileSystem._Delete

REM -----------------------------------------------------------------------------
Private Function _GetConfigFolder(ByVal psFolder As String) As String
&apos;&apos;&apos;	Returns one of next configuration folders: see https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1util_1_1PathSubstitution.html
&apos;&apos;&apos;		inst =&gt; Installation path of LibreOffice
&apos;&apos;&apos;		prog =&gt; Program path of LibreOffice
&apos;&apos;&apos;		user =&gt; The user installation/config directory
&apos;&apos;&apos;		work =&gt; The work directory of the user. Under Windows this would be the &quot;MyDocuments&quot; subdirectory. Under Unix this would be the home-directory
&apos;&apos;&apos;		home =&gt; The home directory of the user. Under Unix this would be the home- directory. 
&apos;&apos;&apos;				Under Windows this would be the CSIDL_PERSONAL directory, for example &quot;Documents and Settings\&lt;username&gt;\Documents&quot;
&apos;&apos;&apos;		temp =&gt; The current temporary directory

Dim oSubst As Object		&apos;	com.sun.star.util.PathSubstitution
Dim sConfig As String		&apos;	Return value

	sConfig = &quot;&quot;
	Set oSubst = SF_Utils._GetUNOService(&quot;PathSubstitution&quot;)
	If Not IsNull(oSubst) Then sConfig = oSubst.getSubstituteVariableValue(&quot;$(&quot; &amp; psFolder &amp; &quot;)&quot;) &amp; &quot;/&quot;

	_GetConfigFolder = SF_FileSystem._ConvertFromUrl(sConfig)

End Function	&apos;	ScriptForge.FileSystem._GetConfigFolder

REM -----------------------------------------------------------------------------
Public Function _IsDocFileSystem(psFile As String) As Boolean
&apos;&apos;&apos;	Returns True when the argument designates a document&apos;s internal file system

	_IsDocFileSystem = SF_String.StartsWith(psFile, DOCFILESYSTEM, CaseSensitive := True)

End Function	&apos;	ScriptForge.SF_FileSystem._IsDocFileSystem

REM -----------------------------------------------------------------------------
Private Sub _ScanFolder(ByVal piTarget As Integer _
							, ByRef psItemsColl As String _
							, ByVal psFolderName As String _
							, ByRef poSfa As Object _
							, ByVal psFilter As String _
							, ByVal pbIncludeSubFolders As Boolean _
							)
&apos;&apos;&apos;	Scan a folder and, when requested, its subfolders recursively.
&apos;&apos;&apos;	The psItemsColl in-out argument concatenates, depending on the target,
&apos;&apos;&apos;	either all files or all folders found.
&apos;&apos;&apos;	The Sub calls itself recursively when relevant.
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		piTarget: 1 when caller routine = Files(), 2 when caller routine = SubFolders()
&apos;&apos;&apos;			It determines the type of items to collect: files or folders
&apos;&apos;&apos;		psItemsColl: the current and future list of folders or files (FileNaming format) separated with cstSEPARATOR
&apos;&apos;&apos;		psFolderName: the folder to scan (FileNaming format)
&apos;&apos;&apos;		poSfa: com.sun.star.ucb.SimpleFileAccess
&apos;&apos;&apos;		psFilter: contains wildcards (&quot;?&quot; and &quot;*&quot;) to limit the list to the relevant folders or files.
&apos;&apos;&apos;			Zero-length string when not applicable.
&apos;&apos;&apos;		pbIncludeSubfolders: when True, subfolders are explored too.

Dim vSubFolders As Variant			&apos;	Array of subfolders 1st level in URL notation
Dim vFiles As Variant				&apos;	Array of files present in psFolderName in FileNaming notation
Dim lFiles As Long					&apos;	Number of files found passing the filter
Dim sFolderName As String			&apos;	URL alias for psFolderName
Dim sItem As String					&apos;	Single folder or single file in FileNaming notation
Dim sItemName As String				&apos;	Base name of sItem
Dim bFolder As Boolean				&apos;	When True, the considered string points to a folder, not a file
Dim bFilter As Boolean				&apos;	When True, no filter or the filter is passed
Dim i As Long		

Check:
	On Local Error Goto catch

Try:
	With poSfa

		&apos;	Get SubFolders, initialize files list
		sFolderName = SF_FileSystem._ConvertToUrl(psFolderName)
		vSubFolders = .getFolderContents(sFolderName, True)
		If UBound(vSubFolders) &lt; 0 Then Exit Sub
		vFiles = Array()
		If piTarget = cstFiles Then
			lFiles = 0
			ReDim vFiles(0 To UBound(vSubFolders))
		End If

		&apos;	List includes files: remove them or adjust notations of folders
		&apos;	When piTarget = cstFiles, the list of files is stored in the vFiles() array
		For i = 0 To UBound(vSubFolders)
			sItem = SF_FileSystem._ConvertFromUrl(vSubFolders(i))
			bFolder = .isFolder(vSubFolders(i))
			Select Case piTarget
				Case cstFiles
					If bFolder Then
						vSubFolders(i) = sItem &amp; &quot;/&quot;
					Else
						&apos;	Build list of files passing the filter
						bFilter = ( Len(psFilter) = 0 )
						If Not bFilter Then
							sItemName = SF_FileSystem.GetName(sItem)
							bFilter = SF_String.IsLike(sItemName, psFilter)
						End If
						If bFilter Then			&apos;	Copy files from folders + files list
							vFiles(lFiles) = sItem
							lFiles = lFiles + 1
						End If
						vSubFolders(i) = &quot;&quot;		&apos;	Keep folders only
					End If
				Case cstFolders
					If bFolder Then vSubFolders(i) = sItem &amp; &quot;/&quot; Else vSubFolders(i) = &quot;&quot;
					&apos;	Reduce list to those passing the filter
					If Len(psFilter) &gt; 0 And Len(vSubFolders(i)) &gt; 0 Then
						sItemName = SF_FileSystem.GetName(sItem)
						If Not SF_String.IsLike(sItemName, psFilter) Then vSubFolders(i) = &quot;&quot;
					End If
			End Select
		Next i
		vSubFolders = SF_Array.TrimArray(vSubFolders)

		&apos;	Store the list of either files or subfolders in the global collection
		Select Case piTarget
			Case cstFiles
				If lFiles &gt; 0 Then
					ReDim Preserve vFiles(0 To lFiles - 1)
					psItemsColl = psItemsColl &amp; cstSEPARATOR &amp; Join(vFiles, cstSEPARATOR)
				End If
			Case cstFolders
				If UBound(vSubFolders) &gt;= 0 Then psItemsColl = psItemsColl &amp; cstSEPARATOR &amp; Join(vSubFolders, cstSEPARATOR)
		End Select

		&apos;	Scan each subfolder when relevant
		If pbIncludeSubfolders Then
			For i = 0 To UBound(vSubFolders)
				_ScanFolder(piTarget, psItemsColl, vSubFolders(i), poSfa, psFilter, True)
			Next i
		End If

	End With

Finally:
	Exit Sub
Catch:
	SF_Exception.Clear()
	psItemsColl = &quot;&quot;
	GoTo Finally
End Sub			&apos;	ScriptForge.SF_FileSystem._ScanFolder

REM -----------------------------------------------------------------------------
Public Function _ParseUrl(psUrl As String) As Object
&apos;&apos;&apos;	Returns a com.sun.star.util.URL structure based on the argument

Dim oParse As Object					&apos;	com.sun.star.util.URLTransformer
Dim bParsed As Boolean					&apos;	True if parsing is successful
Dim oUrl As New com.sun.star.util.URL	&apos;	Return value

	oUrl.Complete = psUrl
	Set oParse = SF_Utils._GetUNOService(&quot;URLTransformer&quot;)
	bParsed = oParse.parseStrict(oUrl, &quot;&quot;)
	If bParsed Then oUrl.Path = ConvertToUrl(oUrl.Path)

	Set _ParseUrl = oUrl

End Function	&apos;	ScriptForge.SF_FileSystem._ParseUrl

REM -----------------------------------------------------------------------------
Public Function _SFInstallFolder() As String
&apos;&apos;&apos;	Returns the installation folder of the ScriptForge library
&apos;&apos;&apos;	Either:
&apos;&apos;&apos;		- The library is present in [My Macros &amp; Dialogs]
&apos;&apos;&apos;			($config)/basic/ScriptForge
&apos;&apos;&apos;		- The library is present in [LibreOffice Macros &amp; Dialogs]
&apos;&apos;&apos;			($install)/share/basic/ScriptForge

Dim sFolder As String		&apos;	Folder

	_SFInstallFolder = &quot;&quot;

	sFolder = BuildPath(ConfigFolder, &quot;basic/ScriptForge&quot;)
	If Not FolderExists(sFolder) Then
		sFolder = BuildPath(InstallFolder, &quot;share/basic/ScriptForge&quot;)
		If Not FolderExists(sFolder) Then Exit Function
	End If

	_SFInstallFolder = _ConvertFromUrl(sFolder)

End Function	&apos;	ScriptForge.SF_FileSystem._SFInstallFolder

REM ============================================ END OF SCRIPTFORGE.SF_FileSystem
</script:module>