print("Tiny Package Manager (TPM)") print("Author: Kiritow") local component=require("component") local shell=require("shell") local serialization=require("serialization") local uuid=require("uuid") local filesystem=require("filesystem") local function showcmd(cmdstr,infostr) local old=component.gpu.setForeground(0xFFFF00) io.write(cmdstr) component.gpu.setForeground(0xFFFFFF) print(" " .. infostr) component.gpu.setForeground(old) end local function showerr(infostr) local old=component.gpu.setForeground(0xFF0000) print(infostr) component.gpu.setForeground(old) end local function cwrite(color,infostr) local old=component.gpu.setForeground(color) io.write(infostr) component.gpu.setForeground(old) end local function resolve_github(reponame,fileaddr,branch) if(branch==nil) then branch="master" end return "https://raw.githubusercontent.com/" .. reponame .. "/" .. branch .. "/" .. fileaddr end local function resolve_cngit(reponame,fileaddr,branch) return "http://kiritow.com:3000/" .. reponame .. "/raw/" .. branch .. "/" .. fileaddr end -- For Test Purpose only resolve_github=resolve_cngit local function beginWith(str,target) local a,b=string.find(str,target) if(a==nil) then return false else return (a==1) end end local args,ops=shell.parse(...) local argc=#args if(argc<1) then print("Usage:") showcmd("tpm install ", "Install package" .. "\n\tInstall via github: github+Author/Repo[/branch]. Branch is 'master' by default" .. "\n\tInstall via http: http+URL" ) showcmd("tpm remove ","Remove package") showcmd("tpm list [-in] []", "Search package" .. "\n\t-i Only search installed package" .. "\n\t-n Only search not installed package" ) showcmd("tpm update","Update software info") return end -- Hardware check if(component.internet==nil) then showerr("No internet device found. tpm requires internet card to function normally.") return end -- Functions local function doRealDownload(url) if(component.internet==nil) then error("The downloader requires an Internet card.") end 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 end --elseif(ret==false) --os.sleep(0.1) end local response_code=handle.response() local ans="" while true do local tmp=handle.read() if(tmp==nil) then break end ans=ans .. tmp end handle.close() return true,ans,response_code end function WriteStringToFile(StringValue,FileName,IsAppend) if(IsAppend==nil) then IsAppend=false end local handle,err if(IsAppend) then handle,err=io.open(FileName,"a") else handle,err=io.open(FileName,"w") end if(handle==nil) then return false,err end handle:write(StringValue) handle:close() return true end -- TPM Main Programs -- Installer local function tpm_set_installed_list(tb) local f=io.open("/etc/tpm/list.bin","w") if(f==nil) then return false,"Unable to open file" end local content=serialization.serialize(tb) f:write(content) f:close() return true end local function tpm_get_installed_list() local f=io.open("/etc/tpm/list.bin","r") if(f==nil) then f=io.open("/etc/tpm/list.bin","w") if(f==nil) then return false,"Unable to open file" else f:close() tpm_set_installed_list({}) return true,{} end end local content=f:read("a") f:close() return true,serialization.unserialize(content) end local function tpm_is_installed(libname) local flg,tb=tpm_get_installed_list() if(not flg) then print("[tpm error] " .. tb) return false else for k,v in pairs(tb) do if(v.libname==libname) then return true end end return false end end local function tpm_mainfest_loader(data) local f,err=load("return " .. data,"Mainfest","t",{}) if(f==nil) then return false,err end local tb=f() -- Check returned table if(tb.libname==nil or tb.name==nil or tb.files==nil) then return false else return true,tb end end local function tpm_mainfest_show(mtb) print("Library Name: " .. mtb.name) print("libname: " .. mtb.libname) print("Info: " .. (mtb.info or "Not provided")) print("Author: " .. (mtb.author or "Not provided")) print("Version: " .. (mtb.version or "Not provided")) print("Setup: " .. (mtb.setup or "Not provided")) print("Uninst: " .. (mtb.uninst or "Not provided")) print("Required Directories: ") if(mtb.dirs==nil) then print("None") else for k,v in pairs(mtb.dirs) do print(v) end end print("Files:") for k,v in pairs(mtb.files) do print(k," -> ",v) end print("Dependency: ") if(mtb.depends==nil) then print("None") else for k,v in pairs(mtb.depends) do print(v) end end end local function tpm_install_github_real(repo,branch) local config_url=resolve_github(repo,"mainfest.txt",branch) local ret,data,code=doRealDownload(config_url) if(not ret) then return false,"Failed to download mainfest from repo " .. repo .. ". Error: " .. data end if(code~=200) then return false,"Got response code " .. code .. " while downloading mainfest from repo " .. repo end local flg,tb=tpm_mainfest_loader(data) if(not flg) then return false,"Invalid mainfest in repo " .. repo .. " Error: " .. tb end -- Debug tpm_mainfest_show(tb) -- Check if(tpm_is_installed(tb.libname)) then print("Package " .. tb.libname .. " is already installed.") return true end -- Depends if(tb.depends~=nil) then print("Analyzing dependency...") local dependsz=#tb.depends local cnt_now=1 for k,v in pairs(tb.depends) do print("[" .. cnt_now .. "/" .. dependsz .. "] Checking " .. v .. "...") local ret,msg=tpm_install(v) if(not ret) then return false,"An error occurs while analyzing dependency: " .. v .. ". Error: " .. msg end cnt_now=cnt_now+1 end end -- Make directories if(tb.dirs~=nil) then for k,v in pairs(tb.dirs) do print("Making directory " .. v) filesystem.makeDirectory(v) end end -- Download for k,v in pairs(tb.files) do io.write("Downloading file " .. k .. "...") local xret,xdata,xcode=doRealDownload(resolve_github(repo,k,branch)) if(not xret) then print("[Failed]") return false,"Failed to download " .. k elseif(xcode~=200) then print("[Failed]") return false,"Response code is " .. xcode .. " while downloading " .. k else local xret,xmsg=WriteStringToFile(xdata,v) if(not xret) then print("[Write Failed]") return false,"Failed to write to file " .. v .. " while downloading " .. k end -- OK here print("[OK]") end end -- Run Setup if(tb.setup~=nil) then print("Running setup...") local setup_file=tb.files[tb.setup] local setup_fn,err=loadfile(setup_file) if(setup_fn==nil) then print("Failed to start setup program. Error: " .. err) end local ret,msg=pcall(setup_fn) if(not ret) then printf("Failed to run setup program. Error: " .. msg) end end -- Update software list print("Updating software info...") local flg,installed_lst=tpm_get_installed_list() table.insert(installed_lst,tb) tpm_set_installed_list(installed_lst) print("Software info updated.") return true end local function tpm_install_github(url) local a=string.find(url,"/") if(a==nil) then return false,"Unknown format of github url" end local author=string.sub(url,1,a-1) local reponame='' local b=string.find(url,"/",a+1) -- Branch set to master by default local branch="master" if(b~=nil) then reponame=string.sub(url,a+1,b-1) branch=string.sub(url,b+1) else reponame=string.sub(url,a+1) end local repo_fullname=author .. "/" .. reponame print("Checking github repo " .. repo_fullname .. " at branch " .. branch .." ...") return tpm_install_github_real(repo_fullname,branch) end local function tpm_install_kgit_real(repo,branch) local config_url=resolve_cngit(repo,"mainfest.txt",branch) local ret,data,code=doRealDownload(config_url) if(not ret) then return false,"Failed to download mainfest from repo " .. repo .. ". Error: " .. data end if(code~=200) then return false,"Got response code " .. code .. " while downloading mainfest from repo " .. repo end local flg,tb=tpm_mainfest_loader(data) if(not flg) then return false,"Invalid mainfest in repo " .. repo .. " Error: " .. tb end -- Debug tpm_mainfest_show(tb) -- Check if(tpm_is_installed(tb.libname)) then print("Package " .. tb.libname .. " is already installed.") return true end -- Depends if(tb.depends~=nil) then print("Analyzing dependency...") local dependsz=#tb.depends local cnt_now=1 for k,v in pairs(tb.depends) do print("[" .. cnt_now .. "/" .. dependsz .. "] Checking " .. v .. "...") local ret,msg=tpm_install(v) if(not ret) then return false,"An error occurs while analyzing dependency: " .. v .. ". Error: " .. msg end cnt_now=cnt_now+1 end end -- Make directories if(tb.dirs~=nil) then for k,v in pairs(tb.dirs) do print("Making directory " .. v) filesystem.makeDirectory(v) end end -- Download for k,v in pairs(tb.files) do io.write("Downloading file " .. k .. "...") local xret,xdata,xcode=doRealDownload(resolve_cngit(repo,k,branch)) if(not xret) then print("[Failed]") return false,"Failed to download " .. k elseif(xcode~=200) then print("[Failed]") return false,"Response code is " .. xcode .. " while downloading " .. k else local xret,xmsg=WriteStringToFile(xdata,v) if(not xret) then print("[Write Failed]") return false,"Failed to write to file " .. v .. " while downloading " .. k end -- OK here print("[OK]") end end -- Run Setup if(tb.setup~=nil) then print("Running setup...") local setup_file=tb.files[tb.setup] local setup_fn,err=loadfile(setup_file) if(setup_fn==nil) then print("Failed to start setup program. Error: " .. err) end local ret,msg=pcall(setup_fn) if(not ret) then printf("Failed to run setup program. Error: " .. msg) end end -- Update software list print("Updating software info...") local flg,installed_lst=tpm_get_installed_list() table.insert(installed_lst,tb) tpm_set_installed_list(installed_lst) print("Software info updated.") return true end local function tpm_install_kgit(url) local a=string.find(url,"/") if(a==nil) then return false,"Unknown format of kgit url" end local author=string.sub(url,1,a-1) local reponame='' local b=string.find(url,"/",a+1) -- Branch set to master by default local branch="master" if(b~=nil) then reponame=string.sub(url,a+1,b-1) branch=string.sub(url,b+1) else reponame=string.sub(url,a+1) end local repo_fullname=author .. "/" .. reponame print("Checking kgit repo " .. repo_fullname .. " at branch " .. branch .." ...") return tpm_install_kgit_real(repo_fullname,branch) end local function tpm_install(install_url) print("tpm_install: ",install_url) if(beginWith(install_url,"github+")) then return tpm_install_github(string.sub(install_url,string.len("github+")+1)) elseif(beginWith(install_url,"kgit+")) then return tpm_install_kgit(string.sub(install_url,string.len("kgit+")+1)) elseif(beginWith(install_url,"http+")) then return tpm_install_http(string.sub(install_url,string.len("http+")+1)) else return tpm_install_package(install_url) end end if(args[1]=="install") then if(argc<2) then showerr("Package name or url is required.") return end for i=2,argc,1 do local ret,msg=tpm_install(args[i]) if(not ret) then print("Failed to install task " .. (i-1) .. ". Error: " .. msg) break end end end