2018-11-24 00:49:01 +08:00
|
|
|
-- Grab : Official OpenComputerScripts Installer
|
2018-11-17 17:26:07 +08:00
|
|
|
-- Created By Kiritow
|
2018-11-21 12:14:03 +08:00
|
|
|
local computer=require('computer')
|
2018-11-17 17:26:07 +08:00
|
|
|
local component=require('component')
|
|
|
|
local shell=require('shell')
|
2018-11-21 11:55:45 +08:00
|
|
|
local filesystem=require('filesystem')
|
|
|
|
local serialization=require('serialization')
|
2018-11-24 16:04:29 +08:00
|
|
|
local event=require('event')
|
2018-11-30 17:48:05 +08:00
|
|
|
local term=require('term')
|
2018-11-17 17:26:07 +08:00
|
|
|
local args,options=shell.parse(...)
|
2018-11-24 00:49:01 +08:00
|
|
|
|
2019-01-07 20:18:34 +08:00
|
|
|
local grab_version="Grab v2.4.9-alpha"
|
2018-12-07 20:08:56 +08:00
|
|
|
local grab_version_info={
|
|
|
|
version=grab_version
|
|
|
|
}
|
2018-11-30 17:48:05 +08:00
|
|
|
|
|
|
|
local usage_text=[===[Grab - Official OpenComputerScripts Installer
|
|
|
|
Usage:
|
|
|
|
grab [<options>] <command> ...
|
|
|
|
Options:
|
|
|
|
--cn Use mirror site in China. By default grab will download from Github. This might be useful for only official packages.
|
|
|
|
--help Display this help page.
|
|
|
|
--version Display version and exit.
|
2018-12-03 15:24:35 +08:00
|
|
|
--router=<Router File> Given a file which will be loaded and returns a route function like:
|
2018-11-30 17:48:05 +08:00
|
|
|
function(RepoName: string, Branch: string ,FileAddress: string): string
|
2018-12-03 15:24:35 +08:00
|
|
|
--proxy=<Proxy File> Given a file which will be loaded and returns a proxy function like:
|
|
|
|
function(Url : string): boolean, string
|
2019-01-07 19:34:59 +08:00
|
|
|
--bin=<path> Set binary install root path.
|
|
|
|
--lib=<path> Set library install root path.
|
2019-01-07 20:18:34 +08:00
|
|
|
-f,--force Force overwrite existing files.
|
2018-11-30 17:48:05 +08:00
|
|
|
--skip-install Library installers will not be executed.
|
|
|
|
--refuse-license <License> Set refused license. Separate multiple values with ','
|
|
|
|
--accept-license <License> Set accepted license. Separate multiple values with ','
|
|
|
|
Command:
|
|
|
|
install <Project> ...: Install projects. Dependency will be downloaded automatically.
|
2018-12-03 14:16:35 +08:00
|
|
|
verify <Provider> ... : Verify program provider info.
|
2018-11-30 17:48:05 +08:00
|
|
|
add <Provider> ... : Add program provider info.
|
|
|
|
update: Update program info.
|
|
|
|
clear: Clear program info.
|
|
|
|
list: List available projects.
|
|
|
|
search <Name or Pattern> : Search projects by name
|
|
|
|
show <Project> : Show more info about project.
|
|
|
|
download <Filename> ...: Directly download files. (Just like the old `update`!)
|
|
|
|
Notice:
|
|
|
|
License
|
|
|
|
By downloading and using Grab, you are indicating your agreement to MIT license. (https://github.com/Kiritow/OpenComputerScripts/blob/master/LICENSE)
|
|
|
|
All scripts in official OpenComputerScript repository are under MIT license.
|
|
|
|
Before downloading any package under other licenses, Grab will ask you to agree with it.
|
|
|
|
This confirmation can be skipped by calling Grab with --accept-license.
|
|
|
|
Example:
|
|
|
|
--accept-license=mit means MIT License is accepted.
|
|
|
|
--refuse-license=mit means MIT License is refused.
|
|
|
|
--accept-license means all licenses are accepted.
|
|
|
|
--refuse-license means all licenses are refused. (Official packages are not affected.)
|
|
|
|
If a license is both accepted and refused, it will be refused.
|
|
|
|
Program Provider
|
|
|
|
A package is considered to be official only if it does not specified repo and proxy. Official packages usually only depend on official packages.
|
|
|
|
You can also install packages from unofficial program provider with Grab, but Grab will not check its security.
|
|
|
|
Notice that override of official packages is not allowed.
|
2018-12-03 15:24:35 +08:00
|
|
|
Router and Proxy
|
|
|
|
route_func(RepoName: string, Branch: string ,FileAddress: string): string
|
|
|
|
A route function takes repo, branch and file address as arguments, and returns a resolved url.
|
|
|
|
It can be used to boost downloading by redirecting requests to mirror site.
|
|
|
|
As router functions can be used to redirect requests, Grab will give an warning if --router option presents.
|
|
|
|
proxy_func(Url : string): boolean, string
|
|
|
|
A proxy function takes url as argument, and returns at least 2 values.
|
|
|
|
It can be used to handle different protocols or low-level network operations like downloading files via SOCKS5 proxy or in-game modem network.
|
|
|
|
The first returned value is true if content is downloaded successfully. Thus, the second value will be the downloaded content.
|
|
|
|
If the first value is false, the downloading is failed. The second value will then be the error message.
|
|
|
|
If proxy functions throw an error, Grab will try the default downloader.
|
2018-12-08 10:57:05 +08:00
|
|
|
Installer
|
|
|
|
A package can provide an installer for Grab. It will be loaded and executed after the package is ready.
|
|
|
|
Thus require(...) calls on depended libraries is ok.
|
|
|
|
From Grab v2.4.6, installer should return a function, which will be later called with a table filled with some information. (Currently, it contains version tag of Grab.)
|
|
|
|
If nothing is returned, Grab will give an warning and ignore it.
|
2019-01-07 19:34:59 +08:00
|
|
|
From Grab v2.4.8, option `installer` is deprecated. Use __installer__ instead.
|
2018-11-30 17:48:05 +08:00
|
|
|
]===]
|
|
|
|
|
|
|
|
-- Install man document
|
2018-12-03 15:24:35 +08:00
|
|
|
local function _update_document()
|
2018-11-30 18:19:58 +08:00
|
|
|
local f=io.open("/etc/grab/grab.version","w")
|
|
|
|
if(f) then
|
|
|
|
f:write(grab_version)
|
|
|
|
f:close()
|
|
|
|
end
|
|
|
|
f=io.open("/usr/man/grab","w")
|
2018-11-30 17:48:05 +08:00
|
|
|
if(f) then
|
|
|
|
f:write(usage_text)
|
|
|
|
f:close()
|
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
end
|
|
|
|
if(not filesystem.exists("/etc/grab/grab.version")) then
|
|
|
|
_update_document()
|
2018-11-30 18:19:58 +08:00
|
|
|
else
|
|
|
|
local f=io.open("/etc/grab/grab.version","r")
|
|
|
|
if(f) then
|
|
|
|
local installed_version=f:read("a")
|
|
|
|
f:close()
|
|
|
|
if(installed_version~=grab_version) then
|
2018-12-03 15:24:35 +08:00
|
|
|
_update_document()
|
2018-11-30 18:19:58 +08:00
|
|
|
end
|
|
|
|
end
|
2018-11-30 17:48:05 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
local function show_usage()
|
|
|
|
if(filesystem.exists("/usr/man/grab")) then
|
|
|
|
os.execute("less /usr/man/grab")
|
|
|
|
else
|
|
|
|
local temp_name=os.tmpname()
|
|
|
|
local f=io.open(temp_name,"w")
|
|
|
|
f:write(usage_text)
|
|
|
|
f:close()
|
|
|
|
os.execute("less " .. temp_name)
|
|
|
|
os.execute("rm " .. temp_name)
|
|
|
|
end
|
|
|
|
end
|
2018-11-24 00:49:01 +08:00
|
|
|
|
|
|
|
local valid_options={
|
2018-11-30 17:48:05 +08:00
|
|
|
["cn"]=true,
|
|
|
|
["help"]=true,
|
|
|
|
["version"]=true,
|
2018-12-03 15:24:35 +08:00
|
|
|
["router"]="string",
|
|
|
|
["proxy"]="string",
|
2019-01-07 19:34:59 +08:00
|
|
|
["bin"]="string",
|
|
|
|
["lib"]="string",
|
2019-01-07 20:18:34 +08:00
|
|
|
["f"]=true,
|
|
|
|
["force"]=true,
|
2018-11-30 17:48:05 +08:00
|
|
|
["skip-install"]=true,
|
|
|
|
["refuse-license"]=true,
|
2019-01-07 19:34:59 +08:00
|
|
|
["accept-license"]=true,
|
2018-11-24 00:49:01 +08:00
|
|
|
}
|
|
|
|
local valid_command={
|
2018-11-26 00:49:18 +08:00
|
|
|
["install"]=true,
|
2018-12-03 14:16:35 +08:00
|
|
|
["verify"]=true,
|
2018-11-30 17:48:05 +08:00
|
|
|
["add"]=true,
|
|
|
|
["update"]=true,
|
|
|
|
["clear"]=true,
|
|
|
|
["list"]=true,
|
|
|
|
["search"]=true,
|
|
|
|
["show"]=true,
|
2018-11-26 00:49:18 +08:00
|
|
|
["download"]=true
|
2018-11-24 00:49:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for k,v in pairs(options) do
|
|
|
|
if(not valid_options[k]) then
|
2018-12-03 15:24:35 +08:00
|
|
|
if(string.len(k)>1) then
|
|
|
|
print("Unknown option: --" .. k)
|
|
|
|
else
|
|
|
|
print("Unknown option: -" .. k)
|
|
|
|
end
|
2018-11-24 00:49:01 +08:00
|
|
|
return
|
2018-12-03 15:24:35 +08:00
|
|
|
elseif(type(valid_options[k])=="string") then
|
|
|
|
if(type(options[k])~=valid_options[k]) then
|
|
|
|
print("Invalid option type: Option type of --" .. k .. " should be " .. valid_options[k])
|
|
|
|
return
|
|
|
|
end
|
2018-11-24 00:49:01 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-03 15:24:35 +08:00
|
|
|
if( (#args<1 and not next(options)) or options["help"]) then
|
2018-11-24 00:49:01 +08:00
|
|
|
show_usage()
|
|
|
|
return
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-24 00:49:01 +08:00
|
|
|
|
2018-11-17 19:21:26 +08:00
|
|
|
if(options["version"]) then
|
2018-11-24 00:49:01 +08:00
|
|
|
print(grab_version)
|
2018-11-17 19:21:26 +08:00
|
|
|
return
|
|
|
|
end
|
2018-11-24 00:49:01 +08:00
|
|
|
|
2018-12-03 15:24:35 +08:00
|
|
|
local function check_internet()
|
2018-12-03 16:58:54 +08:00
|
|
|
if(not options["proxy"] and not component.list("internet")()) then
|
2018-12-03 15:24:35 +08:00
|
|
|
print("Error: An internet card is required to run this program.")
|
|
|
|
return false
|
|
|
|
else
|
|
|
|
-- If proxy presents, internet card is not required. Programs may handle network requests via in-game modem network.
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function default_downloader(url)
|
2018-12-03 16:58:54 +08:00
|
|
|
if(not component.list("internet")()) then
|
2018-12-03 15:24:35 +08:00
|
|
|
return false,"No internet card found."
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
|
2018-11-17 17:26:07 +08:00
|
|
|
local handle=component.internet.request(url)
|
|
|
|
while true do
|
|
|
|
local ret,err=handle.finishConnect()
|
|
|
|
if(ret==nil) then
|
|
|
|
return false,err
|
|
|
|
elseif(ret==true) then
|
|
|
|
break
|
2018-11-24 01:29:56 +08:00
|
|
|
else
|
2018-11-30 03:14:49 +08:00
|
|
|
local ev=event.pull(0.05,"interrupted")
|
2018-11-24 01:29:56 +08:00
|
|
|
if(ev~=nil) then
|
|
|
|
handle.close()
|
|
|
|
return false,"Interrupted from terminal."
|
|
|
|
end
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
|
2018-11-17 17:26:07 +08:00
|
|
|
local code=handle.response()
|
2018-12-03 15:24:35 +08:00
|
|
|
if(code~=200) then
|
|
|
|
handle.close()
|
|
|
|
return false,"Response code " .. code .. " is not 200."
|
|
|
|
end
|
|
|
|
|
2018-11-17 17:26:07 +08:00
|
|
|
local result=''
|
|
|
|
while true do
|
|
|
|
local temp=handle.read()
|
|
|
|
if(temp==nil) then break end
|
|
|
|
result=result .. temp
|
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
|
2018-11-17 17:26:07 +08:00
|
|
|
handle.close()
|
2018-12-03 15:24:35 +08:00
|
|
|
return true,result
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
|
2018-11-17 17:26:07 +08:00
|
|
|
local UrlGenerator
|
2018-12-03 15:24:35 +08:00
|
|
|
if(not options["router"]) then
|
2018-11-17 19:21:26 +08:00
|
|
|
if(not options["cn"]) then
|
|
|
|
UrlGenerator=function(RepoName,Branch,FileAddress)
|
|
|
|
return "https://raw.githubusercontent.com/" .. RepoName .. "/" .. Branch .. "/" .. FileAddress
|
|
|
|
end
|
|
|
|
else
|
|
|
|
UrlGenerator=function(RepoName,Branch,FileAddress)
|
|
|
|
return "http://kiritow.com:3000/" .. RepoName .. "/raw/" .. Branch .. "/" .. FileAddress
|
|
|
|
end
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
else
|
|
|
|
local ok,err=pcall(function()
|
|
|
|
local fn,xerr=loadfile(options["router"])
|
|
|
|
if(not fn) then
|
|
|
|
error(xerr)
|
|
|
|
else
|
|
|
|
UrlGenerator=fn()
|
|
|
|
if(type(UrlGenerator)~="function") then
|
|
|
|
error("Loaded router returns " .. type(UrlGenerator) .. " instead of a function.")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
if(not ok) then
|
|
|
|
print("Unable to load router file: " .. err)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
print("[WARN] Router presents. Be aware of security issues.")
|
|
|
|
end
|
2018-12-03 16:58:54 +08:00
|
|
|
|
2018-12-03 15:24:35 +08:00
|
|
|
local download
|
|
|
|
if(not options["proxy"]) then
|
|
|
|
download=default_downloader
|
2018-11-17 17:26:07 +08:00
|
|
|
else
|
2018-11-17 19:21:26 +08:00
|
|
|
local ok,err=pcall(function()
|
2018-11-26 01:33:41 +08:00
|
|
|
local fn,xerr=loadfile(options["proxy"])
|
2018-12-03 15:24:35 +08:00
|
|
|
if(not fn) then
|
|
|
|
error(xerr)
|
|
|
|
else
|
2018-12-03 16:58:54 +08:00
|
|
|
tmp=fn()
|
|
|
|
if(type(tmp)~="function") then
|
|
|
|
error("Loaded proxy returns " .. type(tmp) .. " instead of a function.")
|
|
|
|
end
|
|
|
|
download=function(url)
|
|
|
|
local pok,ok,data=pcall(tmp,url)
|
|
|
|
if(pok) then
|
|
|
|
return ok,data
|
|
|
|
else
|
|
|
|
return default_downloader(url)
|
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
end
|
|
|
|
end
|
2018-11-17 19:21:26 +08:00
|
|
|
end)
|
|
|
|
if(not ok) then
|
2018-11-26 01:33:41 +08:00
|
|
|
print("Unable to load proxy file: " .. err)
|
2018-11-17 19:21:26 +08:00
|
|
|
return
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
|
|
|
|
print("[WARN] Proxy presents. Be aware of security issues.")
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
|
2018-11-30 17:48:05 +08:00
|
|
|
local function IsOfficial(tb_package)
|
2018-12-03 15:24:35 +08:00
|
|
|
if(tb_package.repo==nil and
|
|
|
|
tb_package.proxy==nil and
|
|
|
|
tb_package.provider==nil
|
|
|
|
) then
|
2018-11-30 17:48:05 +08:00
|
|
|
return true
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-03 15:24:35 +08:00
|
|
|
local function IsTrusted(tb_package)
|
|
|
|
if(tb_package.provider) then
|
|
|
|
-- TODO: Check Provider by comparing with online trusted list.
|
2018-12-03 16:58:54 +08:00
|
|
|
return false
|
2018-12-03 15:24:35 +08:00
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
local db_dirs={"/etc/grab",".grab","/tmp/.grab"}
|
|
|
|
local db_positions={"/etc/grab/programs.info",".grab/programs.info","/tmp/.grab/programs.info"}
|
|
|
|
|
2018-11-30 17:48:05 +08:00
|
|
|
local function VerifyDB(this_db)
|
|
|
|
for k,t in pairs(this_db) do
|
|
|
|
if(type(k)~="string") then
|
|
|
|
return false,"Invalid key type: " .. type(k)
|
|
|
|
elseif(type(t)~="table") then
|
|
|
|
return false,"Invalid value type: " .. type(t)
|
|
|
|
elseif(not t.title) then
|
|
|
|
return false,"Library " .. k .. " does not provide title."
|
|
|
|
elseif(not t.info) then
|
|
|
|
return false,"Library " .. k .. " does not provide info."
|
|
|
|
elseif(not t.files) then
|
|
|
|
return false,"Library " .. k .. " has no file."
|
|
|
|
end
|
|
|
|
|
|
|
|
for kk,vv in pairs(t.files) do
|
|
|
|
if(type(kk)=="number") then
|
|
|
|
if(type(vv)~="string") then
|
|
|
|
return false,"Library " .. k .. " file " .. kk .. " has invalid value type " .. type(vv)
|
|
|
|
end
|
|
|
|
elseif(type(kk)=="string") then
|
2018-12-07 13:38:30 +08:00
|
|
|
if(type(vv)=="table") then
|
|
|
|
for idx,val in pairs(vv) do
|
|
|
|
if(type(idx)~="number" or type(val)~="string") then
|
|
|
|
return false,"Library " .. k .. " file " .. kk .. " table has invalid key,value type: " .. type(idx) .. "," .. type(val)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif(type(vv)~="string") then
|
2018-11-30 17:48:05 +08:00
|
|
|
return false,"Library " .. k .. " file " .. kk .. " has invalid value type " .. type(vv)
|
|
|
|
end
|
|
|
|
else
|
2018-12-03 16:58:54 +08:00
|
|
|
return false,"Library " .. k .. " file has invalid key type " .. type(kk)
|
2018-11-30 17:48:05 +08:00
|
|
|
end
|
|
|
|
end
|
2018-12-07 13:38:30 +08:00
|
|
|
|
|
|
|
if(t.author and type(t.author)~="string") then
|
|
|
|
return false,"Library " .. k .. " has invalid author type: " .. type(t.author)
|
|
|
|
end
|
|
|
|
|
|
|
|
if(t.contact and type(t.contact)~="string") then
|
|
|
|
return false,"Library " .. k .. " has invalid contact type: " .. type(t.contact)
|
|
|
|
end
|
|
|
|
|
2018-11-30 17:48:05 +08:00
|
|
|
if(t.requires) then
|
|
|
|
for kk,vv in pairs(t.requires) do
|
2018-12-07 13:38:30 +08:00
|
|
|
if(type(kk)~="number" or type(vv)~="string") then
|
2018-11-30 17:48:05 +08:00
|
|
|
return false,"Library " .. k .. " has invalid requires with key type " .. type(kk) .. ", value type " .. type(vv)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if(t.license) then
|
|
|
|
if(type(t.license.name)~="string") then
|
|
|
|
return false,"Library " .. k .. " has invalid license name type " .. type(t.license.name)
|
|
|
|
elseif(type(t.license.url)~="string") then
|
|
|
|
return false,"Library " .. k .. " has invalid license url type " .. type(t.license.url)
|
|
|
|
end
|
|
|
|
end
|
2018-12-03 16:58:54 +08:00
|
|
|
if(t.provider) then
|
|
|
|
if(type(t.provider)~="string") then
|
|
|
|
return false,"Library " .. k .. " has invalid provider type " .. type(t.provider)
|
|
|
|
end
|
|
|
|
end
|
2018-12-07 13:38:30 +08:00
|
|
|
if(t.hidden~=nil) then
|
|
|
|
if(type(t.hidden)~="boolean") then
|
|
|
|
return false,"Library " .. k .. " has invalid hidden type " .. type(t.hidden)
|
|
|
|
end
|
|
|
|
end
|
2018-11-30 17:48:05 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
return true,"No error detected."
|
|
|
|
end
|
|
|
|
|
|
|
|
local function CheckAndLoadEx(raw_content)
|
2018-11-21 11:55:45 +08:00
|
|
|
local fn,err=load(raw_content)
|
|
|
|
if(fn) then
|
|
|
|
local ok,result=pcall(fn)
|
2018-11-30 17:48:05 +08:00
|
|
|
if(ok) then
|
|
|
|
return result
|
2018-11-21 11:55:45 +08:00
|
|
|
else return nil,result end
|
|
|
|
end
|
|
|
|
return nil,err
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
|
2018-11-30 17:48:05 +08:00
|
|
|
local function CheckAndLoad(raw_content)
|
|
|
|
local result,err=CheckAndLoadEx(raw_content)
|
|
|
|
if(not result) then
|
|
|
|
return result,err
|
|
|
|
end
|
|
|
|
local ok,err=VerifyDB(result)
|
|
|
|
if(not ok) then
|
|
|
|
return nil,err
|
|
|
|
else
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
local function ReadDB(read_from_this)
|
|
|
|
if(read_from_this) then
|
|
|
|
local f=io.open(read_from_this,"r")
|
2018-11-21 11:55:45 +08:00
|
|
|
if(f) then
|
|
|
|
local result=serialization.unserialize(f:read("*a"))
|
2018-11-17 17:26:07 +08:00
|
|
|
f:close()
|
2018-11-21 11:55:45 +08:00
|
|
|
return result,filename
|
2018-11-24 01:29:56 +08:00
|
|
|
else
|
|
|
|
return nil
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
2018-11-24 01:29:56 +08:00
|
|
|
|
|
|
|
for idx,filename in ipairs(db_positions) do
|
|
|
|
local a,b=ReadDB(filename)
|
|
|
|
if(a) then return a,b end
|
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
local function WriteDB(filename,tb)
|
2018-11-21 11:55:45 +08:00
|
|
|
local f=io.open(filename,"w")
|
|
|
|
if(f) then
|
2018-11-24 01:29:56 +08:00
|
|
|
f:write(serialization.serialize(tb))
|
2018-11-21 11:55:45 +08:00
|
|
|
f:close()
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2018-11-30 17:48:05 +08:00
|
|
|
local function UpdateDB(main_tb,new_tb,checked) -- Change values with same key in main_tb to values in new_tb. Add new items to main_tb
|
2018-11-24 01:29:56 +08:00
|
|
|
for k,v in pairs(new_tb) do
|
2018-11-30 17:48:05 +08:00
|
|
|
if(checked and main_tb[k]) then
|
|
|
|
if(IsOfficial(main_tb[k])) then
|
|
|
|
print("UpdateDB: Attempted to override official library: " .. k)
|
|
|
|
return false
|
|
|
|
else
|
|
|
|
print("UpdateDB: Override library: " .. k)
|
|
|
|
end
|
|
|
|
end
|
2018-11-24 01:29:56 +08:00
|
|
|
main_tb[k]=v
|
|
|
|
end
|
2018-11-30 17:48:05 +08:00
|
|
|
return true
|
2018-11-24 01:29:56 +08:00
|
|
|
end
|
|
|
|
|
2018-11-30 17:48:05 +08:00
|
|
|
local function CreateDB(tb,checked) -- If checked, merging is not allowed.
|
2018-11-21 11:55:45 +08:00
|
|
|
for idx,dirname in ipairs(db_dirs) do
|
|
|
|
filesystem.makeDirectory(dirname) -- buggy
|
|
|
|
end
|
|
|
|
for idx,filename in ipairs(db_positions) do
|
2018-11-24 01:29:56 +08:00
|
|
|
local main_db=ReadDB(filename)
|
|
|
|
if(main_db) then
|
2018-11-30 17:48:05 +08:00
|
|
|
if(not UpdateDB(main_db,tb,checked)) then
|
|
|
|
return nil
|
|
|
|
end
|
2018-11-24 01:29:56 +08:00
|
|
|
if(WriteDB(filename,main_db)) then
|
|
|
|
return filename
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if(WriteDB(filename,tb)) then
|
|
|
|
return filename
|
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2018-11-26 00:49:18 +08:00
|
|
|
if(args[1]=="clear") then
|
|
|
|
print("Clearing programs info...")
|
|
|
|
for idx,filename in pairs(db_positions) do
|
|
|
|
filesystem.remove(filename)
|
|
|
|
end
|
|
|
|
print("Programs info cleaned. You may want to run `grab update` now.")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
if(args[1]=="update") then
|
2018-11-24 00:49:01 +08:00
|
|
|
if(not check_internet()) then return end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
print("Updating programs info....")
|
|
|
|
io.write("Downloading... ")
|
2018-12-03 15:24:35 +08:00
|
|
|
local ok,result=download(UrlGenerator("Kiritow/OpenComputerScripts","master","programs.info"))
|
2018-11-17 17:26:07 +08:00
|
|
|
if(not ok) then
|
2018-11-21 11:55:45 +08:00
|
|
|
print("[Failed] " .. result)
|
|
|
|
else
|
|
|
|
print("[OK]")
|
|
|
|
io.write("Validating... ")
|
|
|
|
local tb_data,validate_err=CheckAndLoad("return " .. result)
|
|
|
|
result=nil -- release memory
|
|
|
|
if(tb_data) then
|
|
|
|
print("[OK]")
|
|
|
|
io.write("Saving files... ")
|
2018-11-30 17:48:05 +08:00
|
|
|
local dbfilename=CreateDB(tb_data,false)
|
2018-11-21 11:55:45 +08:00
|
|
|
if(dbfilename) then
|
|
|
|
print("[OK]")
|
|
|
|
print("Programs info updated and saved to " .. dbfilename)
|
|
|
|
else
|
|
|
|
print("[Failed] Unable to save programs info")
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print("[Failed]" .. validate_err)
|
|
|
|
end
|
2018-11-17 17:26:07 +08:00
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local db,dbfilename=ReadDB()
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
local function check_db()
|
|
|
|
if(db) then return true
|
|
|
|
else
|
|
|
|
print("No programs info found on this computer.")
|
|
|
|
print("Please run `grab update` first.")
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-31 01:02:46 +08:00
|
|
|
local function pairsKey(tb)
|
|
|
|
local tmp={}
|
|
|
|
for k in pairs(tb) do table.insert(tmp,k) end
|
|
|
|
table.sort(tmp)
|
|
|
|
local i=0
|
|
|
|
return function()
|
|
|
|
i=i+1
|
|
|
|
return tmp[i],tb[tmp[i]]
|
|
|
|
end,tb,nil
|
|
|
|
end
|
|
|
|
|
2018-12-03 14:16:35 +08:00
|
|
|
if(args[1]=="verify") then
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to verify.")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
for i=2,#args,1 do
|
|
|
|
local url=string.match(args[i],"^http[s]?://%S+")
|
|
|
|
if(url==nil) then
|
|
|
|
local filename=args[i]
|
|
|
|
local f=io.open(filename,"r")
|
|
|
|
if(not f) then
|
|
|
|
print("Unable to open local file: " .. filename)
|
|
|
|
else
|
|
|
|
local content=f:read("*a")
|
|
|
|
f:close()
|
|
|
|
local t,err=CheckAndLoad("return " .. content)
|
|
|
|
if(t) then
|
|
|
|
print("[Verified] Contains the following library: ")
|
2018-12-31 01:02:46 +08:00
|
|
|
for k in pairsKey(t) do
|
2018-12-03 14:16:35 +08:00
|
|
|
print(k)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print("Failed to load local file: " .. filename .. ". Error: " .. err)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print("Downloading from " .. url)
|
2018-12-03 15:24:35 +08:00
|
|
|
local ok,result=download(url)
|
2018-12-03 14:16:35 +08:00
|
|
|
if(not ok) then
|
|
|
|
print("[Download Failed] " .. result)
|
|
|
|
else
|
|
|
|
local t,err=CheckAndLoad("return " .. result)
|
|
|
|
if(t) then
|
|
|
|
print("[Verified] Contains the following library: ")
|
|
|
|
for k in pairs(t) do
|
|
|
|
print(k)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print("Failed to load downloaded content. Error: " .. err)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-11-26 00:49:18 +08:00
|
|
|
if(args[1]=="add") then
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to add.")
|
2018-11-30 03:14:49 +08:00
|
|
|
return
|
2018-11-26 00:49:18 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
if(not check_db()) then
|
|
|
|
return
|
|
|
|
end
|
2018-11-26 01:33:41 +08:00
|
|
|
|
|
|
|
print("[WARN] Adding unofficial program providers may have security issues.")
|
2018-11-26 00:49:18 +08:00
|
|
|
|
|
|
|
for i=2,#args,1 do
|
|
|
|
local url=string.match(args[i],"^http[s]?://%S+")
|
|
|
|
if(url==nil) then
|
|
|
|
local filename=args[i]
|
|
|
|
local f=io.open(filename,"r")
|
|
|
|
if(not f) then
|
|
|
|
print("Unable to open local file: " .. filename)
|
|
|
|
else
|
|
|
|
local content=f:read("*a")
|
|
|
|
f:close()
|
|
|
|
local t,err=CheckAndLoad("return " .. content)
|
|
|
|
if(t) then
|
|
|
|
print("Updating with local file: " .. filename)
|
2018-11-30 17:48:05 +08:00
|
|
|
local fname=CreateDB(t,true)
|
2018-11-26 00:49:18 +08:00
|
|
|
if(fname) then
|
|
|
|
print("Programs info updated and saved to " .. fname)
|
|
|
|
else
|
|
|
|
print("Unable to update programs info.")
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print("Failed to load local file: " .. filename .. ". Error: " .. err)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print("Downloading from " .. url)
|
2018-12-03 15:24:35 +08:00
|
|
|
local ok,result=download(url)
|
2018-11-26 00:49:18 +08:00
|
|
|
if(not ok) then
|
|
|
|
print("[Download Failed] " .. result)
|
|
|
|
else
|
|
|
|
local t,err=CheckAndLoad("return " .. result)
|
|
|
|
if(t) then
|
|
|
|
print("Updating with downloaded content...")
|
2018-11-30 17:48:05 +08:00
|
|
|
local fname=CreateDB(t,true)
|
2018-11-26 00:49:18 +08:00
|
|
|
if(fname) then
|
|
|
|
print("Programs info updated and saved to " .. fname)
|
|
|
|
else
|
|
|
|
print("Unable to update programs info.")
|
|
|
|
end
|
|
|
|
else
|
|
|
|
print("Failed to load downloaded content. Error: " .. err)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-11-30 11:10:08 +08:00
|
|
|
local function getshowbyte(n)
|
|
|
|
if(n<1024) then
|
|
|
|
return string.format("%.1f B",n+0.0)
|
|
|
|
elseif(n<1024*1024) then
|
|
|
|
return string.format("%.1f KB",n/1024)
|
|
|
|
else
|
|
|
|
return string.format("%.1f MB",n/1024/1024)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function getshowtime(n)
|
|
|
|
if(n<60) then
|
|
|
|
return string.format("%.1fs",n+0.0)
|
|
|
|
else
|
|
|
|
return string.format("%.0fm%.0fs",n/3600,n%3600)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-07 20:08:56 +08:00
|
|
|
local function getshowspeed(n)
|
|
|
|
if(n<1024) then
|
|
|
|
return string.format("%.1f B/s",n+0.0)
|
|
|
|
elseif(n<1024*1024) then
|
|
|
|
return string.format("%.1f KB/s",n/1024)
|
|
|
|
else
|
|
|
|
return string.format("%.1f MB/s",n/1024/1024)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function try_resolve_path(src,dst)
|
|
|
|
-- TIPS:
|
|
|
|
-- filesystem.makeDirectory(...) can throw error because it does not check arguments.
|
2019-01-07 19:34:59 +08:00
|
|
|
|
|
|
|
if(type(src)~="string") then -- Only source path is specified in programs.info
|
2019-01-07 14:27:51 +08:00
|
|
|
local segs=filesystem.segments(dst)
|
|
|
|
return true,segs[#segs]
|
|
|
|
end
|
2018-12-07 20:08:56 +08:00
|
|
|
|
2019-01-07 19:34:59 +08:00
|
|
|
dst=string.gsub(
|
|
|
|
string.gsub(
|
|
|
|
dst,
|
|
|
|
"__bin__",
|
|
|
|
options["bin"] or "/usr/bin"
|
|
|
|
),
|
|
|
|
"__lib__",
|
|
|
|
options["lib"] or "/usr/lib"
|
|
|
|
)
|
|
|
|
|
|
|
|
if(dst:sub(dst:len())=='/') then -- dst is a directory. prepare it and build the filename.
|
2018-12-07 20:08:56 +08:00
|
|
|
if(not filesystem.makeDirectory(dst) and not filesystem.exists(dst)) then
|
|
|
|
return false,"Failed to create directory: " .. dst
|
|
|
|
else
|
|
|
|
local tb_segsrc=filesystem.segments(src)
|
2019-01-07 19:34:59 +08:00
|
|
|
return true,dst .. tb_segsrc[#tb_segsrc]
|
2018-12-07 20:08:56 +08:00
|
|
|
end
|
2019-01-07 19:34:59 +08:00
|
|
|
else -- dst is the filename. Prepare directories.
|
2018-12-31 01:42:46 +08:00
|
|
|
local tb_segdst=filesystem.segments(dst)
|
2018-12-07 20:08:56 +08:00
|
|
|
if(#tb_segdst>1) then
|
|
|
|
local name=table.concat(tb_segdst,"/",1,#tb_segdst-1)
|
|
|
|
if(not filesystem.makeDirectory(name) and not filesystem.exists(name)) then
|
|
|
|
return false,"Failed to create directory: " .. name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return true,dst
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-07 13:38:30 +08:00
|
|
|
local function string_similar_value(a,b)
|
|
|
|
local x,y=a:len(),b:len()
|
|
|
|
local min=( (x>y) and y or x)
|
|
|
|
local c=0
|
|
|
|
for i=1,min do
|
|
|
|
if(a:sub(i,i)==b:sub(i,i)) then
|
|
|
|
c=c+1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return c
|
|
|
|
end
|
|
|
|
|
|
|
|
local function miss_suggestion(wrong_name,ktb)
|
|
|
|
local max=0
|
|
|
|
local maxname=nil
|
|
|
|
for this_lib in pairs(ktb) do
|
|
|
|
local a=string_similar_value(wrong_name,this_lib)
|
|
|
|
if(a>max) then
|
|
|
|
max=a
|
|
|
|
maxname=this_lib
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return maxname,max
|
|
|
|
end
|
|
|
|
|
2019-01-07 20:18:34 +08:00
|
|
|
local function will_overwrite(filename)
|
|
|
|
if(options["f"] or options["force"]) then
|
|
|
|
return false
|
|
|
|
else
|
|
|
|
local f=io.open(filename,"rb")
|
|
|
|
if(f) then
|
|
|
|
f:close()
|
|
|
|
print("[Error] Stop before overwrite regular file: " .. filename)
|
|
|
|
return true
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
if(args[1]=="install") then
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to install.")
|
|
|
|
return
|
|
|
|
else
|
2018-11-24 00:49:01 +08:00
|
|
|
if(not check_internet()) then return end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
print("Checking programs info...")
|
|
|
|
end
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
if(not check_db()) then return end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
local to_install={}
|
|
|
|
for i=2,#args,1 do
|
|
|
|
to_install[args[i]]=true
|
|
|
|
end
|
|
|
|
|
|
|
|
local newly_added=0
|
|
|
|
while true do
|
|
|
|
local to_add={}
|
|
|
|
for this_lib in pairs(to_install) do
|
|
|
|
if(not db[this_lib]) then
|
2018-11-21 12:08:25 +08:00
|
|
|
print("Library '" .. this_lib .. "' not found.")
|
2018-12-07 13:38:30 +08:00
|
|
|
local maybe_this=miss_suggestion(this_lib,db)
|
|
|
|
if(maybe_this) then
|
2018-12-07 20:08:56 +08:00
|
|
|
print("You might want library '" .. maybe_this .. "'.")
|
2018-12-07 13:38:30 +08:00
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
return
|
|
|
|
else
|
|
|
|
if(db[this_lib].requires) then
|
|
|
|
for idx,this_req in ipairs(db[this_lib].requires) do
|
|
|
|
if(not to_install[this_req] and not to_add[this_req]) then
|
|
|
|
newly_added=newly_added+1
|
|
|
|
to_add[this_req]=true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
for this_lib in pairs(to_add) do
|
|
|
|
to_install[this_lib]=true
|
|
|
|
end
|
|
|
|
if(newly_added==0) then break
|
|
|
|
else
|
|
|
|
newly_added=0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
print("About to install the following libraries...")
|
|
|
|
local count_libs=0
|
|
|
|
local count_files=0
|
2018-11-21 12:24:10 +08:00
|
|
|
io.write("\t")
|
2018-12-31 01:02:46 +08:00
|
|
|
for this_lib in pairsKey(to_install) do
|
2018-11-21 11:55:45 +08:00
|
|
|
io.write(this_lib .. " ")
|
|
|
|
count_libs=count_libs+1
|
2018-11-24 16:16:54 +08:00
|
|
|
for k in pairs(db[this_lib].files) do
|
2018-11-21 11:55:45 +08:00
|
|
|
count_files=count_files+1
|
|
|
|
end
|
|
|
|
end
|
2018-11-21 12:24:10 +08:00
|
|
|
print("\n" .. count_libs .. " libraries will be installed. " .. count_files .. " files will be downloaded.")
|
2018-11-21 11:55:45 +08:00
|
|
|
|
2018-11-30 17:48:05 +08:00
|
|
|
local warn_libs_unofficial={}
|
|
|
|
for this_lib in pairs(to_install) do
|
|
|
|
if(not IsOfficial(db[this_lib])) then
|
|
|
|
table.insert(warn_libs_unofficial,this_lib)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if(next(warn_libs_unofficial)) then
|
|
|
|
print("[WARN] The following libraries are unofficial. Install at your own risk.")
|
|
|
|
print("\t" .. table.concat(warn_libs_unofficial," "))
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Third-Party programs or unofficial programs may have license.
|
|
|
|
print("Checking License...")
|
|
|
|
local accepted_license={}
|
|
|
|
local refused_license={}
|
|
|
|
if(options["accept-license"]) then
|
|
|
|
if(type(options["accept-license"])=="boolean") then
|
|
|
|
accepted_license["__ALL__"]=true
|
|
|
|
else
|
|
|
|
local next_license=string.gmatch(options["accept-license"] .. ',',"[A-Za-z0-9]+,")
|
|
|
|
while true do
|
|
|
|
local this_license=next_license()
|
|
|
|
if(not this_license) then break end
|
|
|
|
this_license=string.lower(string.gsub(this_license,',',''))
|
|
|
|
accepted_license[this_license]=true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if(options["refuse-license"]) then
|
|
|
|
if(type(options["refuse-license"])=="boolean") then
|
|
|
|
refused_license["__ALL__"]=true
|
|
|
|
else
|
|
|
|
local next_license=string.gmatch(options["refuse-license"] .. ',',"[A-Za-z0-9]+,")
|
|
|
|
while true do
|
|
|
|
local this_license=next_license()
|
|
|
|
if(not this_license) then break end
|
|
|
|
this_license=string.lower(string.gsub(this_license,',',''))
|
|
|
|
refused_license[this_license]=true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
for this_lib in pairs(to_install) do
|
|
|
|
if(not IsOfficial(db[this_lib]) and db[this_lib].license) then
|
|
|
|
if(refused_license["__ALL__"] or refused_license[string.lower(db[this_lib].license.name)]) then
|
|
|
|
print("[License Refused] License " .. db[this_lib].license.name .. " for library " .. this_lib .. " is refused.")
|
|
|
|
return
|
|
|
|
elseif(accepted_license["__ALL__"] or accepted_license[string.lower(db[this_lib].license.name)]) then
|
|
|
|
print("Accepted license " .. db[this_lib].license.name .. " for library " .. this_lib)
|
|
|
|
else
|
|
|
|
-- Download the license and show it to user.
|
|
|
|
print("Downloading license " .. db[this_lib].license.name .. " for library " .. this_lib .. " from: " .. db[this_lib].license.url)
|
2018-12-03 15:24:35 +08:00
|
|
|
local ok,result=download(db[this_lib].license.url)
|
|
|
|
if(not ok) then
|
2019-01-07 19:34:59 +08:00
|
|
|
print("[Download Failed] Failed to download license.")
|
2018-11-30 17:48:05 +08:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local temp_name=os.tmpname()
|
|
|
|
local f=io.open(temp_name,"w")
|
|
|
|
f:write("----------Grab----------\nYou have to agree with this license for library " .. this_lib .. "\n------------------------\n\n")
|
|
|
|
f:write(result)
|
|
|
|
f:close()
|
|
|
|
|
|
|
|
local confirmed=false
|
|
|
|
while not confirmed do
|
|
|
|
os.execute("less " .. temp_name)
|
|
|
|
print("Do you agree with that license?")
|
|
|
|
print("(Y) - Yes. (N) - No. (A) - View it again.")
|
|
|
|
while true do
|
|
|
|
local x=io.read()
|
|
|
|
if(x~=nil) then
|
|
|
|
if(x=='y' or x=='Y') then
|
|
|
|
confirmed=1
|
|
|
|
break
|
|
|
|
elseif(x=='n' or x=='N') then
|
|
|
|
confirmed=2
|
|
|
|
break
|
|
|
|
elseif(x=='a' or x=='A') then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-07 20:08:56 +08:00
|
|
|
filesystem.remove(temp_name)
|
|
|
|
|
2018-11-30 17:48:05 +08:00
|
|
|
if(confirmed==2) then
|
|
|
|
print("[License Refused] License " .. db[this_lib].license.name .. " for library " .. this_lib .. " is refused by user.")
|
|
|
|
return
|
|
|
|
else
|
|
|
|
print("Accepted license " .. db[this_lib].license.name .. " for library " .. this_lib)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
print("Downloading...")
|
2018-11-30 11:10:08 +08:00
|
|
|
local count_byte=0
|
2018-11-21 12:24:10 +08:00
|
|
|
local id_installing=0
|
2018-12-07 20:08:56 +08:00
|
|
|
local time_before=computer.uptime()
|
2018-11-21 11:55:45 +08:00
|
|
|
for this_lib in pairs(to_install) do
|
2018-11-24 16:04:29 +08:00
|
|
|
for k,v in pairs(db[this_lib].files) do
|
2018-11-21 12:24:10 +08:00
|
|
|
id_installing=id_installing+1
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
local toDownload
|
|
|
|
if(type(k)=="number" and type(v)=="string") then
|
|
|
|
toDownload=v
|
|
|
|
elseif(type(k)=="string") then
|
|
|
|
toDownload=k
|
|
|
|
else
|
|
|
|
print("Invalid programs info: key type: " .. type(k) .. ". value type: " .. type(v))
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
io.write("[" .. id_installing .. "/" .. count_files .. "] Downloading " .. toDownload .. " for " .. this_lib .. "... ")
|
2018-11-26 01:33:41 +08:00
|
|
|
local this_url
|
|
|
|
if(db[this_lib].proxy) then
|
|
|
|
this_url=string.gsub(
|
|
|
|
string.gsub(
|
|
|
|
string.gsub(
|
|
|
|
db[this_lib].proxy,
|
|
|
|
"__repo__",
|
|
|
|
db[this_lib].repo or "Kiritow/OpenComputerScripts"
|
|
|
|
),
|
|
|
|
"__branch__",
|
|
|
|
db[this_lib].branch or "master"
|
|
|
|
),
|
|
|
|
"__file__",
|
|
|
|
toDownload
|
|
|
|
)
|
|
|
|
else
|
|
|
|
this_url=UrlGenerator(db[this_lib].repo or "Kiritow/OpenComputerScripts",db[this_lib].branch or "master",toDownload)
|
|
|
|
end
|
2018-12-03 15:24:35 +08:00
|
|
|
local ok,result=download(this_url)
|
2018-11-21 11:55:45 +08:00
|
|
|
if(not ok) then
|
|
|
|
print("[Download Failed] " .. result)
|
2018-11-21 12:08:25 +08:00
|
|
|
return
|
2018-11-21 11:55:45 +08:00
|
|
|
else
|
2018-11-30 11:10:08 +08:00
|
|
|
count_byte=count_byte+string.len(result)
|
2018-12-07 20:08:56 +08:00
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
if(type(v)=="string") then
|
2018-12-07 20:08:56 +08:00
|
|
|
local toSave
|
|
|
|
if(v=="__installer__") then
|
|
|
|
toSave=os.tmpname()
|
|
|
|
to_install[this_lib]=toSave
|
|
|
|
else
|
|
|
|
toSave=v
|
|
|
|
end
|
|
|
|
|
2019-01-07 19:34:59 +08:00
|
|
|
local ok,fname=try_resolve_path(k,toSave)
|
2018-12-07 20:08:56 +08:00
|
|
|
if(not ok) then
|
|
|
|
print("[Error] " .. fname)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2019-01-07 20:18:34 +08:00
|
|
|
if(will_overwrite(fname)) then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-12-08 10:57:05 +08:00
|
|
|
local f=io.open(fname,"wb")
|
2018-12-07 20:08:56 +08:00
|
|
|
if(not f) then
|
|
|
|
print("[Error] Failed to open file " .. fname .. " for writing.")
|
2018-11-21 11:55:45 +08:00
|
|
|
return
|
|
|
|
else
|
2018-12-07 20:08:56 +08:00
|
|
|
local ok,err=f:write(result)
|
2018-11-21 11:55:45 +08:00
|
|
|
f:close()
|
2018-12-07 20:08:56 +08:00
|
|
|
if(not ok) then
|
|
|
|
print("[Error] Failed while writing to file: " .. fname .. ": " .. err)
|
|
|
|
return
|
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
|
|
|
elseif(type(v)=="table") then
|
2018-12-07 20:08:56 +08:00
|
|
|
local done=false
|
|
|
|
|
|
|
|
for idx,this_name in ipairs(v) do
|
2019-01-07 19:34:59 +08:00
|
|
|
local ok,fname=try_resolve_path(k,this_name)
|
2018-12-07 20:08:56 +08:00
|
|
|
if(ok) then
|
2019-01-07 20:18:34 +08:00
|
|
|
if(will_overwrite(fname)) then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-12-08 10:57:05 +08:00
|
|
|
local f=io.open(fname,"wb")
|
2018-12-07 20:08:56 +08:00
|
|
|
if(f) then
|
|
|
|
local ok,err=f:write(result)
|
|
|
|
f:close()
|
|
|
|
if(not ok) then
|
|
|
|
print("[Error] Failed while writing to file: " .. fname .. ": " .. err)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
done=true
|
|
|
|
break
|
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
|
|
|
end
|
2018-12-07 20:08:56 +08:00
|
|
|
|
|
|
|
if(not done) then
|
|
|
|
print("[Error] Unable to save file: " .. toDownload)
|
2018-12-03 14:16:35 +08:00
|
|
|
return
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
2018-12-07 20:08:56 +08:00
|
|
|
else
|
|
|
|
print("[Error] Invalid program info value type: " .. type(v))
|
|
|
|
return
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
2018-12-07 20:08:56 +08:00
|
|
|
|
|
|
|
-- [OK]
|
|
|
|
print("[" .. getshowbyte(string.len(result)) .. "]")
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-12-07 20:08:56 +08:00
|
|
|
local time_diff=computer.uptime()-time_before
|
|
|
|
print("Fetched " .. count_files .. " files ("
|
|
|
|
.. getshowbyte(count_byte) .. ") in "
|
|
|
|
.. getshowtime(time_diff)
|
|
|
|
.. " (" .. getshowspeed(count_byte/time_diff) .. ")"
|
|
|
|
)
|
2018-11-30 17:48:05 +08:00
|
|
|
if(not options["skip-install"]) then
|
2018-11-26 00:49:18 +08:00
|
|
|
print("Installing...")
|
2018-12-08 10:57:05 +08:00
|
|
|
local has_installed={}
|
|
|
|
local recursion_detect={}
|
2018-12-31 02:06:15 +08:00
|
|
|
local function do_install_dfs(this_lib)
|
2018-12-08 10:57:05 +08:00
|
|
|
if(recursion_detect[this_lib] or has_installed[this_lib]) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
recursion_detect[this_lib]=true
|
|
|
|
|
|
|
|
if(db[this_lib].requires) then
|
2018-12-31 02:06:15 +08:00
|
|
|
for idx,req_lib in ipairs(db[this_lib].requires) do
|
2018-12-08 10:57:05 +08:00
|
|
|
if(not do_install_dfs(req_lib)) then -- Deeper Failure
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-07 20:08:56 +08:00
|
|
|
local this_installer
|
2018-12-08 10:57:05 +08:00
|
|
|
if(type(to_install[this_lib])=="string") then
|
|
|
|
this_installer=to_install[this_lib]
|
2018-12-07 20:08:56 +08:00
|
|
|
elseif(db[this_lib].installer) then
|
2019-01-07 19:34:59 +08:00
|
|
|
print("[WARN] From Grab v2.4.8, option `installer` is deprecated. Use __installer__ instead.")
|
2018-12-07 20:08:56 +08:00
|
|
|
this_installer=db[this_lib].installer
|
2018-12-08 10:57:05 +08:00
|
|
|
else
|
|
|
|
-- No Installer: Mark as installed.
|
|
|
|
has_installed[this_lib]=true
|
|
|
|
recursion_detect[this_lib]=nil
|
|
|
|
return true
|
2018-12-07 20:08:56 +08:00
|
|
|
end
|
|
|
|
|
2018-12-08 10:57:05 +08:00
|
|
|
print("Running installer for " .. this_lib .. "...")
|
|
|
|
local fn,err=loadfile(this_installer)
|
|
|
|
if(not fn) then
|
|
|
|
print("[Installer Error]: " .. err)
|
|
|
|
else
|
|
|
|
local ok,xerr=pcall(fn)
|
|
|
|
if(not ok) then
|
|
|
|
print("[Installer Error]: " .. xerr)
|
|
|
|
elseif(type(xerr)=="function") then
|
|
|
|
if(not pcall(xerr,grab_version_info)) then
|
2018-11-26 00:49:18 +08:00
|
|
|
print("[Installer Error]: " .. xerr)
|
2018-12-07 20:08:56 +08:00
|
|
|
else
|
2018-12-08 10:57:05 +08:00
|
|
|
has_installed[this_lib]=true
|
2018-12-07 20:08:56 +08:00
|
|
|
done=true
|
2018-11-26 00:49:18 +08:00
|
|
|
end
|
2018-12-08 10:57:05 +08:00
|
|
|
else
|
|
|
|
print("[Warn]: From Grab v2.4.6, installers should return functions.")
|
|
|
|
done=true
|
2018-11-24 16:16:54 +08:00
|
|
|
end
|
2018-12-08 10:57:05 +08:00
|
|
|
end
|
2018-12-07 20:08:56 +08:00
|
|
|
|
2018-12-08 10:57:05 +08:00
|
|
|
if(type(this_value)=="string") then -- This might be skipped?
|
|
|
|
filesystem.remove(this_installer)
|
|
|
|
end
|
2018-12-07 20:08:56 +08:00
|
|
|
|
2018-12-08 10:57:05 +08:00
|
|
|
recursion_detect[this_lib]=nil
|
|
|
|
return done
|
|
|
|
end -- end of local function do_install_dfs(...)
|
|
|
|
|
|
|
|
for this_lib in pairs(to_install) do
|
|
|
|
if(not do_install_dfs(this_lib)) then
|
|
|
|
print("Failed to install some library. Installation aborted.")
|
|
|
|
return
|
2018-11-24 16:16:54 +08:00
|
|
|
end
|
2018-11-24 01:29:56 +08:00
|
|
|
end
|
2018-11-26 00:49:18 +08:00
|
|
|
else
|
2018-11-30 17:48:05 +08:00
|
|
|
print("Installation is skipped.")
|
2018-11-24 01:29:56 +08:00
|
|
|
end
|
2018-11-21 12:24:10 +08:00
|
|
|
print("Installed " .. count_libs .. " libraies with " .. count_files .. " files.")
|
2018-11-21 11:55:45 +08:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if(args[1]=="list") then
|
2018-11-24 01:29:56 +08:00
|
|
|
if(not check_db()) then return end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
print("Listing projects...")
|
2018-12-31 01:02:46 +08:00
|
|
|
for this_lib in pairsKey(db) do
|
2018-12-07 13:38:30 +08:00
|
|
|
if(not db[this_lib].hidden) then
|
|
|
|
print(this_lib)
|
|
|
|
end
|
2018-11-21 11:55:45 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-11-26 00:49:18 +08:00
|
|
|
if(args[1]=="search") then
|
|
|
|
if(not check_db()) then return end
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to search.")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
print("Libraries matches '" .. args[2] .. "' :")
|
2018-12-31 01:02:46 +08:00
|
|
|
for this_lib in pairsKey(db) do
|
2018-11-26 00:49:18 +08:00
|
|
|
if(string.match(this_lib,args[2])) then
|
|
|
|
print(this_lib)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-11-24 01:29:56 +08:00
|
|
|
if(args[1]=="show") then
|
|
|
|
if(not check_db()) then return end
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to show.")
|
2018-12-03 14:16:35 +08:00
|
|
|
return
|
2018-11-24 01:29:56 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
if(db[args[2]]) then
|
|
|
|
local this_info=db[args[2]]
|
|
|
|
print("Name: " .. args[2])
|
2018-11-30 17:48:05 +08:00
|
|
|
print("Title: " .. this_info.title)
|
|
|
|
if(this_info.deprecated) then print("Deprecated: Yes") end
|
|
|
|
if(IsOfficial(this_info)) then print("Type: Official")
|
|
|
|
else print("Type: Unofficial") end
|
2018-11-24 21:33:02 +08:00
|
|
|
print("Info: " .. this_info.info)
|
|
|
|
if(this_info.author) then print("Author: " .. this_info.author) end
|
|
|
|
if(this_info.contact) then print("Contact: " .. this_info.contact) end
|
2018-11-24 01:29:56 +08:00
|
|
|
|
|
|
|
local nFiles=0
|
2018-11-24 21:33:02 +08:00
|
|
|
for k,v in pairs(this_info.files) do nFiles=nFiles+1 end
|
2018-11-24 01:29:56 +08:00
|
|
|
print("Files: " .. nFiles)
|
2018-11-24 21:33:02 +08:00
|
|
|
|
|
|
|
if(this_info.precheck) then print("Precheck: Yes") end
|
|
|
|
if(this_info.installer) then print("Installer: Yes") end
|
2018-11-30 17:48:05 +08:00
|
|
|
if(this_info.proxy) then print("Proxy: Yes") end
|
2018-12-07 13:38:30 +08:00
|
|
|
if(this_info.hidden) then print("Hidden: Yes") end
|
2018-12-03 16:58:54 +08:00
|
|
|
if(this_info.provider) then print("Provider: " .. this_info.provider) end
|
2018-11-30 17:48:05 +08:00
|
|
|
|
|
|
|
if(this_info.license) then
|
|
|
|
print("License: " .. this_info.license.name)
|
|
|
|
end
|
2018-11-24 01:29:56 +08:00
|
|
|
else
|
|
|
|
print("Library " .. args[2] .. " not found.")
|
2018-12-07 13:38:30 +08:00
|
|
|
local maybe_this=miss_suggestion(this_lib,db)
|
|
|
|
if(maybe_this) then
|
2018-12-07 20:08:56 +08:00
|
|
|
print("You might want library '" .. maybe_this .. "'.")
|
2018-12-07 13:38:30 +08:00
|
|
|
end
|
2018-11-24 01:29:56 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2018-11-24 00:49:01 +08:00
|
|
|
if(args[1]=="download") then
|
|
|
|
if(#args<2) then
|
|
|
|
print("Nothing to download.")
|
|
|
|
return
|
|
|
|
else
|
|
|
|
if(not check_internet()) then return end
|
|
|
|
print("Collecting files...")
|
|
|
|
end
|
|
|
|
|
|
|
|
local files={}
|
|
|
|
for i=2,#args,1 do
|
|
|
|
table.insert(files,args[i])
|
|
|
|
end
|
|
|
|
|
|
|
|
for i=1,#files,1 do
|
|
|
|
io.write("[" .. i .. "/" .. #files .. "] Downloading " .. files[i] .. "...")
|
2018-12-03 15:24:35 +08:00
|
|
|
local ok,result=download(UrlGenerator("Kiritow/OpenComputerScripts","master",files[i]))
|
2018-11-24 00:49:01 +08:00
|
|
|
if(not ok) then
|
|
|
|
print("[Download Failed] " .. result)
|
|
|
|
return
|
|
|
|
else
|
|
|
|
local f,ferr=io.open(files[i],"w")
|
|
|
|
if(not f) then
|
|
|
|
print("[Write Failed] Unable to write. Error:" .. ferr)
|
|
|
|
else
|
|
|
|
f:write(result)
|
|
|
|
f:close()
|
|
|
|
print("[OK]")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-11-26 00:49:18 +08:00
|
|
|
|
|
|
|
return
|
2018-11-24 00:49:01 +08:00
|
|
|
end
|
|
|
|
|
2018-11-21 11:55:45 +08:00
|
|
|
-- reach here?
|
2018-11-24 00:49:01 +08:00
|
|
|
if(#args<1) then
|
|
|
|
show_usage()
|
|
|
|
else
|
|
|
|
print("Unknown command: " .. args[1])
|
2018-12-07 13:38:30 +08:00
|
|
|
local maybe_this=miss_suggestion(args[1],valid_command)
|
|
|
|
if(maybe_this) then
|
|
|
|
print("Do you mean '" .. maybe_this .. "' ?")
|
|
|
|
end
|
2018-11-24 00:49:01 +08:00
|
|
|
end
|