diff options
Diffstat (limited to 'format.lua')
-rw-r--r-- | format.lua | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/format.lua b/format.lua new file mode 100644 index 0000000..0076fdf --- /dev/null +++ b/format.lua @@ -0,0 +1,512 @@ +--[[ + module for format changes in tables and strings + try to keep non input specific +]]-- + +module (..., package.seeall) + +-- find all return characters and removes them, may get this from a browser +-- that is why didn't do file specific + +function dostounix ( str ) + local data = string.gsub(str, "\r", "") + return data +end + +-- Escape Lua magic characters +function escapemagiccharacters ( str ) + return (string.gsub(str or "", "[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")) +end + +-- Escape shell special characters +function escapespecialcharacters ( str ) + return (string.gsub(str or "", "[~`#%$&%*%(%)\\|%[%]{};\'\"<>/]", "\\%1")) +end + +-- search and remove all blank and commented lines from a string or table of lines +-- returns a table to iterate over without the blank or commented lines + +function parse_lines ( input, comment ) + local lines = {} + comment = comment or "#" + + function parse(line) + if not string.match(line, "^%s*$") and not string.match(line, "^%s*"..comment) then + lines[#lines + 1] = line + end + end + + if type(input) == "string" then + for line in string.gmatch(input, "([^\n]*)\n?") do + parse(line) + end + elseif type(input) == "table" then + for i,line in ipairs(input) do + parse(line) + end + end + + return lines +end + +-- search and remove all blank and commented lines from a string or table of lines +-- parse the lines for words, looking for quotes and removing comments +-- returns a table with an array of words for each line + +function parse_linesandwords ( input, comment ) + local lines = {} + local linenum = 0 + comment = comment or "#" + + function parse(line) + linenum = linenum + 1 + if not string.match(line, "^%s*$") and not string.match(line, "^%s*"..comment) then + local linetable = {linenum=linenum, line=line} + local offset = 1 + while string.find(line, "%S", offset) do + local word = string.match(line, "%S+", offset) + local endword + if string.find(word, "^"..comment) then + break + elseif string.find(word, "^\"") then + endword = select(2, string.find(line, "\"[^\"]*\"", offset)) + word = string.sub(line, string.find(line, "\"", offset), endword) + else + endword = select(2, string.find(line, "%S+", offset)) + end + table.insert(linetable, word) + offset = endword + 1 + end + lines[#lines + 1] = linetable + end + end + + if type(input) == "string" then + for line in string.gmatch(input, "([^\n]*)\n?") do + parse(line) + end + elseif type(input) == "table" then + for i,line in ipairs(input) do + parse(line) + end + end + + return lines +end + +-- returns a table with label value pairs + +function parse_configfile( input, comment ) + local config = {} + local lines = parse_linesandwords(input, comment) + + for i,linetable in ipairs(lines) do + config[linetable[1]] = table.concat(linetable, " ", 2) or "" + end + return config +end + +-- search and replace through a table +-- string is easy string.gsub(string, find, replace) + +function search_replace (input, find, replace) + local lines = {} + for i,line in ipairs(input) do + lines[#lines + 1] = string.gsub(line, find, replace) + end + return lines +end + +-- great for line searches through a file. /etc/conf.d/ ??? +-- might be looking for more than one thing so will return a table +-- will likely want to match whole line entries +-- so we change find to include the rest of the line +-- say want all the _OPTS from a file format.search_for_lines (fs.read_file("/etc/conf.d/cron"), "OPT") +-- if want to avoid commented lines, call parse_lines first + +function search_for_lines (input, find) + local lines = {} + + function findfn(line) + if string.find(line, find) then + lines[#lines + 1] = line + end + end + + if type(input) == "string" then + for line in string.gmatch(input, "([^\n]*)\n?") do + findfn(line) + end + elseif type(input) == "table" then + for i,line in ipairs(input) do + findfn(line) + end + end + + return lines +end + +--string format function to capitalize the beginging of each word. +function cap_begin_word ( str ) + --first need to do the first word + local data = string.gsub(str, "^%l", string.upper) + --word is any space cause no <> regex + data = string.gsub(data, "%s%l", string.upper) + return data +end + +--for cut functionality do something like +--print(format.string_to_table("This is a test", " ")[2]) +--gives you the second field which is .... is + +-- This code comes from http://lua-users.org/wiki/SplitJoin +-- example: format.string_to_table( "Anna, Bob, Charlie,Dolores", ",%s*") +function string_to_table ( text, delimiter) + local list = {} + if text then + -- this would result in endless loops + if string.find("", delimiter) then + -- delimiter matches empty string! + for i=1,#text do + list[#list + 1] = string.sub(text, i, i) + end + else + local pos = 1 + while 1 do + local first, last = string.find(text, delimiter, pos) + if first then -- found? + table.insert(list, string.sub(text, pos, first-1)) + pos = last+1 + else + table.insert(list, string.sub(text, pos)) + break + end + end + end + end + return list +end + + +-- Takes a str and expands any ${...} constructs with the Lua variable +-- ex: a="foo"; print(expand_bash_syntax_vars("a=${a}) - > "a=foo" +expand_bash_syntax_vars = function (str) + local deref = function (f) + local v = getfenv(3) -- get the upstream global env + for w in string.gfind(f, "[%w_]+") do + if v then v = v[w] end + end + return v + end + + for w in string.gmatch (str, "${[^}]*}" ) do + local rvar = string.sub(w,3,-2) + local rval = ( deref(rvar) or "nil" ) + str = string.gsub (str, w, escapespecialcharacters(rval)) + end + + return (str) +end + +-- Removes the linenum line from str and replaces it with line. +-- Do nothing if doesn't exist +-- Set line to nil to remove the line +function replace_line(str, linenum, line) + -- Split the str to remove the line + local startchar, endchar = string.match(str, "^" .. string.rep("[^\n]*\n", linenum-1) .. "()[^\n]*\n?()") + if startchar and endchar then + local lines = {} + lines[1] = string.sub(str, 1, startchar-1) + lines[2] = string.sub(str, endchar, -1) + if line then + table.insert(lines, 2, line .. "\n") + end + str = table.concat(lines) + end + return str +end + +-- Inserts the line into the str after the linenum (or at the end) +function insert_line(str, linenum, line) + -- Split the str to remove the line + local startchar = string.match(str, "^" .. string.rep("[^\n]*\n", linenum) .. "()") + local lines = {} + if startchar then + lines[1] = string.sub(str, 1, startchar-1) + lines[2] = string.sub(str, startchar, -1) + else + lines[1] = str + end + if line then + table.insert(lines, 2, line .. "\n") + end + str = table.concat(lines) + return str +end + +function get_line(str, linenum) + -- Split the str to remove the line + local startchar, endchar = string.match(str, "^" .. string.rep("[^\n]*\n", linenum-1) .. "()[^\n]*()") + local line + if startchar and endchar then + line = string.sub(str, startchar, endchar-1) + end + return line +end + +-- Search the option string for separate options (-x or --xyz) and put them in a table +function opts_to_table ( optstring, filter ) + local optsparams + if optstring then + local optstr = optstring .. " " + for o in string.gmatch(optstr, "%-%-?%a+%s+[^-%s]*") do + local option = string.match(o, "%-%-?%a+") + if not filter or filter == option then + if not optsparams then optsparams = {} end + optsparams[option] = string.match(o, "%S*$") + end + end + end + return optsparams +end + +-- Go through an options table and create the option string +function table_to_opts ( optsparams ) + local optstring = {} + for opt,val in pairs(optsparams) do + optstring[#optstring + 1] = opt + if val ~= "" then + optstring[#optstring + 1] = val + end + end + return table.concat(optstring, " ") +end + +-- The following functions deal with ini files. ini files contain comments, sections, names and values +-- commented lines begin with '#' or ';', in-line comments begin with '#' and run to the end of the line +-- sections are defined by "[section]" on a line. Anything before the first section definition is in section "" +-- name value pairs are defined by "name = value". Names and values may contain spaces but not '#' +-- lines ending with '\' are continued on the next line + + +-- Set a name=value pair in a string +-- If search_section is undefined or "", goes in the default section +-- If value is defined we put "search_name=value" into search_section +-- If value is undefined, we clear search_name out of search section +-- Try not to touch anything but the value we're interested in (although will combine multi-line into one) +-- If the search_section is not found, we'll add it at the end of the string +-- If the search_name is not found, we'll add it at the end of the section +function update_ini_file (file, search_section, search_name, value) + if not file or not search_name or search_name == "" then + return file, false + end + + search_section = search_section or "" + local new_conf_file = {} + local section = "" + local done = false + local skip_lines = {} + for l in string.gmatch(file, "([^\n]*)\n?") do + if done == false then + if string.find ( l, "\\%s*$" ) then + skip_lines[#skip_lines+1] = string.match(l, "^(.*)\\%s*$") + l = nil + else + if #skip_lines then + skip_lines[#skip_lines+1] = l + l = table.concat(skip_lines, " ") + end + -- check if comment line + if not string.find ( l, "^%s*[#;]" ) then + -- find section name + local a = string.match ( l, "^%s*%[%s*(%S+)%s*%]" ) + if a then + -- we reached a new section, if we were in the one we wanted + -- we have to add in the name:value pair now + if (search_section == section) then + new_conf_file[#new_conf_file + 1] = search_name.."="..value + done = true + end + section = a + elseif (search_section == section) then + -- find name + a = string.match ( l, "^%s*([^=]*%S)%s*=" ) + if a and (search_name == a) then + -- We found the name, change the value, keep any comment + local comment = string.match(l, " #.*$") or "" + l = search_name.."="..value..comment + skip_lines = {} -- replacing line + done = true + end + end + end + if #skip_lines > 0 then + for i,line in ipairs(skip_lines) do + new_conf_file[#new_conf_file + 1] = line + end + skip_lines = {} + l = nil + end + end + end + new_conf_file[#new_conf_file + 1] = l + end + + if done == false then + -- we didn't find the section:name, add it now + if section ~= search_section then + new_conf_file[#new_conf_file + 1] = '[' .. search_section .. ']' + end + new_conf_file[#new_conf_file + 1] = search_name.."="..value + end + + file = table.concat(new_conf_file, '\n') + + return file, true +end + +-- Parse string for name=value pairs, returned in a table +-- If search_section is defined, only report values in matching section +-- If search_name is defined, only report matching name (possibly in multiple sections) +function parse_ini_file (file, search_section, search_name) + if not file or file == "" then + return nil + end + local opts = nil + local section = "" + local skip_lines = {} + for l in string.gmatch(file, "([^\n]*)\n?") do + if string.find ( l, "\\%s*$" ) then + skip_lines[#skip_lines+1] = string.match(l, "^(.*)\\%s*$") + else + if #skip_lines then + skip_lines[#skip_lines+1] = l + l = table.concat(skip_lines, " ") + skip_lines = {} + end + -- check if comment line + if not string.find ( l, "^%s*[#;]" ) then + -- find section name + local a = string.match ( l, "^%s*%[%s*(%S+)%s*%]" ) + if a then + if (search_section == section) then break end + section = a + elseif not (search_section) or (search_section == section) then + -- find name + a = string.match ( l, "^%s*([^=]*%S)%s*=" ) + if a and (not (search_name) or (search_name == a)) then + -- Figure out the value + local b = string.match ( l, '=%s*(.*)$' ) or "" + -- remove comments from end of line + if string.find ( b, '#' ) then + b = string.match ( b, '^(.*)#.*$' ) or "" + end + -- remove spaces from front and back + b = string.gsub ( b, '%s+$', '' ) + if not (opts) then opts = {} end + if not (opts[section]) then opts[section] = {} end + opts[section][a] = b + end + end + end + end + end + + if opts and search_section and search_name then + return opts[search_section][search_name] + elseif opts and search_section then + return opts[search_section] + end + return opts +end + +function get_ini_section (file, search_section) + if not file then + return nil + end + search_section = search_section or "" + local sectionlines = {} + local section = "" + for l in string.gmatch(file, "([^\n]*)\n?") do + -- find section name + local a = string.match ( l, "^%s*%[%s*(%S+)%s*%]" ) + if a then + if (search_section == section) then break end + section = a + elseif (search_section == section) then + sectionlines[#sectionlines + 1] = l + end + end + + return table.concat(sectionlines, "\n") +end + +function set_ini_section (file, search_section, section_content) + if not file then + return file, false + end + search_section = search_section or "" + section_content = section_content or "" + local new_conf_file = {} + local done = false + local section = "" + if search_section == "" then new_conf_file[1] = section_content end + for l in string.gmatch(file, "([^\n]*)\n?") do + -- find section name + if not done then + local a = string.match ( l, "^%s*%[%s*(%S+)%s*%]" ) + if a then + if (search_section == section) then + done = true + else + section = a + if (search_section == section) then + l = l .. "\n" .. section_content + end + end + elseif (search_section == section) then + l = nil + end + end + new_conf_file[#new_conf_file + 1] = l + end + + if not done then + -- we didn't find the section, add it now + if section ~= search_section then + new_conf_file[#new_conf_file + 1] = '[' .. search_section .. ']' + new_conf_file[#new_conf_file + 1] = section_content + end + end + + file = table.concat(new_conf_file, '\n') + + return file, true +end + +-- Find the value of an entry allowing for parent section and $variables +-- the file parameter can be a string or structure returned by parse_ini_file +-- beginning and ending quotes are removed +-- returns value or "" if not found +function get_ini_entry (file, section, value) + local opts = file + if not file or not value then + return nil + elseif type(file) == "string" then + opts = parse_ini_file(file) + end + section = section or "" + local result = opts[section][value] + if not result then + section = "" + result = opts[section][value] or "" + end + while string.find(result, "%$[%w_]+") do + local sub = string.match(result, "%$[%w_]+") + result = string.gsub(result, escapemagiccharacters(sub), get_ini_entry(opts, section, sub)) + end + if string.find(result, '^"') and string.find(result, '"$') then + result = string.sub(result, 2, -2) + end + return result +end |