-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. -- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md --- helper scripts -- @module helpers local lfs = require('lfs') local json = require('lunajson') local helpers = {} local function dirtree(dir) assert(dir and dir ~= "", "directory parameter is missing or empty") if string.sub(dir, -1) == "/" then dir = string.sub(dir, 1, -2) end local function yieldtree(dir) for entry in lfs.dir(dir) do if entry ~= "." and entry ~= ".." then entry = dir .. "/" .. entry local attr = lfs.attributes(entry) coroutine.yield(entry, attr) if attr.mode == "directory" then yieldtree(entry) end end end end return coroutine.wrap(function() yieldtree(dir) end) end local function ends_with(str, ending) return ending == "" or str:sub(-#ending) == ending end local function starts_with(str, start) return str:sub(1, #start) == start end local function build_path(prefix, name) if not ends_with(prefix, '/') then prefix = prefix .. '/' end if starts_with(name, '/') then name = name:sub(2, -1) end return prefix .. name end local function strip_from_prefix(prefix, path) local name = path:gsub(prefix, "") name = name:sub(2, -1) return name end local function basedir(p) return p:gsub('[^\\/]+[\\/]?$', '') end --- Copy file -- @function copy_file -- @param filename_in input file -- @param filename_out output file function helpers.copy_file(filename_in, filename_out) local size = 1024 * 512 local size_in = lfs.attributes(filename_in, "size") local fd_in = assert(io.open(filename_in, "r")) local fd_out = assert(io.open(filename_out, "w")) collectgarbage() while true do local block = fd_in:read(size) if not block then break end assert(fd_out:write(block)) end fd_in:close() fd_out:close() end --- Remove directory and its contents -- @function rmdir -- @param dir directory to remove function helpers.rmdir(dir) for file in lfs.dir(dir) do local file_path = dir .. "/" .. file if file ~= "." and file ~= ".." then if lfs.attributes(file_path, "mode") == "file" then assert(os.remove(file_path)) elseif lfs.attributes(file_path, "mode") == "directory" then helpers.rmdir(file_path) end end end lfs.rmdir(dir) end --- Remove directory content -- @function rmdir_content -- @param dir directory path function helpers.rmdir_content(dir) for file in lfs.dir(dir) do local file_path = dir .. "/" .. file if file ~= "." and file ~= ".." then if lfs.attributes(file_path, "mode") == "file" then assert(os.remove(file_path)) elseif lfs.attributes(file_path, "mode") == "directory" then helpers.rmdir(file_path) end end end end --- Remove all files from directory without touching internal directories -- @function rm_files_from_dir -- @param dir directory to remove files from function helpers.rm_files_from_dir(dir) for file in lfs.dir(dir) do local file_path = dir .. "/" .. file if file ~= "." and file ~= ".." then if lfs.attributes(file_path, "mode") == "file" then assert(os.remove(file_path)) end end end end --- Copy directory recursively -- @function copy_dir -- @param from source directory -- @param where target directory function helpers.copy_dir(from, where) for filename, attr in dirtree(from) do local name = filename:gsub(from, "") if attr.mode == "directory" then assert(lfs.mkdir(build_path(where, name))) else helpers.copy_file(filename, build_path(where, name)) end end end --- Move directory recursively -- @function move_dir -- @param from source directory -- @param where target directory function helpers.move_dir(from, where) for filename, attr in dirtree(from) do local name = filename:gsub(from, "") if attr.mode == "directory" then assert(lfs.mkdir(build_path(where, name))) else assert(os.rename(build_path(from, name), build_path(where, name))) end end end --- Copy directory recursively using regex filter -- @function copy_dir_filtered -- @param from source directory -- @param where target directory -- @param matcher regex expression function helpers.copy_dir_filtered(from, where, filter) for filename, attr in dirtree(from) do local name = strip_from_prefix(from, filename) if name:match(filter) then if attr.mode == "directory" then assert(lfs.mkdir(build_path(where, name))) else helpers.copy_file(filename, build_path(where, name)) end end end end --- Get the size of specified directory using regex filter -- @function dir_size_filtered -- @param path directory path -- @param matcher regex expression -- @return total size of directory in bytes function helpers.dir_size_filtered(path, filter) local total_size = 0 for filename, attr in dirtree(path) do local name = strip_from_prefix(path, filename) if name:match(filter) then total_size = total_size + attr.size end end return total_size end --- Get the size of specified directory -- @function dir_size -- @param path directory path -- @return total size of directory in bytes function helpers.dir_size(path) return helpers.dir_size_regex('.*') end --- Check if specified dir/file exists -- @function exists -- @param path directory or file path -- @return true or false function helpers.exists(path) local ret = lfs.attributes(path) return ret ~= nil end --- Read the whole file at once -- @function read_whole_file -- @param file file path -- @return file contents function helpers.read_whole_file(file) local f = assert(io.open(file, "rb")) local content = assert(f:read("*a")) f:close() return content end --- Extract filename from filename path -- @function get_filename -- @param file file path -- @return filename function helpers.get_filename(file) return file:match("^.+/(.+)$") end --- Extract file extension from filename -- @function get_file_extension -- @param file file path -- @return file extension function helpers.get_file_extension(file) return file:match("^.+(%..+)$") end --- Strips file name from its extension -- @function strip_from_extension -- @param file file path -- @return file name without extension function helpers.strip_from_extension(file) return file:match("^(.*)%..*$") end --- Create directory and all required subdirectories -- @function mkdirp -- @param file file path function helpers.mkdirp(p) if lfs.attributes(p, 'mode') == 'directory' then return nil, 'already exists' end local b = basedir(p) if #b > 0 and lfs.attributes(b, 'mode') ~= 'directory' then local r, m = helpers.mkdirp(b) if not r then return r, m end end return lfs.mkdir(p) end --- Get OS version from the 'version.json' -- @function get_os_version -- @param file file path function helpers.get_os_version(file) local contents = helpers.read_whole_file(file) local root = json.decode(contents) return root.os.version end return helpers