summaryrefslogtreecommitdiffstats
path: root/wizards/source/scriptforge/SF_FileSystem.xba
diff options
context:
space:
mode:
Diffstat (limited to 'wizards/source/scriptforge/SF_FileSystem.xba')
-rw-r--r--wizards/source/scriptforge/SF_FileSystem.xba2373
1 files changed, 2373 insertions, 0 deletions
diff --git a/wizards/source/scriptforge/SF_FileSystem.xba b/wizards/source/scriptforge/SF_FileSystem.xba
new file mode 100644
index 0000000000..e37d4d3dce
--- /dev/null
+++ b/wizards/source/scriptforge/SF_FileSystem.xba
@@ -0,0 +1,2373 @@
+<?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) 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 Mode = &quot;OPEN&quot; Then iMode = .FILEOPEN_SIMPLE Else iMode = .FILESAVE_AUTOEXTENSION
+ End With
+
+ &apos; Activate the filepicker dialog
+ Set oFileDialog = SF_Utils._GetUNOService(&quot;FilePicker&quot;)
+ 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 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; FreeText: text to display in the dialog. Default = &quot;&quot;
+&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;], [FreeText=&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(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(FreeText, &quot;FreeText&quot;, V_STRING) Then GoTo Finally
+ End If
+ If SF_FileSystem._IsDocFileSystem(DefaultFolder) Then GoTo CatchNotSupported
+ DefaultFolder = SF_FileSystem._ConvertToUrl(DefaultFolder)
+
+Try:
+ Set oFolderDialog = SF_Utils._GetUNOService(&quot;FolderPicker&quot;)
+ If Not IsNull(oFolderDialog) Then
+ With oFolderDialog
+ If Len(DefaultFolder) &gt; 0 Then .DisplayDirectory = ConvertToUrl(DefaultFolder)
+ .Description = 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> \ No newline at end of file