--[[
POSIX library for Lua 5.1, 5.2 & 5.3.
(c) Gary V. Vaughan <gary@vaughan.pe>, 2014-2015
]]
--[[--
Legacy Lua POSIX bindings.
APIs for maintaining compatibility with previous releases.
@module posix
]]
local _argcheck = require "posix._argcheck"
local bit = require "bit32"
local argerror, argtypeerror, badoption =
_argcheck.argerror, _argcheck.argtypeerror, _argcheck.badoption
local band, bnot, bor = bit.band, bit.bnot, bit.bor
local checkint, checkselection, checkstring, checktable =
_argcheck.checkint, _argcheck.checkselection, _argcheck.checkstring, _argcheck.checktable
local optint, optstring, opttable =
_argcheck.optint, _argcheck.optstring, _argcheck.opttable
local toomanyargerror = _argcheck.toomanyargerror
local st = require "posix.sys.stat"
local S_IRUSR, S_IWUSR, S_IXUSR = st.S_IRUSR, st.S_IWUSR, st.S_IXUSR
local S_IRGRP, S_IWGRP, S_IXGRP = st.S_IRGRP, st.S_IWGRP, st.S_IXGRP
local S_IROTH, S_IWOTH, S_IXOTH = st.S_IROTH, st.S_IWOTH, st.S_IXOTH
local S_ISUID, S_ISGID, S_IRWXU, S_IRWXG, S_IRWXO =
st.S_ISUID, st.S_ISGID, st.S_IRWXU, st.S_IRWXG, st.S_IRWXO
local mode_map = {
{ c = "r", b = S_IRUSR }, { c = "w", b = S_IWUSR }, { c = "x", b = S_IXUSR },
{ c = "r", b = S_IRGRP }, { c = "w", b = S_IWGRP }, { c = "x", b = S_IXGRP },
{ c = "r", b = S_IROTH }, { c = "w", b = S_IWOTH }, { c = "x", b = S_IXOTH },
}
local function pushmode (mode)
local m = {}
for i = 1, 9 do
if band (mode, mode_map[i].b) ~= 0 then m[i] = mode_map[i].c else m[i] = "-" end
end
if band (mode, S_ISUID) ~= 0 then
if band (mode, S_IXUSR) ~= 0 then m[3] = "s" else m[3] = "S" end
end
if band (mode, S_ISGID) ~= 0 then
if band (mode, S_IXGRP) ~= 0 then m[6] = "s" else m[6] = "S" end
end
return table.concat (m)
end
local function rwxrwxrwx (modestr)
local mode = 0
for i = 1, 9 do
if modestr:sub (i, i) == mode_map[i].c then
mode = bor (mode, mode_map[i].b)
elseif modestr:sub (i, i) == "s" then
if i == 3 then
mode = bor (mode, S_ISUID, S_IXUSR)
elseif i == 6 then
mode = bor (mode, S_ISGID, S_IXGRP)
else
return nil -- bad mode
end
end
end
return mode
end
local function octal_mode (modestr)
local mode = 0
for i = 1, #modestr do
mode = mode * 8 + tonumber (modestr:sub (i, i))
end
return mode
end
local function mode_munch (mode, modestr)
if #modestr == 9 and modestr:match "^[-rswx]+$" then
return rwxrwxrwx (modestr)
elseif modestr:match "^[0-7]+$" then
return octal_mode (modestr)
elseif modestr:match "^[ugoa]+%s*[-+=]%s*[rswx]+,*" then
modestr:gsub ("%s*(%a+)%s*(.)%s*(%a+),*", function (who, op, what)
local bits, bobs = 0, 0
if who:match "[ua]" then bits = bor (bits, S_ISUID, S_IRWXU) end
if who:match "[ga]" then bits = bor (bits, S_ISGID, S_IRWXG) end
if who:match "[oa]" then bits = bor (bits, S_IRWXO) end
if what:match "r" then bobs = bor (bobs, S_IRUSR, S_IRGRP, S_IROTH) end
if what:match "w" then bobs = bor (bobs, S_IWUSR, S_IWGRP, S_IWOTH) end
if what:match "x" then bobs = bor (bobs, S_IXUSR, S_IXGRP, S_IXOTH) end
if what:match "s" then bobs = bor (bobs, S_ISUID, S_ISGID) end
if op == "+" then
-- mode |= bits & bobs
mode = bor (mode, band (bits, bobs))
elseif op == "-" then
-- mode &= ~(bits & bobs)
mode = band (mode, bnot (band (bits, bobs)))
elseif op == "=" then
-- mode = (mode & ~bits) | (bits & bobs)
mode = bor (band (mode, bnot (bits)), band (bits, bobs))
end
end)
return mode
else
return nil, "bad mode"
end
end
local M = {
argerror = argerror,
argtypeerror = argtypeerror,
badoption = badoption,
checkstring = checkstring,
checktable = checktable,
optstring = optstring,
toomanyargerror = toomanyargerror,
}
--- Change the mode of the path.
-- @function chmod
-- @string path existing file path
-- @string mode one of the following formats:
--
-- * "rwxrwxrwx" (e.g. "rw-rw-r--")
-- * "ugo+-=rwx" (e.g. "u+w")
-- * +-=rwx" (e.g. "+w")
--
-- @return[1] int `0`, if successful
-- @return[2] nil
-- @treturn[2] string error message
-- @treturn[2] int errnum
-- @see chmod(2)
-- @usage P.chmod ('bin/dof', '+x')
local bit = require "bit32"
local st = require "posix.sys.stat"
local _chmod, stat = st.chmod, st.stat
local RWXALL = bit.bor (st.S_IRWXU, st.S_IRWXG, st.S_IRWXO)
local function chmod (path, modestr)
local mode = (stat (path) or {}).st_mode
local bits, err = mode_munch (mode or 0, modestr)
if bits == nil then
argerror ("chmod", 2, err, 2)
end
return _chmod (path, band (bits, RWXALL))
end
if _DEBUG ~= false then
M.chmod = function (...)
local argt = {...}
checkstring ("chmod", 1, argt[1])
checkstring ("chmod", 2, argt[2])
if #argt > 2 then toomanyargerror ("chmod", 2, #argt) end
return chmod (...)
end
else
M.chmod = chmod
end
--- Create a file.
-- This function is obsoleted by @{posix.fcntl.open} with `posix.O_CREAT`.
-- @function creat
-- @string path name of file to create
-- @string mode permissions with which to create file
-- @treturn[1] int file descriptor of file at *path*, if successful
-- @return[2] nil
-- @treturn[2] string error message
-- @treturn[2] int errnum
-- @see creat(2)
-- @see posix.chmod
-- @usage
-- fd = P.creat ("data", "rw-r-----")
local bit = require "bit32"
local fcntl = require "posix.fcntl"
local st = require "posix.sys.stat"
local band, bor = bit.band, bit.bor
local creat_flags = bor (fcntl.O_CREAT, fcntl.O_WRONLY, fcntl.O_TRUNC)
local RWXALL = bor (st.S_IRWXU, st.S_IRWXG, st.S_IRWXO)
local open = fcntl.open
local function creat (path, modestr)
local mode, err = mode_munch (0, modestr)
if mode == nil then
argerror ("creat", 2, err, 2)
end
return open (path, creat_flags, band (mode, RWXALL))
end
if _DEBUG ~= false then
M.creat = function (...)
local argt = {...}
checkstring ("creat", 1, argt[1])
checkstring ("creat", 2, argt[2])
if #argt > 2 then toomanyargerror ("creat", 2, #argt) end
return creat (...)
end
else
M.creat = creat
end
--- Make a directory.
-- @function mkdir
-- @string path location in file system to create directory
-- @treturn[1] int `0`, if successful
-- @return[2] nil
-- @treturn[2] string error message
-- @treturn[2] int errnum
local bit = require "bit32"
local st = require "posix.sys.stat"
local _mkdir = st.mkdir
local RWXALL = bit.bor (st.S_IRWXU, st.S_IRWXG, st.S_IRWXO)
local function mkdir (path)
return _mkdir (path, RWXALL)
end
if _DEBUG ~= false then
M.mkdir = function (...)
local argt = {...}
checkstring ("mkdir", 1, argt[1])
if #argt > 1 then toomanyargerror ("mkdir", 1, #argt) end
return mkdir (...)
end
else
M.mkdir = mkdir
end
--- Make a FIFO pipe.
-- @function mkfifo
-- @string path location in file system to create fifo
-- @treturn[1] int `0`, if successful
-- @return[2] nil
-- @treturn[2] string error message
-- @treturn[2] int errnum
local bit = require "bit32"
local st = require "posix.sys.stat"
local _mkfifo = st.mkfifo
local RWXALL = bit.bor (st.S_IRWXU, st.S_IRWXG, st.S_IRWXO)
local function mkfifo (path)
return _mkfifo (path, RWXALL)
end
if _DEBUG ~= false then
M.mkfifo = function (...)
local argt = {...}
checkstring ("mkfifo", 1, argt[1])
if #argt > 1 then toomanyargerror ("mkfifo", 1, #argt) end
return mkfifo (...)
end
else
M.mkfifo = mkfifo
end
--- Get a message queue identifier
-- @function msgget
-- @int key message queue id, or `IPC_PRIVATE` for a new queue
-- @int[opt=0] flags bitwise OR of zero or more from `IPC_CREAT` and `IPC_EXCL`
-- @string[opt="rw-rw-rw-"] mode execute bits are ignored
-- @treturn[1] int message queue identifier, if successful
-- @return[2] nil
-- @treturn[2] string error message
-- @treturn[2] int errnum
-- @see msgget(2)
local bit = require "bit32"
local msg = require "posix.sys.msg"
local st = require "posix.sys.stat"
local _msgget = msg.msgget
local band, bor = bit.band, bit.bor
local RWXALL = bor (st.S_IRWXU, st.S_IRWXG, st.S_IRWXO)
local function msgget (key, msgflg, modestr)
local mode, err = mode_munch (0, modestr)
if mode == nil then
argerror ("open", 3, err, 2)
end
return _msgget (key, bor (msgflg, band (mode, RWXALL)))
end
if not _msgget then
-- Not supported by underlying system
elseif _DEBUG ~= false then
M.msgget = function (...)
local argt = {...}
checkint ("msgget", 1, argt[1])
if argt[2] ~= nil and type (argt[2]) ~= "number" then
argtypeerror ("msgget", 2, "int or nil", argt[2])
end
if argt[3] ~= nil and type (argt[3]) ~= "string" then
argtypeerror ("msgget", 3, "string or nil", argt[3])
end
if #argt > 3 then toomanyargerror ("msgget", 3, #argt) end
return msgget (...)
end
else
M.msgget = msgget
end
--- Open a file.
-- @function open
-- @string path file to act on
-- @int oflags bitwise OR of zero or more of `O_RDONLY`, `O_WRONLY`, `O_RDWR`,
-- `O_APPEND`, `O_CREAT`, `O_DSYNC`, `O_EXCL`, `O_NOCTTY`, `O_NONBLOCK`,
-- `O_RSYNC`, `O_SYNC`, `O_TRUNC`
-- @string modestr (used with `O_CREAT`; see @{chmod} for format)
-- @treturn[1] int file descriptor for *path*, if successful
-- @return[2] nil
-- @treturn[2] string error message
-- @treturn[2] int errnum
-- @see open(2)
-- @usage
-- fd = P.open ("data", bit.bor (P.O_CREAT, P.O_RDWR), "rw-r-----")
local bit = require "bit32"
local fcntl = require "posix.fcntl"
local _open, O_CREAT = fcntl.open, fcntl.O_CREAT
local band = bit.band
local function open (path, oflags, modestr)
local mode
if band (oflags, O_CREAT) ~= 0 then
mode, err = mode_munch (0, modestr)
if mode == nil then
argerror ("open", 3, err, 2)
end
mode = band (mode, RWXALL)
end
return _open (path, oflags, mode)
end
if _DEBUG ~= false then
M.open = function (...)
local argt, maxt = {...}, 2
checkstring ("open", 1, argt[1])
local oflags = checkint ("open", 2, argt[2])
if band (oflags, O_CREAT) ~= 0 then
checkstring ("open", 3, argt[3])
maxt = 3
end
if #argt > maxt then toomanyargerror ("open", maxt, #argt) end
return open (...)
end
else
M.creat = creat
end
--- Set log priority mask
-- @function setlogmask
-- @int ... zero or more of `LOG_EMERG`, `LOG_ALERT`, `LOG_CRIT`,
-- `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO` and `LOG_DEBUG`
-- @treturn[1] int `0`, if successful
-- @return[2] nil
-- @treturn[2] string error message
-- @treturn[2] int errnum
local bit = require "bit32"
local log = require "posix.syslog"
local bor = bit.bor
local _setlogmask, LOG_MASK = log.setlogmask, log.LOG_MASK
local function setlogmask (...)
local mask = 0
for _, v in ipairs {...} do
mask = bor (mask, LOG_MASK (v))
end
return _setlogmask (mask)
end
if _DEBUG ~= false then
M.setlogmask = function (...)
for i, v in ipairs {...} do
optint ("setlogmask", i, v, 0) -- for "int or nil" error
end
return setlogmask (...)
end
else
M.setlogmask = setlogmask
end
--- Set file mode creation mask.
-- @function umask
-- @string[opt] mode file creation mask string
-- @treturn string previous umask
-- @see umask(2)
-- @see posix.sys.stat.umask
local st = require "posix.sys.stat"
local _umask, RWXALL = st.umask, bor (st.S_IRWXU, st.S_IRWXG, st.S_IRWXO)
local function umask (modestr)
modestr = modestr or ""
local mode = _umask (0)
_umask (mode)
mode = band (bnot (mode), RWXALL)
if #modestr > 0 then
local bits, err = mode_munch (mode, modestr)
if bits == nil then
argerror ("umask", 1, err, 2)
end
mode = band (bits, RWXALL)
_umask (bnot (mode))
end
return pushmode (mode)
end
if _DEBUG ~= false then
M.umask = function (modestr, ...)
local argt = {modestr, ...}
optstring ("umask", 1, modestr, "")
if #argt > 1 then
toomanyargerror ("umask", 1, #argt)
end
return umask (modestr)
end
else
M.umask = umask
end
return M