if not modules then modules = { } end modules ['l-os'] = { version = 1.001, comment = "companion to luat-lib.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- This file deals with some operating system issues. Please don't bother me -- with the pros and cons of operating systems as they all have their flaws -- and benefits. Bashing one of them won't help solving problems and fixing -- bugs faster and is a waste of time and energy. -- -- path separators: / or \ ... we can use / everywhere -- suffixes : dll so exe ... no big deal -- quotes : we can use "" in most cases -- expansion : unless "" are used * might give side effects -- piping/threads : somewhat different for each os -- locations : specific user file locations and settings can change over time -- -- os.type : windows | unix (new, we already guessed os.platform) -- os.name : windows | msdos | linux | macosx | solaris | .. | generic (new) -- os.platform : extended os.name with architecture -- os.sleep() => socket.sleep() -- math.randomseed(tonumber(string.sub(string.reverse(tostring(math.floor(socket.gettime()*10000))),1,6))) local os = os local date, time = os.date, os.time local find, format, gsub, upper, gmatch = string.find, string.format, string.gsub, string.upper, string.gmatch local concat = table.concat local random, ceil, randomseed = math.random, math.ceil, math.randomseed local rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring = rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring -- The following code permits traversing the environment table, at least -- in luatex. Internally all environment names are uppercase. -- The randomseed in Lua is not that random, although this depends on the operating system as well -- as the binary (Luatex is normally okay). But to be sure we set the seed anyway. math.initialseed = tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6)) randomseed(math.initialseed) if not os.__getenv__ then os.__getenv__ = os.getenv os.__setenv__ = os.setenv if os.env then local osgetenv = os.getenv local ossetenv = os.setenv local osenv = os.env local _ = osenv.PATH -- initialize the table function os.setenv(k,v) if v == nil then v = "" end local K = upper(k) osenv[K] = v if type(v) == "table" then v = concat(v,";") -- path end ossetenv(K,v) end function os.getenv(k) local K = upper(k) local v = osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k) if v == "" then return nil else return v end end else local ossetenv = os.setenv local osgetenv = os.getenv local osenv = { } function os.setenv(k,v) if v == nil then v = "" end local K = upper(k) osenv[K] = v end function os.getenv(k) local K = upper(k) local v = osenv[K] or osgetenv(K) or osgetenv(k) if v == "" then return nil else return v end end local function __index(t,k) return os.getenv(k) end local function __newindex(t,k,v) os.setenv(k,v) end os.env = { } setmetatable(os.env, { __index = __index, __newindex = __newindex } ) end end -- end of environment hack local execute = os.execute local iopopen = io.popen function os.resultof(command) local handle = iopopen(command,"r") -- already has flush if handle then local result = handle:read("*all") or "" handle:close() return result else return "" end end if not io.fileseparator then if find(os.getenv("PATH"),";",1,true) then io.fileseparator, io.pathseparator, os.type = "\\", ";", os.type or "mswin" else io.fileseparator, io.pathseparator, os.type = "/" , ":", os.type or "unix" end end os.type = os.type or (io.pathseparator == ";" and "windows") or "unix" os.name = os.name or (os.type == "windows" and "mswin" ) or "linux" if os.type == "windows" then os.libsuffix, os.binsuffix, os.binsuffixes = 'dll', 'exe', { 'exe', 'cmd', 'bat' } else os.libsuffix, os.binsuffix, os.binsuffixes = 'so', '', { '' } end local launchers = { windows = "start %s", macosx = "open %s", unix = "$BROWSER %s &> /dev/null &", } function os.launch(str) execute(format(launchers[os.name] or launchers.unix,str)) end if not os.times then -- ? -- utime = user time -- stime = system time -- cutime = children user time -- cstime = children system time function os.times() return { utime = os.gettimeofday(), -- user stime = 0, -- system cutime = 0, -- children user cstime = 0, -- children system } end end local gettimeofday = os.gettimeofday or os.clock os.gettimeofday = gettimeofday local startuptime = gettimeofday() function os.runtime() return gettimeofday() - startuptime end -- print(os.gettimeofday()-os.time()) -- os.sleep(1.234) -- print (">>",os.runtime()) -- print(os.date("%H:%M:%S",os.gettimeofday())) -- print(os.date("%H:%M:%S",os.time())) -- no need for function anymore as we have more clever code and helpers now -- this metatable trickery might as well disappear local resolvers = os.resolvers or { } os.resolvers = resolvers setmetatable(os, { __index = function(t,k) local r = resolvers[k] return r and r(t,k) or nil -- no memoize end }) -- we can use HOSTTYPE on some platforms local name, platform = os.name or "linux", os.getenv("MTX_PLATFORM") or "" local function guess() local architecture = os.resultof("uname -m") or "" if architecture ~= "" then return architecture end architecture = os.getenv("HOSTTYPE") or "" if architecture ~= "" then return architecture end return os.resultof("echo $HOSTTYPE") or "" end -- os.bits = 32 | 64 if platform ~= "" then os.platform = platform elseif os.type == "windows" then -- we could set the variable directly, no function needed here -- PROCESSOR_ARCHITECTURE : binary platform -- PROCESSOR_ARCHITEW6432 : OS platform function resolvers.platform(t,k) local platform, architecture = "", os.getenv("PROCESSOR_ARCHITECTURE") or "" if find(architecture,"AMD64",1,true) then -- platform = "mswin-64" platform = "win64" else platform = "mswin" end os.setenv("MTX_PLATFORM",platform) os.platform = platform return platform end elseif name == "linux" then function resolvers.platform(t,k) -- we sometimes have HOSTTYPE set so let's check that first local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" if find(architecture,"x86_64",1,true) then platform = "linux-64" elseif find(architecture,"ppc",1,true) then platform = "linux-ppc" else platform = "linux" end os.setenv("MTX_PLATFORM",platform) os.platform = platform return platform end elseif name == "macosx" then --[[ Identifying the architecture of OSX is quite a mess and this is the best we can come up with. For some reason $HOSTTYPE is a kind of pseudo environment variable, not known to the current environment. And yes, uname cannot be trusted either, so there is a change that you end up with a 32 bit run on a 64 bit system. Also, some proper 64 bit intel macs are too cheap (low-end) and therefore not permitted to run the 64 bit kernel. ]]-- function resolvers.platform(t,k) -- local platform, architecture = "", os.getenv("HOSTTYPE") or "" -- if architecture == "" then -- architecture = os.resultof("echo $HOSTTYPE") or "" -- end local platform, architecture = "", os.resultof("echo $HOSTTYPE") or "" if architecture == "" then -- print("\nI have no clue what kind of OSX you're running so let's assume an 32 bit intel.\n") platform = "osx-intel" elseif find(architecture,"i386",1,true) then platform = "osx-intel" elseif find(architecture,"x86_64",1,true) then platform = "osx-64" else platform = "osx-ppc" end os.setenv("MTX_PLATFORM",platform) os.platform = platform return platform end elseif name == "sunos" then function resolvers.platform(t,k) local platform, architecture = "", os.resultof("uname -m") or "" if find(architecture,"sparc",1,true) then platform = "solaris-sparc" else -- if architecture == 'i86pc' platform = "solaris-intel" end os.setenv("MTX_PLATFORM",platform) os.platform = platform return platform end elseif name == "freebsd" then function resolvers.platform(t,k) local platform, architecture = "", os.resultof("uname -m") or "" if find(architecture,"amd64",1,true) then platform = "freebsd-amd64" else platform = "freebsd" end os.setenv("MTX_PLATFORM",platform) os.platform = platform return platform end elseif name == "kfreebsd" then function resolvers.platform(t,k) -- we sometimes have HOSTTYPE set so let's check that first local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" if find(architecture,"x86_64",1,true) then platform = "kfreebsd-amd64" else platform = "kfreebsd-i386" end os.setenv("MTX_PLATFORM",platform) os.platform = platform return platform end else -- platform = "linux" -- os.setenv("MTX_PLATFORM",platform) -- os.platform = platform function resolvers.platform(t,k) local platform = "linux" os.setenv("MTX_PLATFORM",platform) os.platform = platform return platform end end os.newline = name == "windows" and "\013\010" or "\010" -- crlf or lf function resolvers.bits(t,k) local bits = find(os.platform,"64",1,true) and 64 or 32 os.bits = bits return bits end -- beware, we set the randomseed -- from wikipedia: Version 4 UUIDs use a scheme relying only on random numbers. This algorithm sets the -- version number as well as two reserved bits. All other bits are set using a random or pseudorandom -- data source. Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with hexadecimal -- digits x and hexadecimal digits 8, 9, A, or B for y. e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479. -- -- as we don't call this function too often there is not so much risk on repetition local t = { 8, 9, "a", "b" } function os.uuid() return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x", random(0xFFFF),random(0xFFFF), random(0x0FFF), t[ceil(random(4))] or 8,random(0x0FFF), random(0xFFFF), random(0xFFFF),random(0xFFFF),random(0xFFFF) ) end local d function os.timezone(delta) d = d or tonumber(tonumber(date("%H")-date("!%H"))) if delta then if d > 0 then return format("+%02i:00",d) else return format("-%02i:00",-d) end else return 1 end end local timeformat = format("%%s%s",os.timezone(true)) local dateformat = "!%Y-%m-%d %H:%M:%S" local lasttime = nil local lastdate = nil function os.fulltime(t,default) t = t and tonumber(t) or 0 if t > 0 then -- valid time elseif default then return default else t = time() end if t ~= lasttime then lasttime = t lastdate = format(timeformat,date(dateformat)) end return lastdate end local dateformat = "%Y-%m-%d %H:%M:%S" local lasttime = nil local lastdate = nil function os.localtime(t,default) t = t and tonumber(t) or 0 if t > 0 then -- valid time elseif default then return default else t = time() end if t ~= lasttime then lasttime = t lastdate = date(dateformat,t) end return lastdate end function os.converttime(t,default) local t = tonumber(t) if t and t > 0 then return date(dateformat,t) else return default or "-" end end local memory = { } local function which(filename) local fullname = memory[filename] if fullname == nil then local suffix = file.suffix(filename) local suffixes = suffix == "" and os.binsuffixes or { suffix } for directory in gmatch(os.getenv("PATH"),"[^" .. io.pathseparator .."]+") do local df = file.join(directory,filename) for i=1,#suffixes do local dfs = file.addsuffix(df,suffixes[i]) if io.exists(dfs) then fullname = dfs break end end end if not fullname then fullname = false end memory[filename] = fullname end return fullname end os.which = which os.where = which function os.today() return date("!*t") -- table with values end function os.now() return date("!%Y-%m-%d %H:%M:%S") -- 2011-12-04 14:59:12 end -- if not os.sleep and socket then -- os.sleep = socket.sleep -- end if not os.sleep then local socket = socket function os.sleep(n) if not socket then -- so we delay ... if os.sleep is really needed then one should also -- be sure that socket can be found socket = require("socket") end socket.sleep(n) end end -- print(os.which("inkscape.exe")) -- print(os.which("inkscape")) -- print(os.which("gs.exe")) -- print(os.which("ps2pdf")) -- These are moved from core-con.lua (as I needed them elsewhere). local function isleapyear(year) return (year % 400 == 0) or ((year % 100 ~= 0) and (year % 4 == 0)) end os.isleapyear = isleapyear -- nicer: -- -- local days = { -- [false] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, -- [true] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } -- } -- -- local function nofdays(year,month) -- return days[isleapyear(year)][month] -- return month == 2 and isleapyear(year) and 29 or days[month] -- end -- -- more efficient: local days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } local function nofdays(year,month) if not month then return isleapyear(year) and 365 or 364 else return month == 2 and isleapyear(year) and 29 or days[month] end end os.nofdays = nofdays function os.weekday(day,month,year) return date("%w",time { year = year, month = month, day = day }) + 1 end function os.validdate(year,month,day) -- we assume that all three values are set -- year is always ok, even if lua has a 1970 time limit if month < 1 then month = 1 elseif month > 12 then month = 12 end if day < 1 then day = 1 else local max = nofdays(year,month) if day > max then day = max end end return year, month, day end