Double Commander

2.10. Lua Scripting

Content

1. Introduction
2. DLL required
3. Double Commander functions libraries
3.1. DC libraries
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. OS library

1. Introduction

Double Commander can execute Lua scripts via cm_ExecuteScript command.

This was introduced with version 0.8 of Double Commander.

It is also possible to write content plugins (.wdx) using Lua script.

More informations on Lua script programming language are available on the web on this link.

2. DLL required

In order to interpret Lua script file, we need to have a version 5.1 Lua DLL file in the same folder as the executable of Double Commander or in system folder.

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 (Mac OS).

We can use DLL file from LuaJIT project. It may require to manually edit the filename in <PathToLibrary> parameter. 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.

If we're using a 64-bits version of DC, the dll must be the 64-bits version as well.

Generally, the dll will come with DC installation but if for whatever reason it's not there, we may find it under this link or through our packages manager.

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
Libray nameScript nameQuick description
DCDouble Commander specific functions
SysUtilsVarious system functions
ClipbrdProvides external clipboard functionality
DialogsInteracts with user
LazUtf8UTF-8 string functions
OSFunctions related with the operating system

3.1. DC library

This library contains Double Commander specific functions.

It provides all its functions inside the table DC.

DC library
Function nameDescription
DC.LogWrite [*]

DC.LogWrite(Message, MsgType, bForce, bLogFile)

Write a message to the log window.

Here is the description of each field:

  • Message : The message text.
  • MsgType : 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.
DC.ExecuteCommand

DC.ExecuteCommand(Command, Param1, Param2,...,ParamX)

This allows the script to invoke internal commands of DC.

The Command is holding the actual internal command name.

We may provide as many Param... as command may support.

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 openened tabs
  3. switch to a specific folder
  4. focus the left panel
  5. close all openened 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:

Invoking a lua script from toolbar

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:

Lua syntax highlighting with internal editor

3.2. System library

This library contains various system functions.

It provides all its functions inside the table SysUtils.

System library
Function nameDescription
SysUtils.Sleep

SysUtils.Sleep(Milliseconds)

Suspends the execution of the script for the specified number of Milliseconds.
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 assumtions should be made as to the interval between the ticks.

SysUtils.FileExists

bFlagExists = SysUtils.FileExists(FileName)

Check whether a particular file exists in the filesystem.

Returns in bFlagExists the value true if file with name FileName exists on the disk, or false otherwise.

SysUtils.DirectoryExists

bFlagExists = SysUtils.DirectoryExists(Directory)

Checks whether Directory exists in the filesystem and is actually a directory.

If this is the case, the function returns in bFlagExists the value true otherwise false is returned.

SysUtils.FileGetAttr

Attr = SysUtils.FileGetAttr(FileName)

Returns in Attr the attribute settings of file FileName.

See the detail explanations of the returned value here.

SysUtils.FindFirst

Handle, FindData = SysUtils.FindFirst(Path)

Looks for files that match the Path, 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 occurences 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 mentionned 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 loss.

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.PathDelim

SysUtils.PathDelim

Allows a script to get from DC the system path delimiter.

In Unix/Linux system will be a " / " and in Windows will be " \ "

3.2.1. Details on SysUtils.FileGetAttr returned value

FileGetAttr returns the attribute settings of file FileName.

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

Constants uses in SysUtils.FileGetAttr returned value
ValueSignification
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, block or FIFO file.
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 on Unix/Linux.
0x00000400
faSymLink
File is a symbolic link.
Note: In case of error, -1 is returned.

See an example in the next section.

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 myfileattr

if #params == 1 then -- We got at least one parameter?
  myfileattr = SysUtils.FileGetAttr(params[1])
  if myfileattr > 0 then -- We got a valid attribut?
    if math.floor(myfileattr / 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 param[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:

Parameter with cm_ExecuteScript

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.

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 OutputFile = nil

  OutputFile = 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(OutputFile)
  end
end

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

  1. param[1] - which is the directory we want the content
  2. param[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.

Parameter with cm_ExecuteScript

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.

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 nameDescription
Clipbrd.Clear

Clipbrd.Clear()

Clear the content of the clipboard.

Clipbrd.GetAsText

StringVar = Clipbrd.GetAsText()

Get the current text content of the clipboard to assigned it to StringVar.

Clipbrd.SetAsText

Clipbrd.SetAsText(StringVar)

Store in the clipboard the text content of StringVar.

Clipbrd.SetAsHtml

Clipbrd.SetAsHtml(Html)

Adds html-formatted text Html 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.

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 containts the word that was in clipboard.

Then, the filenames of the files that containt 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 myfileattr
local bFound = false
local sCompleteFilename = ""
local InputFile = nil
local line = ""
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
      myfileattr = SysUtils.FileGetAttr(sCompleteFilename)
      if myfileattr > 0 then -- We got a valid attribut?
        -- We need file, not directory!
        if math.floor(myfileattr / 0x00000010) % 2 == 0 then

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

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

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

          io.close(InputFile)
        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

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 nameDescription
Dialogs.MessageBox

ButPressed = Dialogs.MessageBox(Message, Title, ButFlags)

Will diplay a message box prompting a user to click a button which will be returned by the function.

Here is the description of each field:

  • Message : The message inside the box.
  • Title : The text displayed as the title of the box.
  • ButFlags : 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.
  • ButPressed : Returned value indicating the button user pressed (see this table).
Dialogs.InputQuery

bAck, sAnswer = Dialogs.InputQuery(Title, Msg, bMask, sDefault)

Will display a requester box where user may enter a string value.

Here is the description of each field:

  • Title : The text displayed as the title of the box.
  • Msg : 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.
  • bAck : Returned boolean indicating if user effectively enter something or not.
  • sAnswer : Returned string when user entered something and then clicked ok.
Dialogs.InputListBox [*]

sAnswer = Dialogs.InputListBox(Title, Msg, Items, sDefault)

Displays a dialog box to allow the user to choose from a list of items.

Here is the description of each field:

  • Title : The text displayed as the title of the dialog.
  • Msg : The message inside the dialog.
  • Items : A Lua table, each element of the table must be a string.
  • sDefault : The default selected item in the list.
  • sAnswer : Returned the selected item as a string or nil if the dialog is dismissed.

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 valueButtons displayed, from left to right
0x0000
MB_OK
Button OK
0x0001
MB_OKCANCEL
Button OKButton CANCEL
0x0002
MB_ABORTRETRYIGNORE
Button ABORTButton RETRYButton IGNORE
0x0003
MB_YESNOCANCEL
Button YESButton NOButton CANCEL
0x0004
MB_YESNO
Button YESButton NO
0x0005
MB_RETRYCANCEL
Button RETRYButton CENCEL

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 valueStyle of window
0x0040
MB_ICONINFORMATION
Icon INFORMATION Informative window
0x0030
MB_ICONWARNING
Icon WARNING Warning window
0x0020
MB_ICONQUESTION
Icon QUESTION Confirmation window
0x0010
MB_ICONERROR
Icon ERROR Error window

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

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 valueButton pressed
0x0000
mrNone
No button pressed
0x0001
mrOK
Result OK
0x0002
mrCancel
Result CANCEL
0x0003
mrAbort
Result ABORT
0x0004
mrRetry
Result RETRY
0x0005
mrIgnore
Result IGNORE
0x0006
mrYes
Result YES
0x0007
mrNo
Result NO

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.

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

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

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

Example of usage of the Dialogs.MessageBox

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:

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

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

Example of usage of the Dialogs.InputQuery

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 nameDescription
LazUtf8.Pos

Result = 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.Copy

Result = LazUtf8.Copy(Source, Index, Count)

Copy part of a string.

Copy returns a string which is a copy if the Count characters in Source, starting at position Index.

If Count is larger than the length of the string Source, the result is truncated. If Index is larger than the length of the string Source, then an empty string is returned.

LazUtf8.Length

Result = LazUtf8.Length(String)

Returns the number of UTF-8 characters in the string.

LazUtf8.UpperCase

Result = LazUtf8.UpperCase(String)

Receives a string and returns a copy of this string with all lowercase letters changed to uppercase.

LazUtf8.LowerCase

Result = LazUtf8.LowerCase(String)

Receives a string and returns a copy of this string with all uppercase letters changed to lowercase.

LazUtf8.ConvertEncoding

Result = LazUtf8.ConvertEncoding(String, FromEnc, ToEnc)

Convert String encoding from FromEnc to ToEnc.

List of supported encoding values:

  • Default system encoding (depends on user locale): "default".
  • Default ANSI (Windows) encoding (depends on user locale): "ansi".
  • Default OEM (DOS) encoding (depends on user locale): "oem".
  • Unicode: "utf8", "utf8bom", "ucs2le", "ucs2be".
  • ANSI (Windows): "cp1250", "cp1251", "cp1252", "cp1253", "cp1254", "cp1255", "cp1256", "cp1257", "cp1258".
  • OEM (DOS): "cp437", "cp850", "cp852", "cp866", "cp874", "cp932", "cp936", "cp949", "cp950".
  • ISO 8859: "iso88591", "iso88592", "iso885915".
  • Other: "macintosh", "koi8".
The meaning of special encodings (examples).

In the Windows with Russian locale:
  • "default" - cp1251
  • "ansi" - cp1251
  • "oem" - cp866
In the Linux with Russian locale:
  • "default" - utf8
  • "ansi" - cp1251
  • "oem" - cp866

3.6. 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 nameDescription
os.execute

resultcode = os.execute(command)

Will execute command as it would be typed on the command-line and return the result code of the operation.

The command could either be:

  • A terminal commmand 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

tempfilename = os.tmpname()

Will return a filename to use as a temporary filename.

os.remove

bresult, errorstr, errorno = os.remove(filename)

Will delete the file or the directory with the name filename.

If it works, function returns true.

If it fails, function returns three things:

  1. nil to indicate it failed
  2. errorstr for the error message description
  3. errorno for the error code number
os.rename

bresult, errorstr, errorno = os.rename(nameS, nameD)

Will rename the file nameS with the new name nameD.

If it works, function returns true.

If it fails, function returns three things:

  1. nil to indicate it failed
  2. errorstr for the error message description
  3. errorno 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.


Valid HTML 4.0 Transitional CSS Valid!