office-gobmx/wizards/source/scriptforge/SF_FileSystem.xba
Jean-Pierre Ledure d5e1e4103b ScriptForge (SF_FileSystem) Fix typos
Typos in comment lines

Change-Id: I5e92417af0c9fb1f6f4d240a5a7731c9efa5230d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/154802
Reviewed-by: Jean-Pierre Ledure <jp@ledure.be>
Tested-by: Jenkins
2023-07-23 16:10:06 +02:00

2373 lines
No EOL
105 KiB
XML

<?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>