DC - Lua Scripting

# 2.15. Lua Scripting

## Content

1. Introduction
2. DLL required
3. Double Commander functions libraries
  3.1. DC library
    3.1.1. Example with the DC.ExecuteCommand
  3.2. System library
    3.2.1. Details on SysUtils.FileGetAttr returned value
    3.2.2. Example with SysUtils.FileGetAttr
    3.2.3. Example using FindFirst, FindNext and FindClose
  3.3. Clipboard library
    3.3.1. Example of usage clipboard library
  3.4. Dialogs library
    3.4.1. Buttons displayed in Dialogs.MessageBox
    3.4.2. Style of box of Dialogs.MessageBox
    3.4.3. Default active button of Dialogs.MessageBox
    3.4.4. Returned value of Dialogs.MessageBox
    3.4.5. Example of usage of the Dialogs.MessageBox
    3.4.6. Example of usage of the Dialogs.InputQuery
  3.5. UTF-8 library
  3.6. Char library
  3.7. OS library
4. Index

## 1. Introduction

Detailed information about the Lua scripting programming language can be found on the Lua website.

Double Commander can execute Lua scripts via cm_ExecuteScript command.
Script parameters must be passed as is, without escaping (without quotes or "\"), for this we need to use the %"0 variable: for example, `%"0%p0` for the file under cursor instead of `%p0` or `%"0%D` for the current directory instead of `%D`. Otherwise, if Double Commander automatically adds quotes, they will be passed as part of the parameter and you will have to take them into account.
To get a list of all selected files we can use variables (`%LU`, `%FU` or `%RU`) or internal commands (cm_SaveSelectionToFile, cm_SaveFileDetailsToFile, cm_CopyFullNamesToClip or cm_CopyFileDetailsToClip). We can use, for example, `%p`: in this case, Double Commander will pass the names of all selected files in one line, separating the names with a space.

It is also possible to write content plugins using Lua script, examples can be found in the program folder (`plugins/wdx/scripts`). The Wiki has a page dedicated to writing plugins. Limitations: only the following data types are supported

- ft_numeric_32 (1) – a 32-bit signed number;
- ft_numeric_64 (2) – a 64-bit signed number;
- ft_numeric_floating (3) – a floating point number;
- ft_boolean (6) – boolean type: true or false;
- ft_multiplechoice (7) – a value allowing a limited number of choices;
- ft_string (8) – a text string;
- ft_fulltext (9) – a full text (multiple text strings), used only for searching with plugins;
- ft_datetime (10) – for date/time: a returned date will be converted to a formatted date and time string (value depends on your regional settings).
  The date must be in Windows format, but Lua and the functions that Double Commander provides for searching files use Unix time format. To convert we can use the formula:
  = UnixTime * 10000000 + 116444736000000000
  This is a large number, so you will need a 64-bit version of Double Commander or Lua 5.3+.

The list above contains the names from the header files, in Lua scripts we must use the numeric values which are specified in parentheses.

About text encoding

All additional functions described below accept string parameters in UTF-8 encoding and return strings in this encoding (except for the LazUtf8.ConvertEncoding function).

Some functions from the standard Lua libraries have been replaced with functions from Double Commander or Free Pascal/Lazarus (or new ones have been written), this provides UTF-8 support.

When writing plugins, we should also use UTF-8 for text data (ft_multiplechoice, ft_string and ft_fulltext).

When saving scripts, use UTF-8 encoding without BOM.

Notes

Automation with Lua has great possibilities, but there may be nuances that in some cases need to be kept in mind. Let's try to collect them in this subsection.

1. If auto refresh and the Load file list in separate thread option are enabled, the refresh function will work asynchronously. At the same time, scripts are executed in the main thread of Double Commander and therefore, in some cases, all this may affect the operation of your script. For example, sometimes sequential execution of commands for navigation may not work (for example, large directories, slow disk), in this case try to disable Load file list in separate thread or look for an alternative solution.

If your script creates new files in the current panel or renames existing files, but then does not complete and performs some additional actions (for example, selecting files or moving the cursor), then in some cases these actions will not have an effect: not all files may be in the panel yet and you will need to first call the cm_Refresh command. Under the described conditions, `cm_Refresh` will also be executed asynchronously and Double Commander may not have time to completely refresh the list of files after your changes.

Auto-refreshing and loading the list of files in a separate thread are convenient functions for a file manager, so the stable working method was experimentally found to temporarily return control to the program and allow the file list to be completely refreshed:

    DC.ExecuteCommand("cm_Refresh")
    i = 10
    while i > 0 do
      SysUtils.Sleep(10)
      DC.ExecuteCommand("")
      i = i - 1
    end

2. Lua function `io.open` uses the standard C function `fopen`: in text mode, this function can convert the type of line endings (CRLF, LF or CR) when reading and writing and it can lead to unexpected results. If you come across files with different types of line endings or if you are writing a cross-platform script, this must be taken into account or it may be more practical to give preference to the binary mode.

3. For the file properties dialog in Linux and other Unix-like operating systems, the `ContentGetValue` function is called with the `CONTENT_DELAYIFSLOW` flag (the fourth parameter, the value is 1), this avoids the delay in opening the window: if data retrieval is slow, we can exclude this data by simply adding a flag value check and returning `nil` for such fields or plugin.

4. If the plugin should return an empty string, it will be faster to pass `nil` instead of `""`.

(back to top)

## 2. DLL required

In order to interpret Lua script file, we need to have a Lua DLL file, Double Commander supports versions 5.1 - 5.4.

We can use DLL file from LuaJIT project. LuaJIT combines a high-speed interpreter, written in assembler, with a state-of-the-art JIT compiler. Also we get FFI library, which allows calling external C functions and using C data structures from pure Lua code.

DC distributives for Windows have Lua DLL by default (in DC 0.9.7 and newer from LuaJIT project), in other cases we may find and install it through our packages manager or compile it. If we're using a 64-bits version of DC, the DLL must be the 64-bits version as well.

By default DC looks for a file with name `lua5.1.dll` (Windows), `liblua5.1.so.0` (Unix or GNU/Linux) or `liblua5.1.dylib` (macOS) in its directory and in the system directory. We can change the file name (and path) in the Lua library file to use parameter.

(back to top)

## 3. Double Commander functions libraries

Double Commander offer a few libraries of functions for our Lua scripts.

Here is the list of them.

List of libraries
Library name | Script name | Quick description
--- | --- | ---
Double Commander library | DC | Double Commander specific functions
System library | SysUtils | Various system functions
Clipboard library | Clipbrd | Provides external clipboard functionality
Dialogs library | Dialogs | Interacts with user
UTF-8 library | LazUtf8 | UTF-8 string functions
Char library | Char | Getting information about characters
OS library | os | Functions related with the operating system

(back to top)

## 3.1. DC library

This library contains Double Commander specific functions.

It provides all its functions inside the table `DC`.

DC library
Function name | Description
--- | ---
DC.LogWrite | DC.LogWrite(sMessage, iMsgType, bForce, bLogFile) Write a message to the log window: sMessage : The message text. iMsgType : The message type: 0 - information, 1 - success, 2 - error. bForce : A boolean, when true, will show the log window if invisible. bLogFile : A boolean, when true, will write the message also in the log file. The internal function of Double Commander for writing to the protocol works asynchronously (see notes in the introduction), so messages from `DC.LogWrite` will not be written to the protocol immediately, but after the script is completed. If it is necessary to write the text immediately, we can try to add the call `DC.ExecuteCommand("")` after `DC.LogWrite`.
DC.CurrentPanel | iPanel = DC.CurrentPanel() Get active panel: returns 0 if left panel is active or 1 if right. DC.CurrentPanel(iPanel) Set active panel: left panel if iPanel equal 0 or right if 1.
DC.ExecuteCommand | DC.ExecuteCommand(sCommand, Param1, Param2,...,ParamX) This allows the script to invoke internal commands of Double Commander. The sCommand is holding the actual internal command name. We may provide as many Param... as command may support.
DC.GoToFile | DC.GoToFile(sFileName, bActive) Opens the directory where sFileName is located and places the cursor on sFileName. To open a directory, use cm_ChangeDir or try to add the directory separator and ".." (without quotes) to sFileName. bActive is an optional parameter: function can work in the active file panel (`true`) or inactive one (`false`). `true` by default.
DC.GetPluginField | Result = DC.GetPluginField(sFileName, sPlugin, iField, iUnit) Allows to get data using the installed content plugin (WDX): sFileName : The full (absolute) filename. sPlugin : The plugin name, as it is saved in the corresponding settings section. iField : The plugin field index (counting from 0). iUnit : The unit index (counting from 0); specify 0 if it does not exist. Result : The return value: signed number (including time in Unix format), floating point number, boolean value, text string or `nil`.
DC.ExpandVar | sResult = DC.ExpandVar(String) Returns a string in which all variables are replaced with their values (excluding environment variables, use SysUtils.ExpandEnv for them).

In addition to internal commands, in scripts we can use the special command `cm_ExecuteToolBarItem`, this command allows to call toolbar buttons by their identifier (in the program, this function provides the use of hotkeys for toolbar buttons). The command is used similarly to ordinary internal commands (see examples below) and has the following parameters:

Parameter | Value | Description
--- | --- | ---
ToolBarID | TfrmOptionsToolbar | the button of the main toolbar
TfrmOptionsToolbarMiddle | the button of the middle toolbar
(absent) | the button of the main toolbar
ToolItemID | identifier | the unique identifier of the button

The unique identifier is stored in the `ID` tag and we have several ways to get it: we can find the button in the `doublecmd.xml` file, in the toolbar backup file, or simply copy the button to the clipboard and paste its code into a text editor.

Note: Identifiers are generated automatically and do not have to match the identifiers of similar buttons in another copy of the program, but if necessary, we can manually set our own value.

(back to top)

## 3.1.1. Example with the DC.ExecuteCommand

In this example, we wrote a simple script that will do the following:

1. focus to right panel
2. close all opened tabs
3. switch to a specific folder
4. focus the left panel
5. close all opened tabs
6. switch to a specific folder
7. open a new tab
8. switch to a specific folder

    -- 1. Focus on right panel.
    DC.ExecuteCommand("cm_FocusSwap", "side=right")

    -- 2. Close all tabs.
    DC.ExecuteCommand("cm_CloseAllTabs")

    -- 3. Switch to a specific directory.
    DC.ExecuteCommand("cm_ChangeDir", "E:\\FakeKey\\Documents\\Music")

    -- 4. Focus on left panel.
    DC.ExecuteCommand("cm_FocusSwap", "side=left")

    -- 5. Close all tabs.
    DC.ExecuteCommand("cm_CloseAllTabs")

    -- 6. Switch to a specific directory.
    DC.ExecuteCommand("cm_ChangeDir", "C:\\Users\\Public\\Music")

    -- 7. Open a new tab.
    DC.ExecuteCommand("cm_NewTab")

    -- 8. Switch to a specific directory.
    DC.ExecuteCommand("cm_ChangeDir", "E:\\VirtualMachines\\ShareFolder")

Using the internal command cm_ExecuteScript, we may configure a tool bar button that will execute our script.

Assuming this script file is `E:\scripts\lua\music.lua`, we could have the button configured this way:

Also, we may use the internal Double Commander Editor for editing our scripts. If filename has `.lua` file extension, it will be recognized by internal editor and it will provide us syntax highlighting specific for this Lua language:

(back to top)

## 3.2. System library

This library contains various system functions.

It provides all its functions inside the table `SysUtils`.

System library
Function name | Description
--- | ---
SysUtils.Sleep | SysUtils.Sleep(iMilliseconds) Suspends the execution of the script for the specified number of iMilliseconds. After the specified period has expired, script execution resumes.
SysUtils.GetTickCount | SysUtils.GetTickCount() Returns an increasing clock tick count. It is useful for time measurements, but no assumptions should be made as to the interval between the ticks.
SysUtils.FileExists | bExists = SysUtils.FileExists(sFileName) Check whether a particular file exists in the filesystem. Returns in bExists the value `true` if file with name sFileName exists on the disk, or `false` otherwise.
SysUtils.DirectoryExists | bExists = SysUtils.DirectoryExists(sDirectory) Checks whether sDirectory exists in the filesystem and is actually a directory. If this is the case, the function returns in bExists the value `true` otherwise `false` is returned.
SysUtils.FileGetAttr | iAttr = SysUtils.FileGetAttr(sFileName) Returns in iAttr the attribute settings of file sFileName. See the detail explanations of the returned value here.
SysUtils.FindFirst | Handle, FindData = SysUtils.FindFirst(sPath) Looks for files that match the sPath, generally with wildcards. If no file is found, Handle will be `nil`. When at least one item is found, the returned Handle may be used in subsequent `SysUtils.FindNext` to find other occurrences of the same pattern. The FindData table contains information about the file or directory found. The field of the FindData table are: Name : The file name (without path). Attr : The file attributes of the file (see details here). Size : The size of the file in bytes. Time : The time stamp of the file (seconds since Jan 01 1970)
SysUtils.FindNext | Result, FindData = SysUtils.FindNext(Handle) Finds the next occurrence of a search sequence initiated by `FindFirst` by re-using the Handle returned previously. Returned Result will be non-nil if a file or directory is found and will be `nil` otherwise. The same notes mentioned for `SysUtils.FindFirst` applied here. Remark: The last `SysUtils.FindNext` call must always be followed by a `SysUtils.FindClose` call with the same Handle. Failure to do so will result in memory leaks.
SysUtils.FindClose | SysUtils.FindClose(Handle) Ends a series of `SysUtils.FindFirst`/`SysUtils.FindNext` calls. Frees any memory used by these calls. It is absolutely necessary to do this call, or memory losses may occur.
SysUtils.CreateDirectory | bResult = SysUtils.CreateDirectory(sDirectory) Create a chain of directories, sDirectory is the full path to directory. Returns `true` if sDirectory already exist or was created successfully. If it failed to create any of the parts, `false` is returned.
SysUtils.RemoveDirectory | bResult = SysUtils.RemoveDirectory(sDirectory) Will delete the directory with the name sDirectory. Returns `true` if successful, `false` otherwise. Unlike os.remove, this function does not return a description in case of an error, but it can delete non-empty directories.
SysUtils.CreateHardLink | bResult = SysUtils.CreateHardLink(sFileName, sLinkName) Create the hard link sLinkName to file sFileName. Returns `true` if successful, `false` otherwise.
SysUtils.CreateSymbolicLink | bResult = SysUtils.CreateSymbolicLink(sFileName, sLinkName) Create the symbolic link sLinkName to file or directory sFileName. Returns `true` if successful, `false` otherwise.
SysUtils.ReadSymbolicLink | sTarget = SysUtils.ReadSymbolicLink(sLinkName, bRecursive) Read destination of the symbolic link sLinkName. If bRecursive is `true` and the link points to a link then it's resolved recursively until a valid file name that is not a link is found. Returns the path where the symbolic link sLinkName is pointing to or an empty string when the link is invalid or the file it points to does not exist and bRecursive is `true`.
SysUtils.ExtractFileName | sName = SysUtils.ExtractFileName(sFileName) Extract the filename part from a full path filename. The filename consists of all characters after the last directory separator character ("/" or "\") or drive letter.
SysUtils.ExtractFileExt | sExt = SysUtils.ExtractFileExt(sFileName) Return the extension from a filename (all characters after the last "." (dot), including the "." character).
SysUtils.ExtractFilePath | sPath = SysUtils.ExtractFilePath(sFileName) Extract the path from a filename (including drive letter). The path consists of all characters before the last directory separator character ("/" or "\"), including the directory separator itself.
SysUtils.ExtractFileDir | sDir = SysUtils.ExtractFileDir(sFileName) Extract only the directory part of sFileName, including a drive letter. The directory name has NO ending directory separator, in difference with `SysUtils.ExtractFilePath`.
SysUtils.ExtractFileDrive | sDrive = SysUtils.ExtractFileDrive(sFileName) Extract the drive part from a filename. Note that some operating systems do not support drive letters.
SysUtils.GetAbsolutePath | sName = SysUtils.GetAbsolutePath(sFileName, sBaseDirectory) Returns the absolute (full) path to the file: sFileName : The filename with a relative path. sBaseDirectory : The directory that was used as the base directory for sFileName. If the absolute path could not be obtained, the function will return the sFileName value.
SysUtils.GetRelativePath | sName = SysUtils.GetRelativePath(sFileName, sBaseDirectory) Returns the filename relative to the specified directory: sFileName : The full (absolute) filename. sBaseDirectory : The directory that will be used as the base directory sFileName. If sFileName and sBaseDirectory contain the same value, the function will return an empty string (""). If it was not possible to get the file name with a relative path, the function will return the sFileName value.
SysUtils.MatchesMask | bResult = SysUtils.MatchesMask(sFileName, sMask, iMaskOptions) Returns `true` if sFileName matches the passed mask sMask. iMaskOptions (optional parameter, 0 by default) is set as the sum of the following values:
Value | Description
--- | ---
1 | case sensitive
2 | ignore accents and ligatures
4 | Windows style filter: "*.*" also match files without extension, etc.
8 | enable pinyin support (file `pinyin.tbl` will be used)
SysUtils.MatchesMaskList | bResult = SysUtils.MatchesMaskList(sFileName, sMaskList, sSeparator, iMaskOptions) Returns `true` if sFileName matches at least one of passed masks sMaskList separated by sSeparator (";" by default). sSeparator and iMaskOptions (see above) are optional parameters.
SysUtils.Execute | bResult = SysUtils.Execute(sCommand) Will execute sCommand as it would be typed on the command-line and return `true` if successful and `false` otherwise (the function does not wait for the executed command to complete). Note: In Windows, the os.execute function opens a terminal window every time, but the `SysUtils.Execute` function does not have this drawback.
SysUtils.ExpandEnv | sResult = SysUtils.ExpandEnv(String, bSpecial) Returns a string in which all environment variables are replaced with their values for the current user. bSpecial is an optional parameter: if `true`, pseudo environment variables will also be expanded (see details here). `false` by default.
SysUtils.GetTempName | sTempFileName = SysUtils.GetTempName() Will return a filename to use as a temporary filename (in the system directory for the temporary files), similar to the os.tmpname function, but the file will be created in the `_dc~~~` subdirectory that is automatically deleted when Double Commander is closed. If the function could not create a unique name, it will return an empty string.
SysUtils.GetFileProperty | Result = SysUtils.GetFileProperty(sFileName, iValue) Returns the file property corresponding to iValue:
Value | Description
--- | ---
0 | File size in bytes
1 | String of attributes as in the file panel (for a detailed description of the string value, see here)
2 | Group of the file owner
3 | Owner of the file
4 | Modification date
5 | Creation date
6 | Last access date
7 | Status change date
8 | File type (as in Windows Explorer or MIME-type)
9 | Description (comment) from `descript.ion` (see details here)
Returns a number (file size, timestamps) or a string (in other cases). In case of failure, function returns `nil`.
SysUtils.FileSetTime | bResult = SysUtils.FileSetTime(sFileName, iModification, iCreation, iLastAccess) Allows to set timestamps (Unix time format) for sFileName: modification date, creation date and last access date. Specify zero for those timestamps that should be ignored. Returns `true` if successful, `false` otherwise.
SysUtils.PathDelim | SysUtils.PathDelim The character used by the current operating system to separate directory names in the full file name. In Unix/Linux system the directory separator will be "/" and in Windows it will be "\".

(back to top)

## 3.2.1. Details on SysUtils.FileGetAttr returned value

`FileGetAttr` returns the attribute settings of file sFileName.

The attribute is a OR-ed combination of the following constants:

Constants uses in SysUtils.FileGetAttr returned value
Value | Signification
--- | ---
0x00000001 faReadOnly | The file is read-only.
0x00000002 faHidden | The file is hidden. In Unix/Linux, this means that the filename starts with a dot.
0x00000004 faSysFile | The file is a system file. In Unix/Linux, this means that the file is a character or block device, a named pipe (FIFO).
0x00000008 faVolumeId | Volume Label. Only for DOS/Windows on a plain FAT (not VFAT or FAT32) filesystem.
0x00000010 faDirectory | File is a directory.
0x00000020 faArchive | File is archived. Not possible in Unix/Linux.
0x00000400 faSymLink | File is a symbolic link.
Note: In case of an error, -1 is returned.

See an example in the next section.

(back to top)

## 3.2.2. Example with SysUtils.FileGetAttr

This following script is an example of usage of the `SysUtils.FileGetAttr`.

When the parameter is detected to be a directory, it will open a new tab in the active panel and switch to it.

    local params = {...}
    local iAttr

    if #params == 1 then -- We got at least one parameter?
      iAttr = SysUtils.FileGetAttr(params[1])
      if iAttr > 0 then -- We got a valid attribute?
        if math.floor(iAttr / 0x00000010) % 2 ~= 0 then
          -- bit 4 is set? So it's a directory.
          DC.ExecuteCommand("cm_NewTab")
          DC.ExecuteCommand("cm_ChangeDir", params[1])
        end
      end
    end

In the above example, the params[1] is the 1st parameter passed to the script.

When using the internal command cm_ExecuteScript, it will will be the first parameter passed after the script filename.

So in our example, we may program a sample toolbar button like the following:

In this example, the parameter `%"0%p` will be passed to the script. This will represent, unquoted, the filename of the item currently selected in the active panel at the moment we press the toolbar button.

(back to top)

## 3.2.3. Example using FindFirst, FindNext and FindClose

In the following script example, we'll scan the content of the directory we received in parameter and store resulting data into a text file with the filename passed as a second parameter.

This will give us a good idea of the usage of `FindFirst`, `FindNext` and `FindClose`.

    local params = {...}

    if #params == 2 then -- We got our 2 parameters?
      local Result = nil
      local hOutputFile = nil

      hOutputFile = io.output(params[2])

      local Handle, FindData = SysUtils.FindFirst(params[1] .. "\\*")
      if Handle ~= nil then
        repeat
          io.write(FindData.Name .. "\r")
          io.write(FindData.Size .. "\r")
          io.write("---------------\r")

          Result, FindData = SysUtils.FindNext(Handle)
        until Result == nil

        SysUtils.FindClose(Handle)
        io.close(hOutputFile)
      end
    end

In the above example, we need to pass two parameters to our script:

1. params[1] - which is the directory we want the content
2. params[2] - which is the outputfilename to store the result

So it's easy to configure a toolbar button using the internal command cm_ExecuteScript and pass the parameter to accomplish all this.

In this example, the parameter `%"0%Ds` will be passed to the script as the first parameter. This will represent, unquoted, the directory displayed by the active panel.

(back to top)

## 3.3. Clipboard library

Double Commander may provide external clipboard functionality to our Lua scripts.

Following table gives us the related functions:

Clipboard library
Function name | Description
--- | ---
Clipbrd.Clear | Clipbrd.Clear() Clear the content of the clipboard.
Clipbrd.GetAsText | sVar = Clipbrd.GetAsText() Get the current text content of the clipboard to assigned it to sVar. If the clipboard does not contain text, the function returns an empty string.
Clipbrd.SetAsText | Clipbrd.SetAsText(sVar) Store in the clipboard the text content of sVar.
Clipbrd.SetAsHtml | Clipbrd.SetAsHtml(sHtml) Adds html-formatted text sHtml to the clipboard (`CF_HTML` clipboard format). This contents will be inserted in applications which support this clipboard format, like MS Word, LO Writer, etc. It's correct to store data with both `Clipbrd.SetAsText` and `Clipbrd.SetAsHtml`. When we'll paste, the application will use the best one that it supports. For example we may have this: `Clipbrd.SetAsText("Welcome to Double Commander!")` `Clipbrd.SetAsHtml("Welcome to <b>Double Commander</b>!")` If we switch to Notepad attempting to paste something, it will paste in plain text the message we copied with `Clipbrd.SetAsText`. But if we switch to Microsoft Word and paste something, it will paste the second one, the one with Double Commander in bold since the Microsoft Word recognize and support that clipboard content type.

(back to top)

## 3.3.1. Example of usage clipboard library

The following example is using three functions related with the clipboard: `Clear`, `GetAsText` and `SetAsText`.

It's a relative long script but it's good to put together a few functions we've seen above.

It assumes our active panel is currently into a directory with many source text files.

It also assumes we currently have in clipboard a single word and that it will receive as a single parameter the current active folder.

The script will scan the file in that current level of directory and will read the content of them one by one to detect text line that contains the word that was in clipboard.

Then, the filenames of the files that contain at least one line with that word will be place into the clipboard.

Then, the script will use the internal command cm_LoadSelectionFromClip and the files that have the words will then be selected.

Also, at the end, we put back in our clipboard the original word that needed to be searched.

    local params = {...}
    local Result = nil
    local iAttr
    local bFound = false
    local sCompleteFilename = ""
    local hInputFile = nil
    local sLine = ""
    local iPosS
    local iPosE
    local sFileToSelect = ""
    local sSearchString = ""

    if #params == 1 then -- We got our parameter?
      sSearchString = Clipbrd.GetAsText() -- Get the expression to search.
      Clipbrd.Clear() -- Making sure we have nothing in clipboard.
      DC.ExecuteCommand("cm_MarkUnmarkAll") -- Make sure nothing is selected.

      -- Let's scan one by one all the files of our directory.
      local Handle, FindData = SysUtils.FindFirst(params[1] .. "\\*")
      if Handle ~= nil then
        repeat
          sCompleteFilename = params[1] .. "\\" .. FindData.Name
          iAttr = SysUtils.FileGetAttr(sCompleteFilename)
          if iAttr > 0 then -- We got a valid attribute?
            -- We need file, not directory!
            if math.floor(iAttr / 0x00000010) % 2 == 0 then

              -- Let's now read the file line by line until the the end OR a found.
              hInputFile = io.open(sCompleteFilename, "r")
              bFound = false

              while bFound == false do
                sLine = hInputFile:read()
                if sLine == nil then break end
                iPosS, iPosE = string.find(sLine, sSearchString)
                if iPosS ~= nil then bFound = true end
              end

              if bFound == true then
                sFileToSelect = sFileToSelect .. FindData.Name .. "\n"
              end

              io.close(hInputFile)
            end
          end
          Result, FindData = SysUtils.FindNext(Handle)
        until Result == nil

        SysUtils.FindClose(Handle)
      end

      -- If we've found something, select it!
      if sFileToSelect ~= "" then
        Clipbrd.SetAsText(sFileToSelect)
        DC.ExecuteCommand("cm_LoadSelectionFromClip")
      end

      Clipbrd.SetAsText(sSearchString) -- Restoring what we had in clipboard.
    end

(back to top)

## 3.4. Dialogs library

This library allows our scripts to interact with user to display message, prompt for answers, etc.

Following table gives us the related functions:

Dialogs library
Function name | Description
--- | ---
Dialogs.MessageBox | iButton = Dialogs.MessageBox(sMessage, sTitle, iFlags) Will display a message box prompting a user to click a button which will be returned by the function: sMessage : The message inside the box. sTitle : The text displayed as the title of the box. iFlags : OR'ed value of constants to determine the buttons that will be displayed, the style of window and default button selected for the answer. See the following table the buttons displayed, style of window or default button. iButton : Returned value indicating the button user pressed (see this table).
Dialogs.InputQuery | bResult, sAnswer = Dialogs.InputQuery(sTitle, sMessage, bMask, sDefault) Will display a requester box where user may enter a string value: sTitle : The text displayed as the title of the box. sMessage : The message inside the box. bMask : A boolean, when true, will display "stars" to hide characters. sDefault : The default suggested text that user may type over if necessary. bResult : Returned boolean indicating if user effectively enter something or not. sAnswer : Returned string when user entered something and then clicked ok.
Dialogs.InputListBox | sItem, iItem = Dialogs.InputListBox(sTitle, sMessage, aItems, sDefault) Displays a dialog box to allow the user to choose from a list of items: sTitle : The text displayed as the title of the dialog. sMessage : The message inside the dialog. aItems : A Lua table, each element of the table must be a string. sDefault : The default selected item in the list. sItem : Returned the selected item as a string or `nil` if the dialog is dismissed. iItem : Index of the selected item (counting from one, as is customary in Lua tables).

(back to top)

## 3.4.1. Buttons displayed in Dialogs.MessageBox

The buttons displayed in the box displayed by `Dialogs.MessageBox` function are controlled by a OR'ed value with one of the following:

Constant of ButFlags regarding the buttons displayed of Dialogs.MessageBox
Constant value | Buttons displayed, from left to right
--- | ---
0x0000 MB_OK |
0x0001 MB_OKCANCEL |
0x0002 MB_ABORTRETRYIGNORE |
0x0003 MB_YESNOCANCEL |
0x0004 MB_YESNO |
0x0005 MB_RETRYCANCEL |

(back to top)

## 3.4.2. Style of box of Dialogs.MessageBox

The style of the box displayed by `Dialogs.MessageBox` function are controlled by a OR'ed value with one of the following:

Constant of ButFlags regarding the icon and style of Dialogs.MessageBox
Constant value | Style of window
--- | ---
0x0040 MB_ICONINFORMATION | Informative window
0x0030 MB_ICONWARNING | Warning window
0x0020 MB_ICONQUESTION | Confirmation window
0x0010 MB_ICONERROR | Error window

(back to top)

## 3.4.3. Default active button of Dialogs.MessageBox

The default active button of the box displayed by `Dialogs.MessageBox` function are controlled by a OR'ed value with one of the following:

Constant of ButFlags regarding the default button of Dialogs.MessageBox
Constant value | Default button
--- | ---
0x0000 MB_DEFBUTTON1 | Default will be the first one on left
0x0100 MB_DEFBUTTON2 | Default will be the second one from left
0x0200 MB_DEFBUTTON3 | Default will be the third one from left

(back to top)

## 3.4.4. Returned value of Dialogs.MessageBox

The number returned by the `Dialogs.MessageBox` function represent the button user has pressed according to the following:

ButPressed value returned based on button pressed of Dialogs.MessageBox
Constant value | Button pressed
--- | ---
0x0000 mrNone | No button pressed
0x0001 mrOK |
0x0002 mrCancel |
0x0003 mrAbort |
0x0004 mrRetry |
0x0005 mrIgnore |
0x0006 mrYes |
0x0007 mrNo |

Note: If we press the "x" in top right or press `Esc` to close the window, then the value of the button "Cancel" is will returned.

(back to top)

## 3.4.5. Example of usage of the Dialogs.MessageBox

Here is a little script using `Dialogs.MessageBox` and the resulting window that will be displayed:

    -- Buttons displayed
    MB_OK = 0x0000
    MB_OKCANCEL = 0x0001
    MB_ABORTRETRYIGNORE = 0x0002
    MB_YESNOCANCEL = 0x0003
    MB_YESNO = 0x0004
    MB_RETRYCANCEL = 0x0005

    -- Box style
    MB_ICONINFORMATION = 0x0040
    MB_ICONWARNING = 0x0030
    MB_ICONQUESTION = 0x0020
    MB_ICONERROR = 0x0010

    -- Default button
    MB_DEFBUTTON1 = 0x0000
    MB_DEFBUTTON2 = 0x0100
    MB_DEFBUTTON3 = 0x0200

    -- Returned button pressed
    mrNone = 0x0000
    mrOK = 0x0001
    mrCancel = 0x0002
    mrAbort = 0x0003
    mrRetry = 0x0004
    mrIgnore = 0x0005
    mrYes = 0x0006
    mrNo = 0x0007

    iFlags = MB_YESNO + MB_ICONQUESTION + MB_DEFBUTTON2
    iButton = Dialogs.MessageBox("Do you want to quit?", "Question", iFlags)

    if iButton == mrYes then
      DC.ExecuteCommand("cm_Exit")
    end

(back to top)

## 3.4.6. Example of usage of the Dialogs.InputQuery

Here is a little script using `Dialogs.InputQuery` and the resulting window that will be displayed:

    bResult, sAnswer = Dialogs.InputQuery("Identification", "Enter your name:", false, "John")

    if bResult == true then
      Dialogs.MessageBox("Hello " .. sAnswer .. "!", "Welcome!", 0x0040)
    end

(back to top)

## 3.5. UTF-8 library

This library provides basic support for UTF-8 encoding.

It provides all its functions inside the table `LazUtf8`.

UTF-8 library
Function name | Description
--- | ---
LazUtf8.Pos | iResult = LazUtf8.Pos(SearchText, SourceText, Offset) Search for substring in a string, starting at a certain position. The search is case sensitive. Returns the position of the first occurrence of the substring SearchText in the string SourceText, starting the search at position Offset (default 1). If SearchText does not occur in SourceText after the given Offset, zero is returned.
LazUtf8.Next | LazUtf8.Next(String) An iterator function that, each time it is called, returns the next character in the String and the position of the beginning of this character (in bytes). Example:
    -- Print pairs of values in the form "position : character"
    for iPos, sChar in LazUtf8.Next(String) do
      DC.LogWrite(iPos .. " : " .. sChar)
    end
LazUtf8.Copy | sResult = LazUtf8.Copy(String, iIndex, iCount) Copy part of a string. Copy returns a string which is a copy if the iCount characters in String, starting at position iIndex. If iCount is larger than the length of the string String, the result is truncated. If iIndex is larger than the length of the string String, then an empty string is returned.
LazUtf8.Length | iResult = LazUtf8.Length(String) Returns the number of UTF-8 characters in the string.
LazUtf8.UpperCase | sResult = LazUtf8.UpperCase(String) Receives a string and returns a copy of this string with all lowercase letters changed to uppercase.
LazUtf8.LowerCase | sResult = LazUtf8.LowerCase(String) Receives a string and returns a copy of this string with all uppercase letters changed to lowercase.
LazUtf8.ConvertEncoding | sResult = LazUtf8.ConvertEncoding(String, FromEnc, ToEnc) Convert String encoding from FromEnc to ToEnc. List of supported encoding values: Default system encoding (depends on the system locale): "default". Default ANSI (Windows) encoding (depends on the system locale): "ansi". Default OEM (DOS) encoding (depends on the system locale): "oem". Unicode: "utf8", "utf8bom", "ucs2le", "ucs2be". ANSI (Windows): "cp1250", "cp1251", "cp1252", "cp1253", "cp1254", "cp1255", "cp1256", "cp1257", "cp1258". OEM (DOS): "cp437", "cp850", "cp852", "cp865", "cp866", "cp874", "cp932", "cp936", "cp949", "cp950". ISO 8859: "iso88591", "iso88592", "iso88593", "iso88594", "iso88595", "iso88597", "iso88599", "iso885910", "iso885913", "iso885914", "iso885915", "iso885916". Other: "macintosh", "koi8r", "koi8u", "koi8ru". The meaning of special encodings (examples). In Windows (English or Russian): "default" - cp1252 or cp1251 "ansi" - cp1252 or cp1251 "oem" - cp850 or cp866 In Linux (English or Russian): "default" - utf8 "ansi" - cp1252 or cp1251 "oem" - cp850 or cp866
LazUtf8.DetectEncoding | sEnc = LazUtf8.DetectEncoding(String) Returns the value of encoding of the transmitted text. The list of supported encodings is similar to those used in the `LazUtf8.ConvertEncoding` function.

(back to top)

## 3.6. Char library

This library contains functions for checking whether a character belongs to a particular Unicode category, as well as getting the category of a character.

List of available functions in this library:

Char library
Function name | Description
--- | ---
Char.GetUnicodeCategory | iResult = Char.GetUnicodeCategory(Character) Returns the Unicode category of a character `Character`, one of the following values:
Value | Description
--- | ---
Letter:
0 | Uppercase Letter (Lu)
1 | Lowercase Letter (Ll)
2 | Titlecase Letter (Lt)
3 | Modifier Letter (Lm)
4 | Other Letter (Lo)
Mark:
5 | Non-Spacing Mark (Mn)
6 | Spacing Combining Mark (Mc)
7 | Enclosing Mark (Me)
Number:
8 | Decimal Digit Number (Nd)
9 | Letter Number (Nl)
10 | Other Number (No)
Punctuation:
11 | Connector Punctuation (Pc)
12 | Dash Punctuation (Pd)
13 | Open Punctuation (Ps)
14 | Close Punctuation (Pe)
15 | Initial Punctuation (Pi)
16 | Final Punctuation (Pf)
17 | Other Punctuation (Po)
Symbol:
18 | Math Symbol (Sm)
19 | Currency Symbol (Sc)
20 | Modifier Symbol (Sk)
21 | Other Symbol (So)
Separator:
22 | Space Separator (Zs)
23 | Line Separator (Zl)
24 | Paragraph Separator (Zp)
Other:
25 | Control (Cc)
26 | Format (Cf)
27 | Surrogate (Cs)
28 | Private Use (Co)
29 | Unassigned (Cn)
Char.IsDigit | bResult = Char.IsDigit(Character) Returns `true` if the Character character is in the Nd category.
Char.IsLetter | bResult = Char.IsLetter(Character) Returns `true` if the Character character is in the category Lu, Ll, Lt, Lm or Lo.
Char.IsLetterOrDigit | bResult = Char.IsLetterOrDigit(Character) Returns `true` if the Character character is in the category Lu, Ll, Lt, Lm Lo, Nd или Nl.
Char.IsLower | bResult = Char.IsLower(Character) Returns `true` if the Character character is in the Ll category.
Char.IsUpper | bResult = Char.IsUpper(Character) Returns `true` if the Character character is in the Lu category.

Also, these functions support working with two parameters: instead of a single character, we can specify a string and the position of the character in this string.

(back to top)

## 3.7. OS library

This library contains functions related with the operating system where Double Commander is running.

Here is the list of available functions in this library:

OS library
Function name | Description
--- | ---
os.execute | iResultCode = os.execute(sCommand) Will execute sCommand as it would be typed on the command-line and return the result code of the operation. The sCommand could either be: A terminal command like `os.execute("dir > all.txt")` An executable like `os.execute("C:\\Windows\\System32\\calc.exe")` An executable with parameters: `os.execute("C:\\Utils\\fsum.exe -md5 test.bin > md5.txt")`
os.tmpname | sTempFileName = os.tmpname() Will return a filename to use as a temporary filename (in the system directory for the temporary files). If the function could not create a unique name, it will return an empty string.
os.remove | bResult, sError, iError = os.remove(sFileName) Will delete the file or the directory with the name sFileName. If it works, function returns `true`. If it fails, function returns three things: `nil` to indicate it failed sError for the error message description iError for the error code number
os.rename | bResult, sError, iError = os.rename(sOldName, sNewName) Will rename the file sOldName with the new name sNewName. Note: If a file named sNewName already exists, it will be replaced! If it works, function returns `true`. If it fails, function returns three things: `nil` to indicate it failed sError for the error message description iError for the error code number
os.getenv | Value = os.getenv(VariableName) Will return the Value of the variable VariableName passed in parameter. If no variable of that name exists, it will return `nil`.
os.setenv | os.setenv(VariableName, Value) Add or change the VariableName environment variable. In case of an error, the function returns -1.
os.unsetenv | os.unsetenv(VariableName) Remove the VariableName environment variable. In case of an error, the function returns -1.

(back to top)

## 4. Index

DC library DC.CurrentPanel DC.ExecuteCommand DC.ExpandVar DC.GetPluginField DC.GoToFile DC.LogWrite System library SysUtils.CreateDirectory SysUtils.CreateHardLink SysUtils.CreateSymbolicLink SysUtils.DirectoryExists SysUtils.Execute SysUtils.ExpandEnv SysUtils.ExtractFileDir SysUtils.ExtractFileDrive SysUtils.ExtractFileExt SysUtils.ExtractFileName SysUtils.ExtractFilePath SysUtils.FileExists SysUtils.FileGetAttr SysUtils.FileSetTime SysUtils.FindClose SysUtils.FindFirst SysUtils.FindNext SysUtils.GetAbsolutePath SysUtils.GetFileProperty SysUtils.GetRelativePath SysUtils.GetTempName SysUtils.GetTickCount SysUtils.MatchesMask SysUtils.MatchesMaskList SysUtils.PathDelim SysUtils.ReadSymbolicLink SysUtils.RemoveDirectory SysUtils.Sleep | Clipboard library Clipbrd.Clear Clipbrd.GetAsText Clipbrd.SetAsHtml Clipbrd.SetAsText Dialogs library Dialogs.InputListBox Dialogs.InputQuery Dialogs.MessageBox UTF-8 library LazUtf8.ConvertEncoding LazUtf8.Copy LazUtf8.DetectEncoding LazUtf8.Length LazUtf8.LowerCase LazUtf8.Next LazUtf8.Pos LazUtf8.UpperCase Char library Char.GetUnicodeCategory Char.IsDigit Char.IsLetter Char.IsLetterOrDigit Char.IsLower Char.IsUpper | OS library os.execute os.getenv os.remove os.rename os.setenv os.tmpname os.unsetenv

(back to top)