Operating system for OpenComputers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
5.9KB

  1. local serial = require "serialization"
  2. local dl = require "download"
  3. local mtar = require "libmtar"
  4. local pkg = {}
  5. pkg.cfgPath = "/boot/cfg/pkg"
  6. pkg.sourcePath = pkg.cfgPath .. "/sources.cfg"
  7. pkg.installedPath = pkg.cfgPath .. "/installed.cfg"
  8. local w, lz16 = pcall(require,"liblz16")
  9. if not w then lz16 = nil end
  10. require "pkgfs"
  11. local function getSources()
  12. local f = io.open(pkg.sourcePath,"rb")
  13. if not f then return {main={path="https://oc.shadowkat.net/psychos/pkg",cache=true,name="main"}} end
  14. local c = f:read("*a")
  15. f:close()
  16. return serial.unserialize(c)
  17. end
  18. local function saveSources(t)
  19. fs.makeDirectory(pkg.cfgPath)
  20. local f = io.open(pkg.sourcePath,"wb")
  21. f:write(serial.serialize(t))
  22. f:close()
  23. end
  24. local function getInstalled()
  25. local f = io.open(pkg.installedPath,"rb")
  26. if not f then return {} end
  27. local c = f:read("*a")
  28. f:close()
  29. return serial.unserialize(c)
  30. end
  31. local function saveInstalled(t)
  32. fs.makeDirectory(pkg.cfgPath)
  33. local f = io.open(pkg.installedPath,"wb")
  34. if not f then return false end
  35. f:write(serial.serialize(t))
  36. f:close()
  37. end
  38. local function getRepoMeta(repo)
  39. if not getSources()[repo].cache or not fs.exists("/boot/cfg/pkg/repo-"..repo..".cfg") then
  40. dl(getSources()[repo].path.."/packages.cfg","/boot/cfg/pkg/repo-"..repo..".cfg")
  41. end
  42. local f = io.open("/boot/cfg/pkg/repo-"..repo..".cfg","rb")
  43. local rt = serial.unserialize(f:read("*a"))
  44. f:close()
  45. if not getSources()[repo].cache then
  46. fs.remove("/boot/cfg/pkg/repo-"..repo..".cfg")
  47. end
  48. return rt
  49. end
  50. local function activatePackage(path,compressed)
  51. require("pkgfs").add(path,compressed)
  52. end
  53. local function deactivatePackage(path)
  54. require("pkgfs").remove(path)
  55. end
  56. local function fnormalize(s)
  57. return table.concat(fs.segments(s),"/")
  58. end
  59. local function installSystemPackage(path,comp)
  60. local f
  61. if comp and lz16 then
  62. f = lz16.open(path,"rb")
  63. else
  64. f = io.open(path,"rb")
  65. end
  66. for fname, read, size in mtar.iter(f) do
  67. local opath = "/boot/"..fnormalize(fname)
  68. print(opath..": "..tostring(size))
  69. fs.makeDirectory(opath:match("(.+)/[^/]+"))
  70. local of = io.open(opath,"wb")
  71. if not of then error("unable to open "..opath.." for writing") end
  72. local tmp
  73. repeat
  74. tmp = read(2048) or ""
  75. of:write(tmp)
  76. until not tmp or tmp:len() < 1
  77. of:close()
  78. end
  79. return true
  80. end
  81. function pkg.addRepo(name,path,cache) -- string string boolean -- boolean -- Adds a repository, referred to as *name*, to the list of package sources, with the remote path *path*. If *cache* is set, keep a local copy of the repository index.
  82. local sources = getSources()
  83. sources[name] = {path=path,cache=cache,name=name}
  84. saveSources(sources)
  85. end
  86. function pkg.delRepo(name) -- string -- boolean -- Removes a repository from the list of repositories.
  87. local sources = getSources()
  88. sources[name] = nil
  89. saveSources(sources)
  90. end
  91. function pkg.update() -- Re-download cached repository indexes.
  92. for repo,meta in pairs(getSources()) do
  93. fs.remove("/boot/cfg/pkg/repo-"..repo..".cfg")
  94. if meta.cache then
  95. getRepoMeta(repo)
  96. end
  97. end
  98. end
  99. function pkg.list(filter,installed) -- string boolean -- -- Print a list of available packages matching *filter*, optionally filtering to only installed packages if *installed* is set.
  100. filter = filter or ""
  101. local pkglist = {}
  102. for repo,_ in pairs(getSources()) do
  103. for pkg,meta in pairs(getRepoMeta(repo)) do
  104. if pkg:find(filter) or (pkg.meta or ""):find(filter) then
  105. meta.repo = repo
  106. pkglist[pkg] = meta
  107. end
  108. end
  109. end
  110. for k,v in pairs(pkglist) do
  111. if v.system then io.write("\27[31m") end
  112. print(string.format("%s/%s: %s\27[0m\n %s\n Authors: %s",v.repo,k,v.name,v.description,v.authors))
  113. end
  114. end
  115. function pkg.getMeta(pkgname) -- string -- table -- Returns the metadata for a the package specified in *pkgname*.
  116. print("Finding package "..pkgname)
  117. for repo,info in pairs(getSources()) do
  118. local pkg = getRepoMeta(repo)[pkgname]
  119. if pkg then
  120. print("Package "..pkgname.." located in repo "..repo.." at "..info.path)
  121. pkg.repository = info
  122. return pkg
  123. end
  124. end
  125. end
  126. function pkg.get(pkgname,auto) -- string boolean -- boolean -- Downloads and mounts a package, identified as *pkgname,* onto the pkgfs. Setting *auto* will flag the package as automatically installed; this is used for dependencies.
  127. local pkginfo = pkg.getMeta(pkgname)
  128. if not pkginfo then error("unable to locate package "..pkgname) end
  129. pkginfo.manual = not auto
  130. fs.makeDirectory("/boot/pkg")
  131. for k,v in ipairs(pkginfo.dependencies or {}) do
  132. if not getInstalled()[v] then
  133. pkg.get(v,true)
  134. end
  135. end
  136. dl(pkginfo.repository.path.."/"..pkginfo.filename,"/boot/pkg/"..pkginfo.filename)
  137. local installed = getInstalled()
  138. installed[pkgname] = pkginfo
  139. saveInstalled(installed)
  140. if pkginfo.system then
  141. local rv = installSystemPackage("/boot/pkg/"..pkginfo.filename,pkginfo.compressed)
  142. fs.remove("/boot/pkg/"..pkginfo.filename)
  143. return rv
  144. end
  145. pcall(activatePackage,"/boot/pkg/"..pkginfo.filename,pkginfo.compressed)
  146. return true
  147. end
  148. function pkg.upgrade(force) -- boolean -- -- Upgrades all packages on the system to the current version stored in the relevant repository. If *force* is set, re-download all packages.
  149. pkg.update()
  150. fs.makeDirectory("/boot/pkg")
  151. local installed = getInstalled()
  152. for repo,info in pairs(getSources()) do
  153. for pkgname,pkginfo in pairs(getRepoMeta(repo)) do
  154. if installed[pkgname] and pkginfo.version ~= installed[pkgname].version or force then
  155. pkg.remove(pkgname)
  156. pkg.get(pkgname,pkginfo.auto)
  157. end
  158. end
  159. end
  160. end
  161. function pkg.remove(pkgname) -- string -- boolean -- Remove the package *pkgname* from the pkgfs and package directory.
  162. local installed = getInstalled()
  163. local pkginfo = installed[pkgname]
  164. if not pkginfo then return true end
  165. pcall(deactivatePackage,"/boot/pkg/"..pkginfo.filename)
  166. fs.remove("/boot/pkg/"..pkginfo.filename)
  167. if pkginfo.system then
  168. for k,v in pairs(pkginfo.files) do
  169. fs.remove(v)
  170. end
  171. end
  172. installed[pkgname] = nil
  173. saveInstalled(installed)
  174. return true
  175. end
  176. return pkg