From 1a22bfbcef8da7d80df516cf5afb5f9fd36b2b33 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Wed, 11 Jul 2018 11:32:48 -0400 Subject: [PATCH] Added system library to handle running external commands on different operating systems in a transparent manner. Added file and path quoting that is cross platform compatible. Changed gimp.lua to use the new utilities. --- contrib/gimp.lua | 9 ++-- lib/dtutils/file.lua | 25 ++++++++++ lib/dtutils/string.lua | 34 ++++++++++++++ lib/dtutils/system.lua | 103 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 lib/dtutils/system.lua diff --git a/contrib/gimp.lua b/contrib/gimp.lua index 8366e11..e7f2623 100644 --- a/contrib/gimp.lua +++ b/contrib/gimp.lua @@ -65,6 +65,7 @@ local dt = require "darktable" local df = require "lib/dtutils.file" +local dtsys = require "lib/dtutils.system" require "official/yield" local gettext = dt.gettext local gimp_widget = nil @@ -94,7 +95,7 @@ local function group_if_not_member(img, new_img) dt.print_log("Added to group") end end - +--[[ local function sanitize_filename(filepath) local path = df.get_path(filepath) local basename = df.get_basename(filepath) @@ -104,7 +105,7 @@ local function sanitize_filename(filepath) return path .. sanitized .. "." .. filetype end - +]] local function show_status(storage, image, format, filename, number, total, high_quality, extra_data) dt.print(string.format(_("Export Image %i/%i"), number, total)) @@ -130,7 +131,7 @@ local function gimp_edit(storage, image_table, extra_data) --finalize img_list = "" for _,exp_img in pairs(image_table) do - exp_img = sanitize_filename(exp_img) + exp_img = df.sanitize_filename(exp_img) img_list = img_list ..exp_img.. " " end @@ -141,7 +142,7 @@ local function gimp_edit(storage, image_table, extra_data) --finalize dt.print_log(gimpStartCommand) - dt.control.execute( gimpStartCommand) + dtsys.external_command(gimpStartCommand) -- for each of the image, exported image pairs -- move the exported image into the directory with the original diff --git a/lib/dtutils/file.lua b/lib/dtutils/file.lua index aaf5927..120b70f 100644 --- a/lib/dtutils/file.lua +++ b/lib/dtutils/file.lua @@ -1,5 +1,6 @@ local dtutils_file = {} local dt = require "darktable" +local ds = require "lib/dtutils.string" local log = require "lib/dtutils.log" @@ -529,4 +530,28 @@ function dtutils_file.executable_path_widget(executables) return box end +dtutils_file.libdoc.functions["sanitize_filename"] = { + Name = [[sanitize_filename]], + Synopsis = [[make a filename safe to pass as an argument]], + Usage = [[local df = require "lib/dtutils.file" + + local sanitized_filename = df.sanitize_filename(filename) + filename - string - a filepath and filename]], + Description = [[sanitize_file places quotes around the filename in an + operating system specific manner. The result is safe to pass as + an argument to the operating system.]], + Return_Value = [[sanitized_filename - string - quoted filename]], + Limitations = [[]], + Example = [[]], + See_Also = [[]], + Reference = [[]], + License = [[]], + Copyright = [[]], +} + +function dtutils_file.sanitize_filename(filename) + return ds.sanitize(filename) +end + return dtutils_file + diff --git a/lib/dtutils/string.lua b/lib/dtutils/string.lua index db04423..7db7acf 100644 --- a/lib/dtutils/string.lua +++ b/lib/dtutils/string.lua @@ -1,4 +1,7 @@ local dtutils_string = {} + +local dt = require "darktable" + dtutils_string.libdoc = { Name = [[dtutils.string]], Synopsis = [[a library of string utilities for use in darktable lua scripts]], @@ -170,4 +173,35 @@ function dtutils_string.urlencode(str) return str end + +dtutils_string.libdoc.functions["sanitize"] = { + Name = [[sanitize]], + Synopsis = [[surround a string in quotes making it safe to pass as an argument]], + Usage = [[local ds = require "lib/dtutils.string" + + local result = ds.sanitize(str) + str - string - the string that needs to be made safe]], + Description = [[sanitize converts a string into a version suitable for + use passing as an argument in a system command.]], + Return_Value = [[result - string - a websafe string]], + Limitations = [[]], + Example = [[]], + See_Also = [[]], + Reference = [[]], + License = [[]], + Copyright = [[]], +} + +function dtutils_string.sanitize(str) + local result = "" + + if dt.configuration.running_os == "windows" then + result = '"' .. str .. '"' + else + result = "'" .. str .. "'" + end + + return result +end + return dtutils_string diff --git a/lib/dtutils/system.lua b/lib/dtutils/system.lua new file mode 100644 index 0000000..b668faf --- /dev/null +++ b/lib/dtutils/system.lua @@ -0,0 +1,103 @@ +local dtutils_system = {} + +local dt = require "darktable" + +dtutils_system.libdoc = { + Name = [[dtutils.system]], + Synopsis = [[a library of system utilities for use in darktable lua scripts]], + Usage = [[local ds = require "lib/dtutils.system"]], + Description = [[This library contains routines for interfacing to the operating system from + darktable lua scripts.]], + Return_Value = [[du - library - the darktable lua system library]], + Limitations = [[]], + Example = [[]], + See_Also = [[]], + Reference = [[]], + License = [[This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see .]], + Copyright = [[Copyright (c) 2018 Bill Ferguson ]], + functions = {} +} + +dtutils_system.libdoc.functions["external_command"] = { + Name = [[external_command]], + Synopsis = [[pass a command to the operating system for execution and return the result]], + Usage = [[local dsys = require "lib/dtutils.system" + + local result = dsys.external_command(command) + command - string - a string containing the command and arguments to be passed to the operating system for execution.]], + Description = [[external_command passes a command to the operating system for execution and returns the results.]], + Return_Value = [[result - the return value signalling success or failure.]], + Limitations = [[]], + Example = [[]], + See_Also = [[]], + Reference = [[]], + License = [[]], + Copyright = [[]], +} + +function dtutils_system.external_command(command) + local result = nil + + if dt.configuration.running_os == "windows" then + result = dtutils_system.windows_command(command) + else + result = dt.control.execute(command) + end + + return result +end + +dtutils_system.libdoc.functions["windows_command"] = { + Name = [[windows_command]], + Synopsis = [[pass a command to the windows operating system for execution and return the result]], + Usage = [[local dsys = require "lib/dtutils.system" + + local result = dsys.windows_command(command) + command - string - a string containing the command and arguments to be passed to the operating system for execution.]], + Description = [[The normal method of executing an operating system command is using dt.control.execute(), but that doesn't + work with Windows when more than one item in the command is quoted. In order to ensure command execution on Windows we + create a batch file in the temporary directory, put the command in it, execute the batch file, then return the result.]], + Return_Value = [[result - the return value signalling success or failure.]], + Limitations = [[]], + Example = [[]], + See_Also = [[]], + Reference = [[]], + License = [[]], + Copyright = [[]], +} + +function dtutils_system.windows_command(command) + local result = 1 + + local fname = dt.configuration.tmp_dir .. "/run_command.bat" + + local file = io.open(fname, "w") + if file then + dt.print_log("opened file") + command = string.gsub(command, "%%", "%%%%") -- escape % from windows shell + file:write(command) + file:close() + + result = dt.control.execute(fname) + dt.print_log("result from windows command was " .. result) + + os.remove(fname) + else + dt.print_error("Windows command failed: unable to create batch file") + end + + return result +end + +return dtutils_system \ No newline at end of file