luci-base: break circular luci.config <> luci.model.uci dependency
[project/luci.git] / modules / luci-base / luasrc / model / ipkg.lua
index f5a11c2..e27ea52 100644 (file)
@@ -1,4 +1,4 @@
--- Copyright 2008-2011 Jo-Philipp Wich <xm@subsignal.org>
+-- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
 -- Copyright 2008 Steven Barth <steven@midlink.org>
 -- Licensed to the public under the Apache License 2.0.
 
@@ -15,18 +15,19 @@ local table = table
 local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
 local icfg = "/etc/opkg.conf"
 
---- LuCI OPKG call abstraction library
 module "luci.model.ipkg"
 
 
 -- Internal action function
 local function _action(cmd, ...)
-       local pkg = ""
+       local cmdline = { ipkg, cmd }
+
+       local k, v
        for k, v in pairs({...}) do
-               pkg = pkg .. " '" .. v:gsub("'", "") .. "'"
+               cmdline[#cmdline+1] = util.shellquote(v)
        end
 
-       local c = "%s %s %s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" %{ ipkg, cmd, pkg }
+       local c = "%s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" % table.concat(cmdline, " ")
        local r = os.execute(c)
        local e = fs.readfile("/tmp/opkg.stderr")
        local o = fs.readfile("/tmp/opkg.stdout")
@@ -75,17 +76,17 @@ local function _parselist(rawdata)
 end
 
 -- Internal lookup function
-local function _lookup(act, pkg)
-       local cmd = ipkg .. " " .. act
+local function _lookup(cmd, pkg)
+       local cmdline = { ipkg, cmd }
        if pkg then
-               cmd = cmd .. " '" .. pkg:gsub("'", "") .. "'"
+               cmdline[#cmdline+1] = util.shellquote(pkg)
        end
 
        -- OPKG sometimes kills the whole machine because it sucks
        -- Therefore we have to use a sucky approach too and use
        -- tmpfiles instead of directly reading the output
        local tmpfile = os.tmpname()
-       os.execute(cmd .. (" >%s 2>/dev/null" % tmpfile))
+       os.execute("%s >%s 2>/dev/null" %{ table.concat(cmdline, " "), tmpfile })
 
        local data = _parselist(io.lines(tmpfile))
        os.remove(tmpfile)
@@ -93,80 +94,67 @@ local function _lookup(act, pkg)
 end
 
 
---- Return information about installed and available packages.
--- @param pkg Limit output to a (set of) packages
--- @return Table containing package information
 function info(pkg)
        return _lookup("info", pkg)
 end
 
---- Return the package status of one or more packages.
--- @param pkg Limit output to a (set of) packages
--- @return Table containing package status information
 function status(pkg)
        return _lookup("status", pkg)
 end
 
---- Install one or more packages.
--- @param ... List of packages to install
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
 function install(...)
        return _action("install", ...)
 end
 
---- Determine whether a given package is installed.
--- @param pkg Package
--- @return Boolean
 function installed(pkg)
        local p = status(pkg)[pkg]
        return (p and p.Status and p.Status.installed)
 end
 
---- Remove one or more packages.
--- @param ... List of packages to install
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
 function remove(...)
        return _action("remove", ...)
 end
 
---- Update package lists.
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
 function update()
        return _action("update")
 end
 
---- Upgrades all installed packages.
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
 function upgrade()
        return _action("upgrade")
 end
 
 -- List helper
-function _list(action, pat, cb)
-       local fd = io.popen(ipkg .. " " .. action ..
-               (pat and (" '%s'" % pat:gsub("'", "")) or ""))
+local function _list(action, pat, cb)
+       local cmdline = { ipkg, action }
+       if pat then
+               cmdline[#cmdline+1] = util.shellquote(pat)
+       end
 
+       local fd = io.popen(table.concat(cmdline, " "))
        if fd then
-               local name, version, desc
+               local name, version, sz, desc
                while true do
                        local line = fd:read("*l")
                        if not line then break end
 
-                       name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
+                       name, version, sz, desc = line:match("^(.-) %- (.-) %- (.-) %- (.+)")
 
                        if not name then
-                               name, version = line:match("^(.-) %- (.+)")
+                               name, version, sz = line:match("^(.-) %- (.-) %- (.+)")
                                desc = ""
                        end
 
-                       cb(name, version, desc)
+                       if name and version then
+                               if #version > 26 then
+                                       version = version:sub(1,21) .. ".." .. version:sub(-3,-1)
+                               end
+
+                               cb(name, version, sz, desc)
+                       end
 
                        name    = nil
                        version = nil
+                       sz      = nil
                        desc    = nil
                end
 
@@ -174,33 +162,19 @@ function _list(action, pat, cb)
        end
 end
 
---- List all packages known to opkg.
--- @param pat  Only find packages matching this pattern, nil lists all packages
--- @param cb   Callback function invoked for each package, receives name, version and description as arguments
--- @return     nothing
 function list_all(pat, cb)
-       _list("list", pat, cb)
+       _list("list --size", pat, cb)
 end
 
---- List installed packages.
--- @param pat  Only find packages matching this pattern, nil lists all packages
--- @param cb   Callback function invoked for each package, receives name, version and description as arguments
--- @return     nothing
 function list_installed(pat, cb)
-       _list("list_installed", pat, cb)
+       _list("list_installed --size", pat, cb)
 end
 
---- Find packages that match the given pattern.
--- @param pat  Find packages whose names or descriptions match this pattern, nil results in zero results
--- @param cb   Callback function invoked for each patckage, receives name, version and description as arguments
--- @return     nothing
 function find(pat, cb)
-       _list("find", pat, cb)
+       _list("find --size", pat, cb)
 end
 
 
---- Determines the overlay root used by opkg.
--- @return             String containing the directory path of the overlay root.
 function overlay_root()
        local od = "/"
        local fd = io.open(icfg, "r")
@@ -227,3 +201,47 @@ function overlay_root()
 
        return od
 end
+
+function compare_versions(ver1, comp, ver2)
+       if not ver1 or not ver2
+       or not comp or not (#comp > 0) then
+               error("Invalid parameters")
+               return nil
+       end
+       -- correct compare string
+       if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~="
+       elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<="
+       elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">="
+       elseif comp == "="  or comp == "==" then comp = "=="
+       elseif comp == "<<" then comp = "<"
+       elseif comp == ">>" then comp = ">"
+       else
+               error("Invalid compare string")
+               return nil
+       end
+
+       local av1 = util.split(ver1, "[%.%-]", nil, true)
+       local av2 = util.split(ver2, "[%.%-]", nil, true)
+
+       local max = table.getn(av1)
+       if (table.getn(av1) < table.getn(av2)) then
+               max = table.getn(av2)
+       end
+
+       for i = 1, max, 1  do
+               local s1 = av1[i] or ""
+               local s2 = av2[i] or ""
+
+               -- first "not equal" found return true
+               if comp == "~=" and (s1 ~= s2) then return true end
+               -- first "lower" found return true
+               if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
+               -- first "greater" found return true
+               if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
+               -- not equal then return false
+               if (s1 ~= s2) then return false end
+       end
+
+       -- all equal and not compare greater or lower then true
+       return not (comp == "<" or comp == ">")
+end