Merge pull request #74 from wpferguson/libraries

Libraries
This commit is contained in:
supertobi
2016-10-25 13:30:37 +02:00
committed by GitHub
8 changed files with 1362 additions and 93 deletions

218
lib/dtutils.lua Normal file
View File

@@ -0,0 +1,218 @@
local dtutils = {}
dtutils.libdoc = {
Name = [[dtutils]],
Synopsis = [[A Darktable lua utilities library]],
Usage = [[local du = require "lib/dtutils"]],
Description = [[dtutils provides a common library of functions used to build
lua scripts. There are also sublibraries that provide more functions.]],
Return_Value = [[du - library - the library of functions]],
Limitations = [[]],
Example = [[]],
See_Also = [[dtutils.debug(3), dtutils.file(3), dtutils.log(3), dtutils.string(3)]],
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 <http://www.gnu.org/licenses/>.]],
Copyright = [[Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
Copyright (C) 2016 Tobias Jakobs]],
functions = {}
}
local dt = require "darktable"
local log = require "lib/dtutils.log"
dt.configuration.check_version(...,{3,0,0})
dtutils.libdoc.functions["split"] = {
Name = [[split]],
Synopsis = [[split a string on a specified separator]],
Usage = [[local du = require "lib/dtutils"
local result = du.split(str, pat)
str - string - the string to split
pat - string - the pattern to split on]],
Description = [[split separates a string into a table of strings. The strings are separated at each
occurrence of the supplied pattern. The pattern may be any pattern as described in the lua docs.
Each match of the pattern is consumed and not returned.]],
Return_Value = [[result - a table of strings on success, or an empty table on error]],
Limitations = [[]],
Example = [[split("/a/long/path/name/to/a/file.txt", "/") would return a table like
{a, "long", "path", "name", "to", a, "file.txt"}]],
See_Also = [[]],
Reference = [[http://lua-users.org/wiki/SplitJoin]],
License = [[]],
Copyright = [[]],
}
function dtutils.split(str, pat)
local t = {}
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
dtutils.libdoc.functions["join"] = {
Name = [[join]],
Synopsis = [[join a table of strings with a specified separator]],
Usage = [[local du = require "lib/dtutils"
local result = du.join(tabl, pat)
tabl - a table of strings
pat - a separator]],
Description = [[join assembles a table of strings into a string with the specified pattern
in between each string]],
Return_Value = [[result - string - the joined string on success, or an empty string on failure]],
Limitations = [[]],
Example = [[join({a, "long", "path", "name", "to", a, "file.txt"}, " ") would return the string
"a long path name to a file.txt"]],
See_Also = [[]],
Reference = [[http://lua-users.org/wiki/SplitJoin]],
License = [[]],
Copyright = [[]],
}
function dtutils.join(tabl, pat)
returnstr = ""
for i,str in pairs(tabl) do
returnstr = returnstr .. str .. pat
end
return string.sub(returnstr, 1, -(pat:len() + 1))
end
--[[
NAME
prequire - a protected lua require
SYNOPSIS
local du = require "lib/dtutils"
local result = du.prequire(req_name)
req_name - the filename of the lua code to load without the ".lua" filetype
DESCRIPTION
prequire is a protected require that can survive an error in the code being loaded without
bringing down the calling routine.
RETURN VALUE
result - the code or true on success, otherwise an error message
EXAMPLE
prequire("lib/dtutils.file") which would load lib/dtutils/file.lua
]]
dtutils.libdoc.functions["prequire"] = {
Name = [[prequire]],
Synopsis = [[a protected lua require]],
Usage = [[local du = require "lib/dtutils"
local status, lib = du.prequire(req_name)
req_name - the filename of the lua code to load without the ".lua" filetype]],
Description = [[prequire is a protected require that can survive an error in the code being loaded without
bringing down the calling routine.]],
Return_Value = [[status - boolean - true on success
lib - if status is true, then the code, otherwise an error message]],
Limitations = [[]],
Example = [[local status, lib = prequire("lib/dtutils.file") which would load lib/dtutils/file.lua which
would return a status of true and the reference to the library in lib.]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils.prequire(req_name)
local status, lib = pcall(require, req_name)
if status then
log.msg(log.info, "Loaded " .. req_name)
else
log.msg(log.info, "Error loading " .. req_name)
end
return status, lib
end
dtutils.libdoc.functions["spairs"] = {
Name = [[spairs]],
Synopsis = [[an iterator that provides sorted pairs from a table]],
Usage = [[local du = require "lib/dtutils"
for key, value in du.spairs(t, order) do
t - table - table of key, value pairs
order - function - an optional function to sort the pairs
if none is supplied, table.sort() is used]],
Description = [[spairs is an iterator that returns key, value pairs from a table in sorted
order. The sorting order is the result of table.sort() if no function is
supplied, otherwise sorting is done as specified in the function.]],
Return_Value = [[]],
Limitations = [[]],
Example = [[HighScore = { Robin = 8, Jon = 10, Max = 11 }
-- basic usage, just sort by the keys
for k,v in spairs(HighScore) do
print(k,v)
end
--> Jon 10
--> Max 11
--> Robin 8
-- this uses an custom sorting function ordering by score descending
for k,v in spairs(HighScore, function(t,a,b) return t[b] < t[a] end) do
print(k,v)
end
--> Max 11
--> Jon 10
--> Robin 8]],
See_Also = [[]],
Reference = [[Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua]],
License = [[]],
Copyright = [[]],
}
-- Sort a table
function dtutils.spairs(_table, order) -- Code copied from http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
-- collect the keys
local keys = {}
for _key in pairs(_table) do keys[#keys + 1] = _key end
-- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys
if order then
table.sort(keys, function(a,b) return order(_table, a, b) end)
else
table.sort(keys)
end
-- return the iterator function
local i = 0
return function()
i = i + 1
if keys[i] then
return keys[i], _table[keys[i]]
end
end
end
return dtutils

161
lib/dtutils/debug.lua Normal file
View File

@@ -0,0 +1,161 @@
--[[
This file is part of darktable,
Copyright (c) 2014 Jérémy Rosen
Copyright (c) 2016 Bill Ferguson
darktable 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.
darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
DEBUG HELPERS
A collection of helper functions to help debugging lua scripts.
require it as
dhelpers = require "lib/dtutils.debug"
Each function is documented in its own header
]]
local dt = require "darktable"
local io = require "io"
local table = require "table"
require "darktable.debug"
local log = require "lib/dtutils.log"
local M = {} -- The actual content of the module
M.libdoc = {
Name = [[dtutils.debug]],
Synopsis = [[debugging helpers used in developing darktable lua scripts]],
Usage = [[local dd = require "lib/dtutils.debug"]],
Description = [[dtutils.debug provides an interface to the darktable debugging routines.]],
Return_Value = [[dd - library - the darktable lua debugging helpers]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = log.libdoc.License,
Copyright = [[Copyright (c) 2014 Jérémy Rosen
Copyright (c) 2016 Bill Ferguson
]],
functions = {}
}
M.libdoc.functions["tracepoint"] = {
Name = [[tracepoint]],
Synopsis = [[print out a tracepoint and dump the arguments]],
Usage = [[local dd = require "lib/dtutils.debug"
local result = tracepoint(name, ...)
name - string - the name of the tracepoint to print out
... - arguments - variables to dump the contents of]],
Description = [[tracepoint prints its name and dumps its parameters using
dt.debug]],
Return_Value = [[result - ... - the supplied argument list]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function M.tracepoint(name,...)
log.msg(log.always, 4, "***** "..name.." ****")
params = {...}
log.msg(log.always, 0, dt.debug.dump(params,"parameters"))
return ...;
end
M.libdoc.functions["new_tracepoint"] = {
Name = [[new_tracepoint]],
Synopsis = [[create a function returning a tracepoint]],
Usage = [[local dd = require "lib/dtutils.debug"
local result = new_tracepoint(name, ...)
name - string - the name of the tracepoint to print out
... - arguments - variables to dump the contents of]],
Description = [[A function that returns a tracepoint function with the given name
This is mainly used to debug callbacks.]],
Return_Value = [[result - function - a function that returns the result of a tracepoint]],
Limitations = [[]],
Example = [[register_event(event, dd.new_tracepoint("hit callback"))
will print the following each time the callback is called
**** hit callback ****
<all the callback's parameters dumped>]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function M.new_tracepoint(name)
return function(...) return M.tracepoint(name,...) end
end
M.libdoc.functions["dprint"] = {
Name = [[dprint]],
Synopsis = [[pass a variable to dt.debug.dump and print the results to stdout]],
Usage = [[local dd = require "lib/dtutils.debug"
dd.dprint(var)
var - variable - any variable that you want to see the contents of]],
Description = [[Wrapper around debug.dump, will directly print to stdout,
same calling convention]],
Return_Value = [[]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function M.dprint(...)
log.msg(log.always, 4, dt.debug.dump(...))
end
M.libdoc.functions["terse_dump"] = {
Name = [[terse_dump]],
Synopsis = [[set dt.debug.known to shorten all image dumps to a single line]],
Usage = [[local dd = require "lib/dtutils.debug"
dd.terse_dump()]],
Description = [[terse_dump sets dt.debug.known to shorten all images to a single line.
If you don't need to debug the content of images, this will avoid them flooding your logs]],
Return_Value = [[]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function M.terse_dump()
for _,v in ipairs(dt.database) do
dt.debug.known[v] = tostring(v)
end
end
return M
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;

394
lib/dtutils/file.lua Normal file
View File

@@ -0,0 +1,394 @@
local dtutils_file = {}
local dt = require "darktable"
local log = require "lib/dtutils.log"
dtutils_file.libdoc = {
Name = [[dtutils.file]],
Synopsis = [[common darktable lua file functions]],
Usage = [[local df = require "lib/dtutils.file"]],
Description = [[[dtutils.file provides common file manipulation functions used in
constructing Darktable lua scripts]],
Return_Value = [[df - library - the file functions]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = log.libdoc.License,
Copyright = [[Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.]],
Copyright = [[Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.
Copyright (C) 2016 Tobias Jakobs]],
functions = {}
}
local gettext = dt.gettext
dt.configuration.check_version(...,{3,0,0})
-- Tell gettext where to find the .mo file translating messages for a particular domain
gettext.bindtextdomain("dtutils.file",dt.configuration.config_dir.."/lua/locale/")
local function _(msgid)
return gettext.dgettext("dtutils.file", msgid)
end
dtutils_file.libdoc.functions["check_if_bin_exists"] = {
Name = [[check_if_bin_exists]],
Synopsis = [[check if an executable is in the path]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.check_if_bin_exists(bin)
bin - string - the binary to check for]],
Description = [[check_if_bin_exists checks to see if the specified binary executable is
in the path.]],
Return_Value = [[result - boolean - true if the executable was found, false if not]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.check_if_bin_exists(bin)
local result = os.execute("which " .. bin)
if not result then
result = false
end
return result
end
dtutils_file.libdoc.functions["split_filepath"] = {
Name = [[split_filepath]],
Synopsis = [[split a filepath into parts]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.split_filepath(filepath)
filepath - string - path and filename]],
Description = [[split_filepath splits a filepath into the path, filename, basename and filetype and puts
that in a table]],
Return_Value = [[result - table - a table containing the path, filename, basename, and filetype]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.split_filepath(str)
-- strip out single quotes from quoted pathnames
str = string.gsub(str, "'", "")
local result = {}
-- Thank you Tobias Jakobs for the awesome regular expression, which I tweaked a little
result["path"], result["filename"], result["basename"], result["filetype"] = string.match(str, "(.-)(([^\\/]-)%.?([^%.\\/]*))$")
return result
end
dtutils_file.libdoc.functions["get_path"] = {
Name = [[get_path]],
Synopsis = [[get the path from a file path]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.get_path(filepath)
filepath - string - path and filename]],
Description = [[get_path strips the filename and filetype from a path and returns the path]],
Return_Value = [[result - string - the path]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.get_path(str)
local parts = dtutils_file.split_filepath(str)
return parts["path"]
end
dtutils_file.libdoc.functions["get_filename"] = {
Name = [[get_filename]],
Synopsis = [[get the filename and extension from a file path]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.get_filename(filepath)
filepath - string - path and filename]],
Description = [[get_filename strips the path from a filepath and returns the filename]],
Return_Value = [[result - string - the file name and type]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.get_filename(str)
local parts = dtutils_file.split_filepath(str)
return parts["filename"]
end
dtutils_file.libdoc.functions["get_basename"] = {
Name = [[get_basename]],
Synopsis = [[get the filename without the path or extension]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.get_basename(filepath)
filepath - string - path and filename]],
Description = [[get_basename returns the name of the file without the path or filetype
]],
Return_Value = [[result - string - the basename of the file]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.get_basename(str)
local parts = dtutils_file.split_filepath(str)
return parts["basename"]
end
dtutils_file.libdoc.functions["get_filetype"] = {
Name = [[get_filetype]],
Synopsis = [[get the filetype from a filename]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.get_filetype(filepath)
filepath - string - path and filename]],
Description = [[get_filetype returns the filetype from the supplied filepath]],
Return_Value = [[result - string - the filetype]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.get_filetype(str)
local parts = dtutils_file.split_filepath(str)
return parts["filetype"]
end
-- Thanks Tobias Jakobs for the idea
dtutils_file.libdoc.functions["check_if_file_exists"] = {
Name = [[check_if_file_exists]],
Synopsis = [[check if a file or path exist]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.check_if_file_exists(filepath)
filepath - string - a file or path to check]],
Description = [[check_if_file_exists checks to see if a file or path exists]],
Return_Value = [[result - boolean - true if the file or path exists, false if it doesn't]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.check_if_file_exists(filepath)
local result = os.execute("test -e " .. filepath)
if not result then
result = false
end
return result
end
dtutils_file.libdoc.functions["chop_filetype"] = {
Name = [[chop_filetype]],
Synopsis = [[remove a filetype from a filename]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.chop_filetype(path)
path - string - a filename with or without a path]],
Description = [[chop_filetype removes the filetype from the filename]],
Return_Value = [[result - string - the path and filename without the filetype]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.chop_filetype(path)
local length = dtutils_file.get_filetype(path):len() + 2
return string.sub(path, 1, -length)
end
dtutils_file.libdoc.functions["file_copy"] = {
Name = [[file_copy]],
Synopsis = [[copy a file to another name/location]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.file_copy(fromFile, toFile)
fromFile - string - name of file to copy from
toFile - string - name of file to copy to]],
Description = [[copy a file using a succession of methods from operating system
to a pure lua solution]],
Return_Value = [[result - boolean - nil on error, true on success]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.file_copy(fromFile, toFile)
local result = nil
-- if cp exists, use it
if dtutils_file.check_if_bin_exists("cp") then
result = os.execute("cp '" .. fromFile .. "' '" .. toFile .. "'")
end
-- if cp was not present, or if cp failed, then a pure lua solution
if not result then
local fileIn, err = io.open(fromFile, 'rb')
if fileIn then
local fileOut, errr = io.open(toFile, 'w')
if fileOut then
local content = fileIn:read(4096)
while content do
fileOut:write(content)
content = fileIn:read(4096)
end
result = true
fileIn:close()
fileOut:close()
else
log.msg(log.error, errr)
end
else
log.msg(log.error, err)
end
end
return result
end
dtutils_file.libdoc.functions["file_move"] = {
Name = [[file_move]],
Synopsis = [[move a file from one directory to another]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.file_move(fromFile, toFile)
fromFile - string - name of the original file
toFile - string - the new file location and name]],
Description = [[Move a file from one place to another. Try a succession of methods from
builtin to operating system to a pure lua solution.]],
Return_Value = [[result - boolean - nil on error, some value on success]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.file_move(fromFile, toFile)
local success = os.rename(fromFile, toFile)
if not success then
-- an error occurred, so let's try using the operating system function
if dtutils_file.check_if_bin_exists("mv") then
success = os.execute("mv '" .. fromFile .. "' '" .. toFile .. "'")
end
-- if the mv didn't exist or succeed, then...
if not success then
-- pure lua solution
success = dtutils_file.file_copy(fromFile, toFile)
if success then
os.remove(fromFile)
else
log.msg(log.error, "Unable to move " .. fromFile .. " to " .. toFile .. ". Leaving " .. fromFile .. " in place.")
end
end
end
return success -- nil on error, some value if success
end
dtutils_file.libdoc.functions["filename_increment"] = {
Name = [[filename_increment]],
Synopsis = [[add a two digit increment to a filename]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.filename_increment(filepath)
filepath - string - filename to increment]],
Description = [[filename_increment solves the problem of filename confllict by adding an
increment to the filename. If the supplied filename has no increment then
"01" is added to the basename. If the filename already has an increment, then
1 is added to it and the filename returned.]],
Return_Value = [[result - string - the incremented filename]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.filename_increment(filepath)
-- break up the filepath into parts
local path = dtutils_file.get_path(filepath)
local basename = dtutils_file.get_basename(filepath)
local filetype = dtutils_file.get_filetype(filepath)
-- check to see if we've incremented before
local increment = string.match(basename, "_(%d-)$")
if increment then
-- we do 2 digit increments so make sure we didn't grab part of the filename
if string.len(increment) > 2 then
-- we got the filename so set the increment to 01
increment = "01"
else
increment = string.format("%02d", tonumber(increment) + 1)
basename = string.gsub(basename, "_(%d-)$", "")
end
else
increment = "01"
end
local incremented_filepath = path .. basename .. "_" .. increment .. "." .. filetype
return incremented_filepath
end
dtutils_file.libdoc.functions["create_unique_filename"] = {
Name = [[create_unique_filename]],
Synopsis = [[create a unique filename from the supplied argment]],
Usage = [[local df = require "lib/dtutils.file"
local result = df.create_unique_filename(filepath)
filepath - string - the path and filename requested]],
Description = [[create_unique_filename takes a requested filepath and checks to see if
it exists. If if doesn't then it's returned intact. If it already exists, then a two
digit increment is added to the filename and it is tested again. The increment keeps
increasing until either a unique filename is found or there have been 100 attempts.]],
Return_Value = [[result - string - the incremented filename]],
Limitations = [[create_unique_filename will only attempt 100 increments.]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_file.create_unique_filename(filepath)
while dtutils_file.check_if_file_exists(filepath) do
filepath = dtutils_file.filename_increment(filepath)
-- limit to 99 more exports of the original export
if string.match(dtfileutils.get_basename(filepath), "_(d-)$") == "99" then
break
end
end
return filepath
end
return dtutils_file

291
lib/dtutils/log.lua Normal file
View File

@@ -0,0 +1,291 @@
local dtutils_log = {}
dtutils_log.libdoc = {
Name = [[dtutils.log]],
Synopsis = [[darktable lua logging library]],
Usage = [[local log = require "lib/dtutils.log"]],
Description = [[log provides a multi-level logging solution for use with
the darktable lua scripts. With this library you can leave log messages
scattered through out your code and only turn them on as necessary.]],
Return_Value = [[log - library - the darktable lua logging functions]],
Limitations = [[]],
Example = [[local log = require "lib/dtutils.log"
local cur_level = log.log_level()
log.log_level(log.warn)
print out warning, error and success messages as code is running
log.log_level(debug)
print out debugging messages too because this isnt working
log.log_level(info)
I want to make sure this is working ok
log.log_level(cur_level)
reset the logging level back to normal]],
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 <http://www.gnu.org/licenses/>.]],
Copyright = [[Copyright (C) 2016 Bill Ferguson <wpferguson@gmail.com>.]],
functions = {}
}
local dt = require "darktable"
local dt_print_error = dt.print_error
-- work around until dt.print_log()
local dt_print_log = dt.print_error
local dt_print = dt.print
-- set the default log levels
dtutils_log.debug = {
label = "DEBUG:",
enabled = false,
engine = dt_print_log,
level = 1,
}
dtutils_log.info = {
label = "INFO:",
enable = false,
engine = dt_print_log,
level = 2,
}
dtutils_log.warn = {
label = "WARN:",
enabled = false,
engine = dt_print_log,
level = 3,
}
dtutils_log.error = {
label = "ERROR:",
enabled = true,
engine = dt_print_error,
level = 4,
}
dtutils_log.success = {
label = "SUCCESS:",
enabled = true,
engine = dt_print_log,
level = 5,
}
dtutils_log.screen = {
label = "",
enabled = true,
engine = dt_print,
level = 9,
}
dtutils_log.always = {
label = "",
enabled = true,
engine = dt_print_log,
level = 9,
}
dtutils_log.critical = {
label = "CRITICAL:",
enabled = true,
engine = print,
level = 9,
}
dtutils_log.libdoc.functions["caller"] = {
Name = [[caller]],
Synopsis = [[get the name and line number of the calling routine]],
Usage = [[local log = require "lib/dtutils.log"
result = log.caller(level)
level - number - the number of stack levels to go down to retrieve the caller routine information]],
Description = [[caller gets the name and line number of the calling routine and returns it]],
Return_Value = [[result - string - the name and line number of the calling function or 'callback: ' if the attempt to get the
caller returns nil]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_log.caller(level)
local name = debug.getinfo(level).name
local lineno = nil
local source = nil
if name then
lineno = debug.getinfo(level).currentline
-- returns the path to the file prefixed with @
source = debug.getinfo(level).source
-- we just need the filename, so grab it from the string
-- Thanks, Tobias Jakobs :-)
source = string.match(source, "@.-([^\\/]-%.?[^%.\\/]*)$")
return name .. ": " .. source .. ": " .. lineno .. ":"
else
return "callback:"
end
end
dtutils_log.libdoc.functions["msg"] = {
Name = [[msg]],
Synopsis = [[print a log message]],
Usage = [[local log = require "lib/log"
log.msg(level, ...)
level - table - the type of message, one of:
log.debug - debugging messages
log.info - informational messages
log.warn - warning messages
log.error - error messages
log.success - success messages
log.always - an internal message for debugging
log.screen - output 1 line of text to the screen
log.critical - print a critical message to the console
... - string(s) - the message to print, which could be a comma separated set of strings]],
Description = [[msg checks the level to see if it is enabled, then prints the level type and message if it is.
Messages are output using the engine configured in each log level.]],
Return_Value = [[]],
Limitations = [[If you use log.msg in a callback, the name of the calling routine can't be determined. A solution
is to include some means of reference such as the name of the callback as an argument, i.e.
log.msg(log.debug, "libPlugin.format_combobox:", "value is " .. self.value)
which would result in
DEBUG: callback: libPlugin.format_combobox: value is JPEG]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_log.msg(level, ...)
if level.enabled then
local args = {...}
local call_level = 3
if level == dtutils_log.always then
call_level = args[1]
table.remove(args, 1)
end
local log_msg = level.label
if level.engine ~= dt_screen and call_level ~= 0 then
log_msg = log_msg .. dtutils_log.caller(call_level) .. " "
elseif log_msg:len() > 2 then
log_msg = log_msg .. " "
end
for i = 1,#args do
log_msg = log_msg .. tostring(args[i]) .. " "
end
level.engine(log_msg)
end
end
dtutils_log.libdoc.functions["log_level"] = {
Name = [[log_level]],
Synopsis = [[get or set the log level]],
Usage = [[local log = require "lib/log"
local result = log.log_level(...)
... - arguments - if none is supplied, then the current log level is returned as one of:
log.debug, log.info, log.warn, log.error, log.success. If one of log.debug, log.info, log.warn,
log.error, or log.success is supplied as the argument then the log level is set to that value. All
log levels greater than or equal in value will be enabled. Any levels of lesser value will be disabled.]],
Description = [[log_level gets and sets the logging level. When called with no arguments the current log level
is returned as one of log.debug, log.info, log.warn, log.error, or log.success. When called with one of log.debug,
log.info, log.warn, log.error or log.success then the log level is set. When setting the log level all levels
equal or greater are enabled and any of lesser value are disabled. See the example.]],
Return_Value = [[result - the log level, one of log.debug, log.info, log.warn, log.error or log.success]],
Limitations = [[]],
Example = [[Assume that the current log level is log.error. Calling log.log_level() will return log.error.
Calling log.log_level(log.info) will leave log.debug disabled, and enable log.info, log.warn, log.error and
log.success. log.info will be returned as the log_level.]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_log.log_level(...)
local levels = {"debug", "info", "warn", "error", "success"}
local args = {...}
local log_level = nil
if #args > 0 then
log_level = args[1]
if log_level == dtutils_log.critical or
log_level == dtutils_log.screen or
log_level == dtutils_log.always then
-- these aren't valid for setting levels
return nil
else
for _,v in ipairs(levels) do
if dtutils_log[v].level >= log_level.level then
dtutils_log[v].enabled = true
else
dtutils_log[v].enabled = false
end
end
end
else
for _,v in ipairs(levels) do
if dtutils_log[v].enabled == true then
log_level = dtutils_log[v]
end
end
end
return log_level
end
dtutils_log.libdoc.functions["engine"] = {
Name = [[engine]],
Synopsis = [[get and set the output engine]],
Usage = [[local log = require "lib/dtutils.log"
result = log.engine(level, ...)
level - table - the log level to get or set the engine for, one of log.debug, log.info, log.warn, log.error
log.success, log.always, log.screen, log.critical
... - function - the output function, one of dt.print, dt.print_error, dt.print_log, print
if not function is included, the current engine is returned for the specified log level]],
Description = [[engine returns the output engine for the specified log level if a second argument is not
supplied. If a function is supplied as the second argment, then the output engine for the specified log level
is set to that.]],
Return_Value = [[result - function - the current output engine]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[]],
License = [[]],
Copyright = [[]],
}
function dtutils_log.print_engine(level, ...)
local engines = {"dt_print", "dt_print_error", "dt_print_log", "print"}
local args = {...}
local cur_engine = ""
if #args == 0 then
cur_engine = level.engine
else
for _,v in ipairs(engines) do
if args[1] == v then
level.engine = args[1]
cur_engine = level.engine
end
end
end
return cur_engine
end
return dtutils_log

173
lib/dtutils/string.lua Normal file
View File

@@ -0,0 +1,173 @@
local dtutils_string = {}
dtutils_string.libdoc = {
Name = [[dtutils.string]],
Synopsis = [[a library of string utilities for use in darktable lua scripts]],
Usage = [[local ds = require "lib/dtutils.string"]],
Description = [[This library contains string manipulation routines to aid in building
darktable lua scripts.]],
Return_Value = [[du - library - the darktable lua string 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 <http://www.gnu.org/licenses/>.]],
Copyright = [[Copyright (c) 2016 Bill Ferguson <wpferguson@gmail.com>]],
functions = {}
}
dtutils_string.libdoc.functions["strip_accents"] = {
Name = [[strip_accents]],
Synopsis = [[strip accents from characters]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.strip_accents(str)
str - string - the string with characters that need accents removed]],
Description = [[strip_accents removes accents from accented characters returning the
unaccented character.]],
Return_Value = [[result - string - the string containing unaccented characters]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[Copied from https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/]],
License = [[]],
Copyright = [[]],
}
function dtutils_string.strip_accents( str )
local tableAccents = {}
tableAccents["à"] = "a"
tableAccents["á"] = "a"
tableAccents["â"] = "a"
tableAccents["ã"] = "a"
tableAccents["ä"] = "a"
tableAccents["ç"] = "c"
tableAccents["è"] = "e"
tableAccents["é"] = "e"
tableAccents["ê"] = "e"
tableAccents["ë"] = "e"
tableAccents["ì"] = "i"
tableAccents["í"] = "i"
tableAccents["î"] = "i"
tableAccents["ï"] = "i"
tableAccents["ñ"] = "n"
tableAccents["ò"] = "o"
tableAccents["ó"] = "o"
tableAccents["ô"] = "o"
tableAccents["õ"] = "o"
tableAccents["ö"] = "o"
tableAccents["ù"] = "u"
tableAccents["ú"] = "u"
tableAccents["û"] = "u"
tableAccents["ü"] = "u"
tableAccents["ý"] = "y"
tableAccents["ÿ"] = "y"
tableAccents["À"] = "A"
tableAccents["Á"] = "A"
tableAccents["Â"] = "A"
tableAccents["Ã"] = "A"
tableAccents["Ä"] = "A"
tableAccents["Ç"] = "C"
tableAccents["È"] = "E"
tableAccents["É"] = "E"
tableAccents["Ê"] = "E"
tableAccents["Ë"] = "E"
tableAccents["Ì"] = "I"
tableAccents["Í"] = "I"
tableAccents["Î"] = "I"
tableAccents["Ï"] = "I"
tableAccents["Ñ"] = "N"
tableAccents["Ò"] = "O"
tableAccents["Ó"] = "O"
tableAccents["Ô"] = "O"
tableAccents["Õ"] = "O"
tableAccents["Ö"] = "O"
tableAccents["Ù"] = "U"
tableAccents["Ú"] = "U"
tableAccents["Û"] = "U"
tableAccents["Ü"] = "U"
tableAccents["Ý"] = "Y"
local normalizedString = ""
for strChar in string.gmatch(str, "([%z\1-\127\194-\244][\128-\191]*)") do
if tableAccents[strChar] ~= nil then
normalizedString = normalizedString..tableAccents[strChar]
else
normalizedString = normalizedString..strChar
end
end
return normalizedString
end
dtutils_string.libdoc.functions["escape_xml_characters"] = {
Name = [[escape_xml_characters]],
Synopsis = [[escape characters for xml documents]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.escape_xml_characters(str)
str - string - the string that needs escaped]],
Description = [[escape_xml_characters provides the escape sequences for
"&", '"', "'", "<", and ">" with the corresponding "&amp;",
"&quot;", "&apos;", "&lt;", and "&gt;".]],
Return_Value = [[result - string - the string containing escapes for the xml characters]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents]],
License = [[]],
Copyright = [[]],
}
-- Keep &amp; first, otherwise it will double escape other characters
function dtutils_string.escape_xml_characters( str )
str = string.gsub(str,"&", "&amp;")
str = string.gsub(str,"\"", "&quot;")
str = string.gsub(str,"'", "&apos;")
str = string.gsub(str,"<", "&lt;")
str = string.gsub(str,">", "&gt;")
return str
end
dtutils_string.libdoc.functions["urlencode"] = {
Name = [[urlencode]],
Synopsis = [[encode a string in a websage manner]],
Usage = [[local ds = require "lib/dtutils.string"
local result = ds.urlencode(str)
str - string - the string that needs to be made websafe]],
Description = [[urlencode converts a string into a websafe version suitable for
use in a web browser.]],
Return_Value = [[result - string - a websafe string]],
Limitations = [[]],
Example = [[]],
See_Also = [[]],
Reference = [[https://forums.coronalabs.com/topic/43048-remove-special-characters-from-string/]],
License = [[]],
Copyright = [[]],
}
function dtutils_string.urlencode(str)
if (str) then
str = string.gsub (str, "\n", "\r\n")
str = string.gsub (str, "([^%w ])", function () return string.format ("%%%02X", string.byte()) end)
str = string.gsub (str, " ", "+")
end
return str
end
return dtutils_string

View File

@@ -1,93 +0,0 @@
--[[
This file is part of darktable,
copyright (c) 2014 Jérémy Rosen
darktable 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.
darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
]]
--[[
DEBUG HELPERS
A collection of helper functions to help debugging lua scripts.
require it as
dhelpers = require "official/debug-helpers"
Each function is documented in its own header
]]
local dt = require "darktable"
local io = require "io"
local table = require "table"
require "darktable.debug"
local M = {} -- The actual content of the module
--[[
A function which prints its name and its parameters
* name : the name of the tracepoint
* ... : anything, will be dumped using dt.debug
]]
function M.tracepoint(name,...)
print("***** "..name.." ****")
params = {...}
print(dt.debug.dump(params,"parameters"))
return ...;
end
--[[
A function that returns a tracepoint function with the given name
This is mainly used to debug callbacks,
register_event(event, dhelpers.new_tracepoint("hit callback"))
will print the following each time the callback is called
**** hit callback ****
<all the callback's parameters dumped>
]]
function M.new_tracepoint(name)
return function(...) return M.tracepoint(name,...) end
end
--[[
Wrapper around debug.dump, will directly print to stdout,
same calling covention
]]
function M.dprint(...)
print(dt.debug.dump(...))
end
--[[
sets dt.debug.known to shorten all images to a single line
if you don't need to debug the content of images, this will avoid them flooding your logs
]]
function M.terse_dump()
for _,v in ipairs(dt.database) do
dt.debug.known[v] = tostring(v)
end
end
return M
-- vim: shiftwidth=2 expandtab tabstop=2 cindent
-- kate: tab-indents: off; indent-width 2; replace-tabs on; remove-trailing-space on;

View File

@@ -0,0 +1,76 @@
--[[
get_lib_manpages.lua - retrieve the included library documentation and output it as man pages
Copyright (c) 2016, Bill Ferguson
]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local log = require "lib/dtutils.log"
local libname = nil
dt.configuration.check_version(...,{3,0,0})
local keys = {"Name", "Synopsis", "Usage", "Description", "Return_Value", "Limitations",
"Example", "See_Also", "Reference", "License", "Copyright"}
local function output_man(d)
local name = d["Name"]
if not libname then
libname = name
end
local fname = "/tmp/" .. name .. ".3"
local mf = io.open(fname, "w")
if mf then
mf:write(".TH " .. string.upper(name) .. " 3 \"\" \"\" \"Darktable " .. libname .. " functions\"\n")
for _,section in ipairs(keys) do
if d[section]:len() > 0 then
mf:write(".SH " .. string.upper(string.gsub(section, "_", " ")) .. "\n")
mf:write(d[section] .. "\n")
end
end
mf:close()
if df.check_if_bin_exists("groff") then
if df.check_if_bin_exists("ps2pdf") then
os.execute("groff -man " .. fname .. " | ps2pdf - " .. fname .. ".pdf")
else
log.msg(log.error, "Missing ps2pdf. Can't generate pdf man pages.")
end
else
log.msg(log.error, "Missing groff. Can't generate pdf man pages.")
end
else
log.msg(log.error, "Can't open file " .. fname .. "for writing")
end
end
-- find the libraries
local output = io.popen("cd "..dt.configuration.config_dir.."/lua/lib ;find . -name \\*.lua -print | sort")
-- loop through the libraries
for line in output:lines() do
line = string.gsub(line, "/", ".")
local lib_name = line:sub(3,-5)
if lib_name:len() > 2 then
lib_name = "lib/" .. lib_name
local lib = require(lib_name)
-- print the documentation for the library
if lib.libdoc then
local doc = lib.libdoc
if doc then
output_man(doc)
for _,fdoc in pairs(doc.functions) do
-- print the documentation for each of the functions
output_man(fdoc)
end
end
end
end
libname = nil
end

49
tools/get_libdoc.lua Normal file
View File

@@ -0,0 +1,49 @@
--[[
get_libdoc.lua - retrieve the included library documentation and output it
Copyright (c) 2016, Bill Ferguson
]]
local dt = require "darktable"
dt.configuration.check_version(...,{3,0,0})
local keys = {"Name", "Synopsis", "Usage", "Description", "Return_Value", "Limitations",
"Example", "See_Also", "Reference", "License", "Copyright"}
local function output_doc(d)
for _,section in ipairs(keys) do
if d[section]:len() > 0 then
print(string.upper(string.gsub(section, "_", " ")))
print("\t" .. d[section] .. "\n")
end
end
print("\f")
end
-- find the libraries
local output = io.popen("cd "..dt.configuration.config_dir.."/lua/lib ;find . -name \\*.lua -print | sort")
-- loop through the libraries
for line in output:lines() do
line = string.gsub(line, "/", ".")
local lib_name = line:sub(3,-5)
if lib_name:len() > 2 then
lib_name = "lib/" .. lib_name
local lib = require(lib_name)
-- print the documentation for the library
if lib.libdoc then
local doc = lib.libdoc
output_doc(doc)
for _,fdoc in pairs(doc.functions) do
-- print the documentation for each of the functions
output_doc(fdoc)
end
end
end
end