diff options
author | Ted Trask <ttrask01@yahoo.com> | 2009-12-31 14:14:17 +0000 |
---|---|---|
committer | Ted Trask <ttrask01@yahoo.com> | 2009-12-31 14:14:17 +0000 |
commit | 30e3d9b315164804de9738efebf4d6aaaad50197 (patch) | |
tree | 0dc401aea8a81625e0fa3389e0b831fdfc7f4875 | |
download | acf-lib-30e3d9b315164804de9738efebf4d6aaaad50197.tar.bz2 acf-lib-30e3d9b315164804de9738efebf4d6aaaad50197.tar.xz |
Lua libraries for standard functions moved out of acf-core 0.9.0v0.1.0
-rw-r--r-- | Makefile | 42 | ||||
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | apk.lua | 44 | ||||
-rw-r--r-- | date.lua | 328 | ||||
-rw-r--r-- | format.lua | 512 | ||||
-rw-r--r-- | fs.lua | 222 | ||||
-rw-r--r-- | html.lua | 262 | ||||
-rw-r--r-- | processinfo.lua | 221 | ||||
-rw-r--r-- | validator.lua | 172 |
9 files changed, 1804 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5a93e73 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +APP_NAME=lib +PACKAGE=acf-$(APP_NAME) +VERSION=0.1.0 + +APP_DIST=\ + *.lua\ + + +EXTRA_DIST=README Makefile + +DISTFILES=$(APP_DIST) $(EXTRA_DIST) + +TAR=tar + +P=$(PACKAGE)-$(VERSION) +tarball=$(P).tar.bz2 +install_dir=/usr/share/lua/5.1/ + +all: +clean: + rm -rf $(tarball) $(P) + +dist: $(tarball) + +install: + mkdir -p "$(install_dir)" + cp -a $(APP_DIST) "$(install_dir)" + +$(tarball): $(DISTFILES) + rm -rf $(P) + mkdir -p $(P) + cp -a $(DISTFILES) $(P) + $(TAR) -jcf $@ $(P) + rm -rf $(P) + +# target that creates a tar package, unpacks is and install from package +dist-install: $(tarball) + $(TAR) -jxf $(tarball) + $(MAKE) -C $(P) install DESTDIR=$(DESTDIR) + rm -rf $(P) + +.PHONY: all clean dist install dist-install @@ -0,0 +1 @@ +ACF libraries for standard usage @@ -0,0 +1,44 @@ +-- apk library +module (..., package.seeall) + +local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " + +delete = function(package) + local success = false + local cmdresult + local cmd = path .. "apk del " .. package .. " 2>&1" + local f = io.popen( cmd ) + cmdresult = f:read("*a") or "" + f:close() + if string.find(cmdresult, "^OK") then + cmdresult = "ERROR: Package not found\n"..cmdresult + elseif not string.find(cmdresult, "ERROR") then + success = true + end + return success, cmdresult +end + +install = function(package) + local success = true + local cmdresult + local cmd = path .. "apk add " .. package .. " 2>&1" + local f = io.popen( cmd ) + cmdresult = f:read("*a") + f:close() + if string.find(cmdresult, "^ERROR") then + success = false + end + return success, cmdresult +end + +version = function(package) + local cmdresult + local cmd = path .. "apk info -ve " .. package .. " 2>&1" + local f = io.popen( cmd ) + cmdresult = f:read("*a") + f:close() + if string.find(cmdresult, "^%s*$") then + cmdresult = nil + end + return cmdresult +end diff --git a/date.lua b/date.lua new file mode 100644 index 0000000..09592fc --- /dev/null +++ b/date.lua @@ -0,0 +1,328 @@ +--date and time functions + +module(..., package.seeall) + +require("posix") +require("format") +require("fs") + +--global for date formating see below for more information +--Mon Nov 26 19:56:10 UTC 2007 looks like most systems use this +--print(os.date(date.format)) +formats = "%a %b %d %X %Z %Y" + +months ={ {"January","Jan"}, + {"February", "Feb"}, + {"March","Mar"}, + {"April", "Apr"}, + {"May","May"}, + {"June","Jun"}, + {"July","Jul"}, + {"August","Aug"}, + {"September","Sep"}, + {"October","Oct"}, + {"November","Nov"}, + {"December","Dec"} + } + +revmonths = {["january"] = 1, ["jan"] = 1, + ["february"] = 2, ["feb"] = 2, + ["march"] = 3, ["mar"] = 3, + ["april"] = 4, ["apr"] = 4, + ["may"] = 5, + ["june"] = 6, ["jun"] = 6, + ["july"] = 7, ["jul"] = 7, + ["august"] = 8, ["aug"] = 8, + ["september"] = 9, ["sep"] = 9, + ["october"] = 10, ["oct"] = 10, + ["november"] = 11, ["nov"] = 11, + ["december"] = 12, ["dec"] = 12 + } + +dow = { {"Sunday","Sun"}, + {"Monday","Mon"}, + {"Tuesday","Tue"}, + {"Wednesday","Wed"}, + {"Thursday","Thu"}, + {"Friday","Fri"}, + {"Saturday","Sat"} + } + +revdow = { ["sunday"] = 1, ["sun"] = 2, + ["monday"] = 2, ["mon"] = 2, + ["tuesday"] = 3, ["tue"] = 3, + ["wednesday"] = 4, ["wed"] = 4, + ["thursday"] = 5, ["thu"] = 5, + ["friday"] = 6, ["fri"] = 6, + ["saturday"] = 7, ["sat"] =7 + } + +-- + usually denotes right of PM and - means left. It seems that /etc/TZ needs these +--reversed for alpine/busybox. difference in col 2 and 5 +--this list is not full. May need some more added. No Africa or Asia +--Abrr TZ,Real Offset, FullName, Location, What would be put in /etc/TZ(busybox needed offset) + +timezones = { + +{"A","+1","Alpha Time Zone","Military","Alpha-1"}, +{"ACDT","+10:30","Australian Central Daylight Time","Australia","ACDT-10:30"}, +{"ACST","+9:30","Australian Central Standard Time","Australia","ACST-9:30"}, +{"ADT","-3","Atlantic Daylight Time","North America","ADT+3"}, +{"AEDT","+11","Australian Eastern Daylight Time","Australia","AEDT-11"}, +{"AEST","+10","Australian Eastern Standard Time","Australia","AEST-10"}, +{"AKDT","-8","Alaska Daylight Time","North America","AKDT+8"}, +{"AKST","-9","Alaska Standard Time","North America","AKST+9"}, +{"AST","-4","Atlantic Standard Time","North America","AST+4"}, +{"AWDT","+9","Australian Western Daylight Time","Australia","AWDT-9"}, +{"AWST","+8","Australian Western Standard Time","Australia","AWST-8"}, + +{"B","+2","Bravo Time Zone","Military","Bravo-2"}, +{"BST","+1","British Summer Time","Europe","BST-1"}, + +{"C","+3","Charlie Time Zone","Military","Charlie-3"}, +{"CDT","-5","Central Daylight Time","North America","CDT+5"}, +{"CEDT","+2","Central European Daylight Time","Europe","CEDT-2"}, +{"CEST","+2","Central European Summer Time","Europe","CEST-2"}, +{"CET","+1","Central European Time","Europe","CET-1"}, +{"CST","+10:30","Central Summer(Daylight) Time","Australia","CST-10:30"}, +{"CST","+9:30","Central Standard Time","Australia","CST-9:30"}, +{"CST","-6","Central Standard Time","North America","CST+6"}, +{"CXT","+7","Christmas Island Time","Australia","CXT-7"}, + +{"D","+4","Delta Time Zone","Military","Delta-4"}, + +{"E","+5","Echo Time Zone","Military","Echo-5"}, +{"EDT","-4","Eastern Daylight Time","North America","EDT+4"}, +{"EEDT","+3","Eastern European Daylight Time","Europe","EEDT-3"}, +{"EEST","+3","Eastern European Summer Time","Europe","EEST-3"}, +{"EET","+2","Eastern European Time","Europe","EET-2"}, +{"EST","+11","Eastern Summer(Daylight) Time","Australia","EST-11"}, +{"EST","+10","Eastern Standard Time","Australia","EST-10"}, +{"EST","-5","Eastern Standard Time","North America","EST+5"}, + +{"F","+6","Foxtrot Time Zone","Military","Foxtrot-6"}, + +{"G","+7","Golf Time Zone","Military","Golf-7"}, +{"GMT","+0","Greenwich Mean Time","Europe","GMT+0"}, + +{"H","+8","Hotel Time Zone","Military","Hotel-8"}, +{"HAA","-3","Heure Avancée de l'Atlantique","North America","HAA+3"}, +{"HAC","-5","Heure Avancée du Centre","North America","HAC+5"}, +{"HADT","-9","Hawaii-Aleutian Daylight Time","North America","HADT+9"}, +{"HAE","-4","Heure Avancée de l'Est","North America","HAE+4"}, +{"HAP","-7","Heure Avancée du Pacifique","North America","HAP+7"}, +{"HAR","-6","Heure Avancée des Rocheuses","North America","HAR+6"}, +{"HAST","-10","Hawaii-Aleutian Standard Time","North America","HAST+10"}, +{"HAT","-2:30","Heure Avancée de Terre-Neuve","North America","HAT+2:30"}, +{"HAY","-8","Heure Avancée du Yukon","North America","HAY+8"}, +{"HNA","-4","Heure Normale de l'Atlantique","North America","HNA+4"}, +{"HNC","-6","Heure Normale du Centre","North America","HNC+6"}, +{"HNE","-5","Heure Normale de l'Est","North America","HNE+5"}, +{"HNP","-8","Heure Normale du Pacifique","North America","HNP+8"}, +{"HNR","-7","Heure Normale des Rocheuses","North America","HNR+7"}, +{"HNT","-3:30","Heure Normale de Terre-Neuve","North America","HNT+3:30"}, +{"HNY","-9","Heure Normale du Yukon","North America","HNY+9"}, + +{"I","+9","India Time Zone","Military","India-9"}, +{"IST","+1","Irish Summer Time","Europe","IST-1"}, + +{"K","+10","Kilo Time Zone","Military","Kilo-10"}, + +{"L","+11","Lima Time Zone","Military","Lima-11"}, + +{"M","+12","Mike Time Zone","Military","Mike-12"}, +{"MDT","-6","Mountain Daylight Time","North America","MDT+6"}, +{"MESZ","+2","Mitteleuroäische Sommerzeit","Europe","MESZ-2"}, +{"MEZ","+1","Mitteleuropäische Zeit","Europe","MEZ-1"}, +{"MST","-7","Mountain Standard Time","North America","MST+7"}, + +{"N","-1","November Time Zone","Military","November+1"}, +{"NDT","-2:30","Newfoundland Daylight Time","North America","NDT+2:30"}, +{"NFT","+11:30","Norfolk (Island) Time","Australia","NFT-11:30"}, +{"NST","-3:30","Newfoundland Standard Time","North America","NST+3:30"}, + +{"O","-2","Oscar Time Zone","Military","Oscar+2"}, + +{"P","-3","Papa Time Zone","Military","Papa+3"}, +{"PDT","-7","Pacific Daylight Time","North America","PDT+7"}, +{"PST","-8","Pacific Standard Time","North America","PST+8"}, + +{"Q","-4","Quebec Time Zone","Military","Quebec+4"}, + +{"R","-5","Romeo Time Zone","Military","Romeo+5"}, + +{"S","-6","Sierra Time Zone","Military","Sierra+6"}, + +{"T","-7","Tango Time Zone","Military","Tango+7"}, + +{"U","-8","Uniform Time Zone","Military","Uniform+8"}, +{"UTC","+0","Coordinated Universal Time","Europe","UTC+0"}, + +{"V","-9","Victor Time Zone","Military","Victor+9"}, + +{"W","-10","Whiskey Time Zone","Military","Whiskey+10"}, +{"WEDT","+1","Western European Daylight Time","Europe","WEDT-1"}, +{"WEST","+1","Western European Summer Time","Europe","WEST-1"}, +{"WET","+0","Western European Time","Europe","WET+0"}, +{"WST","+9","Western Summer(Daylight) Time","Australia","WST-9"}, +{"WST","+8","Western Standard Time","Australia","WST-8"}, + +{"X","-11","X-ray Time Zone","Military","X-ray+11"}, + +{"Y","-12","Yankee Time Zone","Military","Yankee+12"}, + +{"Z","+0","Zulu Time Zone","Military","Zulu+0"} + +} + +--os.time() will give seconds since 1970-epoch +--os.date() will give formated time strings +--os.time{year=2007,month=1,day=1,hour=2,min=1,sec=1} +--os.date(date.format,os.time()) + +--give me a table +--t = { {year=2007,month=1,day=2,hour=2}, {year=2006,month=1,day=5} } +--will return a table sorted by oldest <-> newest +--to grab the largest and smallest a,b=g[1],g[table.maxn(g)] +function date_to_seconds (t) + g = {} + count = table.maxn(t) + for i = 1,count do + g[#g+1] = os.time(t[i]) + end + table.sort(g) + return g +end + +-- the reverse of date_to_seconds. expecting a table of seconds +--format can be changed. This seems to be standard, dow,mon,dom,time,zone,year +-- seems like %z- +0000 time zone format and %Z- 3 letter timezone undocumented or new + +function seconds_to_date (t) + g = {} + count = table.maxn(t) + for i = 1,count do + g[#g+1] = os.date(formats,t[i]) + end + + return g +end + +--Wed Nov 28 14:01:23 UTC 2007 +--os.date(date.formats) put into a table +--year,month,day,hour,min,sec,isdst- may need a dst table to set this automatically +function string_to_table (str) + if str == nil then str = os.date(formats) end + g = {} + temp = format.string_to_table(str,"%s") + month = abr_month_num(temp[2]) + g["month"] = month + day = temp[3] + g["day"] = day + --may do something with this if have a tz table ?? + tz = temp[5] + year = temp[6] + g["year"] = year + temp2 = format.string_to_table(temp[4],":") + hour = temp2[1] + g["hour"] = hour + min = temp2[2] + g["min"] = min + sec = temp2[3] + g["sec"] = sec + return g + +end + + +--give dates in seconds and gives the difference in years,months,days,... +--gives a table back with hour,min,month,sec,day,year to display something like +--you have 10 years, 14 hours, 10 days to renew you certificate +-- in secs - year, day, hour,min,sec +t_time = { field_names = {"years","days","hours","minutes","seconds"}, + 31556926,86400,3600,60,1 + } + +function date_diff (d1, d2) + g = {} + if d2 == nil then d2 = os.time() end + --first sum of seconds + sum = math.abs(os.difftime(d1,d2)) + --going to go through and get it smaller with each pass through the table + for a,b in ipairs(t_time) do + print(sum) + hold = math.modf(sum/b) + g[t_time.field_names[a]] = hold + sum = (sum - (hold*b)) + end + + return g +end + +--give a search number and return the month name + +function num_month_name (search) + return months[search][1] +end + +--give a search number and return the month abr + +function num_month_name_abr (search) + return months[search][2] +end + +function name_month_num (search) + return revmonths[string.lower(search)] +end + +function abr_month_num (search) + return revmonths[string.lower(search)] +end + +function num_dow_name (search) + return dow[search][1] +end + +function num_dow_name_abr (search) + return dow[search][2] +end + +function name_dow_num (search) + return revdow[string.lower(search)] +end + +function abr_dow_num (search) + return revdow[string.lower(search)] +end + +--tell me what TimeZone my system is set to + +function what_tz () + f = fs.read_file_as_array("/etc/TZ") or {} + local tz = f[1] + return tz +end + +--change the timezone my system is set to + +function change_tz ( tz ) + --give us something like CET-1, this is busy box offset need to fix. + + tz = string.gsub(tz, "%+", "%%+") + tz = string.gsub(tz, "%-", "%%-") + tz = "^" .. tz .. "$" + result = {} + for a=1,table.maxn(date.timezones) do + c = string.match(date.timezones[a][5], tz) + if c ~= nil then result[#result +1] = c end + end + + if table.maxn(result) == 1 then + fs.write_file("/etc/TZ", result[1]) + mess = "Success" + else + mess = "Too many matches." + end + + return mess,date.what_tz() +end 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 @@ -0,0 +1,222 @@ +--[[ + module for generic filesystem funcs + + Copyright (c) Natanael Copa 2006 + MM edited to use "posix" +]]-- + +module (..., package.seeall) + +require("posix") +require("format") + +-- generic wrapper funcs +function is_dir ( pathstr ) + return posix.stat ( pathstr or "", "type" ) == "directory" +end + +function is_file ( pathstr ) + return posix.stat ( pathstr or "", "type" ) == "regular" +end + +function is_link ( pathstr ) + return posix.stat ( pathstr or "", "type" ) == "link" +end + +-- Creates a directory if it doesn't exist, including the parent dirs +function create_directory ( path ) + local pos = string.find(path, "/") + while pos do + posix.mkdir(string.sub(path, 1, pos)) + pos = string.find(path, "/", pos+1) + end + posix.mkdir(path) + return is_dir(path) +end + +-- Deletes a directory along with its contents +function remove_directory ( path ) + if fs.is_dir(path) then + for d in posix.files(path) do + if (d == ".") or (d == "..") then + -- ignore + elseif fs.is_dir(path .. "/" .. d) then + remove_directory(path .. "/" ..d) + else + os.remove(path .. "/" ..d) + end + end + os.remove(path) + return true + end + return false +end + +-- Creates a blank file (and the directory if necessary) +function create_file ( path ) + path = path or "" + if not posix.stat(posix.dirname(path)) then create_directory(posix.dirname(path)) end + local f = io.open(path, "w") + if f then f:close() end + return is_file(path) +end + +-- Copies a file to a directory or new filename (creating the directory if necessary) +-- fails if new file is already a directory (this is different than cp function) +-- if newpath ends in "/", will treat as a directory +function copy_file(oldpath, newpath) + local use_dir = string.find(newpath or "", "/%s*$") + if not is_file(oldpath) or not newpath or newpath == "" or (not use_dir and is_dir(newpath)) or (use_dir and is_dir(newpath .. posix.basename(oldpath))) then + return false + end + if use_dir then newpath = newpath .. posix.basename(oldpath) end + if not posix.stat(posix.dirname(newpath)) then create_directory(posix.dirname(newpath)) end + local old = io.open(oldpath, "r") + local new = io.open(newpath, "w") + new:write(old:read("*a")) + new:close() + old:close() + return is_file(newpath) +end + +-- Moves a file to a directory or new filename (creating the directory if necessary) +-- fails if new file is already a directory (this is different than mv function) +-- if newpath ends in "/", will treat as a directory +function move_file(oldpath, newpath) + local use_dir = string.find(newpath or "", "/%s*$") + if not is_file(oldpath) or not newpath or newpath == "" or (not use_dir and is_dir(newpath)) or (use_dir and is_dir(newpath .. posix.basename(oldpath))) then + return false + end + if use_dir then newpath = newpath .. posix.basename(oldpath) end + if not posix.stat(posix.dirname(newpath)) then create_directory(posix.dirname(newpath)) end + local status, errstr, errno = os.rename(oldpath, newpath) + -- errno 18 means Invalid cross-device link + if status or errno ~= 18 then + -- successful move or failure due to something else + return (status ~= nil), errstr, errno + else + status = copy_file(oldpath, newpath) + if status then + os.remove(oldpath) + end + return status + end +end + +-- Returns the contents of a file as a string +function read_file ( path ) + local file = io.open(path or "") + if ( file ) then + local f = file:read("*a") + file:close() + return f + else + return nil + end +end + +-- Returns an array with the contents of a file, +-- or nil and the error message +function read_file_as_array ( path ) + local file, error = io.open(path or "") + if ( file == nil ) then + return nil, error + end + local f = {} + for line in file:lines() do + table.insert ( f , line ) + --sometimes you will see it like f[#f+1] = line + end + file:close() + return f +end + +-- write a string to a file, will replace file contents +function write_file ( path, str ) + path = path or "" + if not posix.stat(posix.dirname(path)) then create_directory(posix.dirname(path)) end + local file = io.open(path, "w") + --append a newline char to EOF + str = string.gsub(str or "", "\n*$", "\n") + if ( file ) then + file:write(str) + file:close() + end +end + +-- this could do more than a line. This will append +-- fs.write_line_file ("filename", "Line1 \nLines2 \nLines3") +function write_line_file ( path, str ) + path = path or "" + if not posix.stat(posix.dirname(path)) then create_directory(posix.dirname(path)) end + local file = io.open(path) + if ( file) then + local c = file:read("*a") or "" + file:close() + fs.write_file(path, c .. (str or "")) + end +end + +-- returns an array of files under "where" that match "what" (a Lua pattern) +function find_files_as_array ( what, where, follow, t ) + where = where or posix.getcwd() + what = what or ".*" + t = t or {} + + local link + if follow and fs.is_link(where) then + link = posix.readlink(where) + if not string.find(link, "^/") then + link = posix.dirname(where).."/"..link + end + end + + if fs.is_dir(where) or (link and fs.is_dir(link)) then + for d in posix.files ( where ) do + if (d == ".") or ( d == "..") then + -- do nothing + elseif fs.is_dir ( where .. "/" .. d ) then + find_files_as_array (what, where .. "/" .. d, follow, t ) + elseif follow and fs.is_link ( where .. "/" .. d ) then + find_files_as_array (what, where .. "/" .. d, follow, t ) + elseif (string.match (d, "^" .. what .. "$" )) then + table.insert (t, ( string.gsub ( where .. "/" .. d, "/+", "/" ) ) ) + end + end + elseif (string.match (posix.basename(where), "^" .. what .. "$" )) and posix.stat(where) then + table.insert (t, where ) + end + + return (t) +end + +-- iterator function for finding dir entries matching (what) (a Lua pattern) +-- starting at where, or currentdir if not specified. +function find ( what, where, follow ) + local t = find_files_as_array ( what, where, follow ) + local idx = 0 + return function () + idx = idx + 1 + return t[idx] + end +end + +-- This function does almost the same as posix.stat, but instead it writes the output human readable. +function stat ( path ) + local filedetails = posix.stat(path or "") + if (filedetails) then + filedetails["ctime"]=os.date("%c", filedetails["ctime"]) + filedetails["mtime"]=os.date("%c", filedetails["mtime"]) + filedetails["path"]=path + if ( filedetails["size"] > 1073741824 ) then + filedetails["size"]=((filedetails["size"]/1073741824) - (filedetails["size"]/1073741824%0.1)) .. "G" + elseif ( filedetails["size"] > 1048576 ) then + filedetails["size"]=((filedetails["size"]/1048576) - (filedetails["size"]/1048576%0.1)) .. "M" + elseif ( filedetails["size"] > 1024 ) then + filedetails["size"]=((filedetails["size"]/1024) - (filedetails["size"]/1024%0.1)) .. "k" + else + filedetails["size"]=filedetails["size"] + end + end + return filedetails +end diff --git a/html.lua b/html.lua new file mode 100644 index 0000000..33d6d71 --- /dev/null +++ b/html.lua @@ -0,0 +1,262 @@ +--[[ lowlevel html functions + Written for Alpine Configuration Framework (ACF) -- see www.alpinelinux.org + Copyright (C) 2007 Nathan Angelacos + Licensed under the terms of GPL2 +]]-- +module (..., package.seeall) + +--[[ Cookie functions ]]------------------------------------------------------ +cookie={} + +-- Set a cookie - returns a string suitable for setting a cookie +-- if the value is the boolean "false", then set the cookie to expire +cookie.set = function ( name, value, path ) + local expires = "" + if name == nil then + return ("") + end + if value == false then + expires = 'expires=Thu Jan 1 00:00:00 EST 1970' + value = "" + end + if path == nil then + path = "/" + end + return (string.format('Set-Cookie: %s=%s; path=%s; %s\n', html_escape(tostring(name)), + html_escape(tostring(value)), html_escape(path), html_escape(expires))) +end + + +-- wrapper function to clear a cookie +cookie.unset = function ( name, path) + return cookie.set (name, false, path) +end + + + +-- escape unsafe html characters +function html_escape (text ) + text = text or "" + local str = string.gsub (text, "&", "&" ) + str = string.gsub (str, "<", "<" ) + str = string.gsub (str, ">", ">" ) + str = string.gsub (str, "'", "'" ) + return (string.gsub (str, '"', """ )) +end + +-- return a name,value pair as a string. +local nv_pair = function ( name, value) + if ( name == nil ) then + return ( value or "" ) + end + + if ( type(value) == "boolean" ) then + value = tostring(value) + end + + if ( value == nil ) then + return ( "" ) + else + return (string.format (' %s="%s" ', html_escape(name) , html_escape(value) )) + end +end + + +--[[ + each of these functions take a table that has an associative array of + the values we might care about: + + value -- this is the value in the form element, or the selected element + name -- this is the name of the element + cols, rows + class + id + etc. +]]-- + +local generic_input = function ( field_type, v ) + if type(v.value) == "table" then + ret = {} + local vals = v.value + for n, val in ipairs(vals) do + v.value = val + table.insert(ret, generic_input(field_type, v)) + end + v.value = vals + return table.concat(ret) + end + if ( field_type == nil ) then + return nil + end + + local str = string.format ( '<input class="%s" type="%s" ', html_escape(field_type), html_escape(field_type) ) + + for i,k in ipairs ( { + "name", "size", "checked", "maxlength", + "value", "length", "class", "id", "src", + "align", "alt", "contenteditable", "readonly", + "tabindex", "accesskey", "onfocus", "onblur" + } ) do + str = str .. nv_pair ( k, v[k] ) + end + + if ( v.disabled ~= nil ) then + str = str .. " disabled" + end + + return ( str .. ">" ) +end + + +--[[ Form functions ]]------------------------------------------------------ +-- These expect something like a cfe to work (see mvc.lua) + +form = {} +form.text = function ( v ) + return generic_input ( "text", v ) +end + + +form.longtext = function ( v ) + local str = "<textarea" + for i,k in ipairs ( { + "name", "rows", "cols", + "class", "id", "tabindex", "accesskey", + "onfocus", "onblur", "readonly" + } ) do + str = str .. nv_pair ( k, v[k] ) + end + str = str .. nv_pair (nil, v.disabled) + return ( str .. ">" .. html_escape(v.value) .. "</textarea>" ) +end + + +function form.password ( v ) + return generic_input ( "password", v ) +end + +function form.hidden ( v ) + return generic_input ( "hidden", v ) +end + + +function form.submit ( v ) + return generic_input ( "submit", v ) +end + + +function form.action (v) + return generic_input ("submit", v) +end + +function form.file ( v ) + return generic_input ( "file", v ) +end + +function form.image ( v ) + return generic_input ( "image", v ) +end + + +-- v.value is the selected item (or an array if multiple) +-- v.option is an array of valid options +-- NOTE use of value and values (plural) +function form.select ( v ) + if ( v.name == nil ) then + return nil + end + local str = "<select" + for i,k in ipairs ( { + "name", "size", "tabindex", "accesskey", + "onfocus", "onblur", "onchange", "id", + "class", "multiple" + } ) do + str = str .. nv_pair ( k, v[k] ) + end + + if ( v.disabled ~= nil ) then + str = str .. " disabled" + end + str = str .. ">" + -- now the options + local reverseval = {} + if type(v.value) == "table" then + for x,val in ipairs(v.value) do + reverseval[val]=x + end + end + local selected = false + for i, k in ipairs ( v.option ) do + local val = k + local txt = nil + if type(val) == "table" then + txt=val[1] + val=val[0] + end + str = str .. "<option " + if type(v.value) == "table" then + if reverseval[val] then + str = str .. " selected" + selected = true + end + elseif ( v.value == val ) then + str = str .. " selected" + selected = true + end + str = str .. nv_pair("value", val) .. ">" .. html_escape(val) .. "</option>" + end + if not selected then + str = str .. '<option selected value="' .. html_escape(v.value) ..'">[' .. html_escape(v.value) .. ']</option>' + end + str = str .. "</select>" + return (str) +end + +function form.checkbox ( v ) + return generic_input ( "checkbox", v ) +end + + +-- NOTE: VALUE of a form is a table containing the form elements ... +function form.start ( v) + if ( v.action == nil ) then + return nil + end + + local method = v.method or "get" + return ( string.format ( + '<form %s%s%s>', + nv_pair ( "class", html_escape(v.class) ), + nv_pair ( "method", html_escape(v.method) ), + nv_pair ( "action", html_escape(v.action) ) + ) ) +end + +function form.stop ( ) + return ("</form>") +end + +-- For "h1, h2, p," etc +-- WARNING - Text is printed verbatim - you may want to +-- wrap the text in html_escape +function entity (tag, text, class, id) + return ( string.format ( + "<%s%s%s>%s</%s>", + html_escape(tag), + nv_pair ("class", class), + nv_pair("id", id), html_escape(text), html_escape(tag)) + ) +end + + +function link ( v ) + if ( v.value == nil ) then + return nil + end + local str = nv_pair ( "href", v.value ) + for i,k in ipairs( { "class", "id" }) do + str = str .. nv_pair ( k, v[k] ) + end + + return ( "<a " .. str .. ">" .. html_escape(v.label) .. "</a>" ) +end diff --git a/processinfo.lua b/processinfo.lua new file mode 100644 index 0000000..7058b04 --- /dev/null +++ b/processinfo.lua @@ -0,0 +1,221 @@ + +module(..., package.seeall) + +require("posix") +require("fs") +require("format") +require("apk") + +local path = "PATH=/usr/bin:/bin:/usr/sbin:/sbin " + +function package_version(packagename) + local result = apk.version(packagename) + local errtxt + if not result then + errtxt = "Program not installed" + end + return result,errtxt +end + +function process_autostart(servicename) + local result + local errtxt = "Not programmed to autostart" + local f = io.popen( "/sbin/rc-update show" ) + local cmdresult = f:read("*a") or "" + f:close() + for line in string.gmatch(cmdresult, "[^\n]+") do + if string.match(line, "^%s*"..format.escapemagiccharacters(servicename).."%s+|") then + local runlevels = string.match(line, "|(.*)") + -- ignore the shutdown runlevel + runlevels = string.gsub(runlevels, "%sshutdown%s", " ") + runlevels = string.gsub(runlevels, "^%s+", "") + runlevels = string.gsub(runlevels, "%s+$", "") + if runlevels ~= "" then + result = "Service will autostart at next boot (at runlevel '" .. runlevels .. "')" + errtxt = nil + end + break + end + end + return result,errtxt +end + +function read_initrunlevels() + local config = {} + local f = io.popen( "/sbin/rc-update show -v" ) + local cmdresult = f:read("*a") or "" + f:close() + for line in string.gmatch(cmdresult, "([^\n]*)\n?") do + local service = string.match(line, "^%s*(%S+)") + local runlevels = string.match(line, "|%s*(%S.*)") + if service then + local runlevel = {} + if runlevels then + runlevel = format.string_to_table(string.gsub(runlevels, "%s+$", ""), "%s+") or {} + end + config[#config+1] = {servicename=service, runlevels=runlevel} + end + end + table.sort(config, function(a,b) return a.servicename < b.servicename end) + return config +end + +function add_runlevels(servicename, runlevels) + local cmdresult,cmderrors + if not servicename then + cmderrors = "Invalid service name" + else + if runlevels and #runlevels > 0 then + local cmd = {path, "rc-update add"} + cmd[#cmd+1] = format.escapespecialcharacters(servicename) + for i,lev in ipairs(runlevels) do + cmd[#cmd+1] = lev + end + cmd[#cmd+1] = "2>&1" + local f = io.popen(table.concat(cmd, " ")) + cmdresult = f:read("*a") + f:close() + cmdresult = string.gsub(cmdresult, "\n+$", "") + else + cmdresult = "No runlevels added" + end + end + + return cmdresult,cmderrors +end + +function delete_runlevels(servicename, runlevels) + local cmdresult,cmderrors + if not servicename then + cmderrors = "Invalid service name" + else + if runlevels and #runlevels > 0 then + local cmd = {path, "rc-update del"} + cmd[#cmd+1] = format.escapespecialcharacters(servicename) + for i,lev in ipairs(runlevels) do + cmd[#cmd+1] = lev + end + cmd[#cmd+1] = "2>&1" + local f = io.popen(table.concat(cmd, " ")) + cmdresult = f:read("*a") + f:close() + cmdresult = string.gsub(cmdresult, "\n+$", "") + else + cmdresult = "No runlevels deleted" + end + end + + return cmdresult,cmderrors +end + +function daemoncontrol (process, action) + + local cmdresult = "" + local cmderrors + if not process then + cmderrors = "Invalid service name" + elseif not action then + cmderrors = "Invalid action" + else + local file = io.popen( "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin /etc/init.d/" .. + format.escapespecialcharacters(process) .. " " .. format.escapespecialcharacters(string.lower(action)) .. " 2>&1" ) + if file ~= nil then + cmdresult = file:read( "*a" ) + file:close() + end + end + return cmdresult,cmderrors +end + +-- the following methods are available: +-- /proc/<pid>/stat the comm field (2nd) field contains name but only up +-- to 15 chars. does not resolve links +-- +-- /proc/<pid>/cmdline argv[0] contains the command. However if it is a script +-- then will the interpreter show up +-- +-- /proc/<pid>/exe link to exe file. this will resolv links +-- +-- returns list of all pids for given exe name + +--[[ +-- gives lots of false positives for busybox +local function is_exe(path, name) + local f = posix.readlink(path.."/exe") + if f and (f == name or posix.basename(f) == name) then + return true + else + return false + end +end +]]-- + + +local function is_stat(path, name) + local f = io.open(path.."/stat") + if (f) then + local line = f:read() + local p = string.gsub(line, ".*%(", "") + p = string.gsub(p, "%).*", "") + f:close() + end + if p ~= nil then + if string.len(name) <= 15 and p == name then + return true + end + end + return false +end + +local function is_cmdline(path, name) + local f = io.open(path.."/cmdline") + if f == nil then + return false + end + local line = f:read() + f:close() + if line == nil then + return false + end + local arg0 = string.gsub(line, string.char(0)..".*", "") + if posix.basename(arg0) == name then + return true + end +end + +local function has_pidfile(name) + local pid + local file = "/var/run/"..name..".pid" + if fs.is_file(file) then + -- check to see if there's a matching proc directory and that it was created slightly after the pid file + -- this allows us to find init scripts with differing process names and avoids the problem with + -- proc numbers wrapping + local tmp = string.match(fs.read_file(file) or "", "%d+") + if tmp then + local dir = "/proc/" .. tmp + filetime = posix.stat(file, "ctime") + dirtime = posix.stat(dir, "ctime") + if dirtime and (tonumber(dirtime) - tonumber(filetime) < 100) then + pid = tmp + end + end + end + return pid +end + +function pidof(name) + local pids = {has_pidfile(name)} + local i, j + + for i,j in pairs(posix.glob("/proc/[0-9]*")) do + local pid = tonumber(posix.basename(j)) + if is_stat(j, name) or is_cmdline(j, name) then + table.insert(pids, pid) + end + end + if #pids == 0 then + pids = nil + end + return pids +end + diff --git a/validator.lua b/validator.lua new file mode 100644 index 0000000..ca8ed41 --- /dev/null +++ b/validator.lua @@ -0,0 +1,172 @@ +-------------------------------------------------- +-- Validation Functions for Alpine Linux' Webconf +-------------------------------------------------- +module (..., package.seeall) + +function is_string ( str ) + return (type(str) == "string") +end + +function is_boolean ( str ) + return (type(str) == "boolean") +end + +function is_number ( str ) + return (type(str) == "number") +end + +-- +-- This function validates an ipv4 address. +-- +function is_ipv4(ipv4) + local retval = false; + local nums = {}; + local iplen = string.len(ipv4); + + -- check the ipv4's length + if (iplen < 7 or iplen > 15) then + return false, "Invalid Length" + end + + -- NC: Split the string into an array. separate with '.' (dots) + -- ^ beginning of string + -- () capture + -- %. litteral '.' The % neutralizes the . character class. + -- %d+ one or more digits + -- $ end of string + nums = {ipv4:match ("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")} + + -- check if all nums are filled + if ( nums[1] == nil or + nums[2] == nil or + nums[3] == nil or + nums[4] == nil) then + -- we have an empty number + return false, "Invalid Format" + end + + -- too big? + if (tonumber(nums[1]) > 255 or + tonumber(nums[2]) > 255 or + tonumber(nums[3]) > 255 or + tonumber(nums[4]) > 255) then + -- at least one number is too big + return false, "Invalid Value" + end + + return true +end + +-- +-- This function validates a partial ipv4 address. +-- +function is_partial_ipv4(ipv4) + local retval = false; + local nums = {}; + + -- Check to see if any invalid characters + if not ipv4 or not ipv4:match("^[%d%.]+$") then + return false, "Invalid Format" + end + + -- NC: Split the string into an array. separate with '.' (dots) + -- %d+ one or more digits + for num in ipv4:gmatch("%d+") do + nums[#nums+1] = num + -- too big? + if tonumber(num) > 255 then + return false, "Invalid Format" + end + end + + -- too many numbers + if #nums > 4 then + return false, "Invalid Format" + end + + return true +end + +function is_mac(mac) + + local tmpmac = string.upper(mac) + + if (string.len(tmpmac) ~= 17) then + return false, "Invalid Length" + end + + -- check for valid characters + local step = 1; + while (step <= 17) do + if (string.sub(tmpmac, step, step) ~= ":") and + (string.sub(tmpmac, step, step) < "0" or string.sub(tmpmac, step, step) > "9") and + (string.sub(tmpmac, step, step) < "A" or string.sub(tmpmac, step, step) > "F") then + -- we have found an invalid character! + return false, "Invalid Chars" + end + step = step + 1; + end + + -- check for valid colon positions + if (string.sub(tmpmac, 3, 3) ~= ":" or + string.sub(tmpmac, 6, 6) ~= ":" or + string.sub(tmpmac, 9, 9) ~= ":" or + string.sub(tmpmac, 12, 12) ~= ":" or + string.sub(tmpmac, 15, 15) ~= ":") then + return false, "Invalid Format" + end + + -- check for valid non colon positions + step = 1; + while (step <= 17) do + if ((string.sub(tmpmac, step, step) == ":") and + ((step ~= 3) and (step ~= 6) and (step ~= 9) and (step ~= 12) and + (step ~= 15))) then + return false, "Invalid Value" + end + step = step + 1; + end + + return true +end + +-- +-- This function checks if the given input +-- consists of number-chars between 0..9 only +-- and eventually a leading '-' +-- +function is_integer(numstr) + -- ^ beginning of string + -- -? one or zero of the char '-' + -- %d+ one or more digits + -- $ end of string + return string.find(numstr, "^-?%d+$") ~= nil +end + + +-- +-- This function checks if the given input +-- consists of number-chars between 0..9 only +-- and if it is within a given range. +-- +function is_integer_in_range(numstr, min, max) + return is_integer(numstr) + and tonumber(numstr) >= min + and tonumber(numstr) <= max + +end + +-- +-- This function checks if the given number is an integer +-- and wheter it is between 1 .. 65535 +-- +function is_port(numstr) + return is_integer_in_range(numstr, 1, 65535) +end + +function is_valid_filename ( path, restriction ) + if not (path) or ((restriction) and (string.find (path, "^" .. format.escapemagiccharacters(restriction) ) == nil or string.find (path, "/", #restriction+2) )) then + return false + end + return true +end |