· 5 years ago · Feb 26, 2020, 09:58 PM
1local json = load([===[
2local rA5U=20160728.17
3local Uc06="-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20160728.17 ]-"local lcBL={VERSION=rA5U,AUTHOR_NOTE=Uc06}local DHPxI=" "
4local dx={pretty=true,align_keys=false,indent=DHPxI}
5local RRuSHnxf={__tostring=function()return"JSON array"end}RRuSHnxf.__index=RRuSHnxf
6local mcYOuT={__tostring=function()return"JSON object"end}mcYOuT.__index=mcYOuT;function lcBL:newArray(iXxD6s)
7return setmetatable(iXxD6s or{},RRuSHnxf)end;function lcBL:newObject(oiY)return
8setmetatable(oiY or{},mcYOuT)end;local function Rr(FsYIVlkf)
9return
10type(FsYIVlkf)=='number'and FsYIVlkf or FsYIVlkf.N end
11local scRP0={__index=isNumber,__tostring=function(HLXS0Q_)return HLXS0Q_.S end,__unm=function(Kw)return
12Rr(Kw)end,__concat=function(nvaIsNv7,vDnoL55)
13return tostring(nvaIsNv7)..tostring(vDnoL55)end,__add=function(xlAK,zr1y)return Rr(xlAK)+Rr(zr1y)end,__sub=function(Hs,jk)return
14Rr(Hs)-Rr(jk)end,__mul=function(qzSFyIO,Z65)
15return Rr(qzSFyIO)*Rr(Z65)end,__div=function(umyCNfj,FT)return Rr(umyCNfj)/Rr(FT)end,__mod=function(YVLXYq,bJfct)return
16Rr(YVLXYq)%Rr(bJfct)end,__pow=function(OhuFpq_N,Dzg)return
17Rr(OhuFpq_N)^Rr(Dzg)end,__lt=function(_4O,C)return Rr(_4O)<Rr(C)end,__eq=function(fLI2zRe,_Fr2YU)return
18Rr(fLI2zRe)==Rr(_Fr2YU)end,__le=function(Xfn,U)return
19Rr(Xfn)<=Rr(U)end}
20function lcBL:asNumber(Ebsw)
21if getmetatable(Ebsw)==scRP0 then return Ebsw elseif
22type(Ebsw)=='table'and
23type(Ebsw.S)=='string'and type(Ebsw.N)=='number'then return setmetatable(Ebsw,scRP0)else
24local UlikV={S=tostring(Ebsw),N=tonumber(Ebsw)}return setmetatable(UlikV,scRP0)end end
25local function AI0R2TQ6(JtAjijkG)
26if JtAjijkG<=127 then return string.char(JtAjijkG)elseif JtAjijkG<=2047 then
27local s=math.floor(JtAjijkG/0x40)local YAtG_LV3=JtAjijkG- (0x40*s)return
28string.char(0xC0+s,0x80+YAtG_LV3)elseif JtAjijkG<=65535 then
29local LfEJbh_=math.floor(JtAjijkG/0x1000)local JD=JtAjijkG-0x1000*LfEJbh_
30local u=math.floor(JD/0x40)local pzDMZwG=JD-0x40*u;LfEJbh_=0xE0+LfEJbh_;u=0x80+u
31pzDMZwG=0x80+pzDMZwG
32if
33
34(LfEJbh_==0xE0 and u<0xA0)or
35(LfEJbh_==0xED and u>0x9F)or(LfEJbh_==0xF0 and u<0x90)or(LfEJbh_==0xF4 and u>0x8F)then return"?"else return string.char(LfEJbh_,u,pzDMZwG)end else local XPoQB=math.floor(JtAjijkG/0x40000)
36local XxJ=JtAjijkG-0x40000*XPoQB;local o5sms=math.floor(XxJ/0x1000)
37XxJ=XxJ-0x1000*o5sms;local JQi1jg=math.floor(XxJ/0x40)local wVzn=XxJ-0x40*JQi1jg
38return string.char(
390xF0+XPoQB,0x80+o5sms,0x80+JQi1jg,0x80+wVzn)end end
40function lcBL:onDecodeError(pE,RSjapQ,QJf,zC)if RSjapQ then
41if QJf then
42pE=string.format("%s at char %d of: %s",pE,QJf,RSjapQ)else pE=string.format("%s: %s",pE,RSjapQ)end end
43if zC~=nil then pE=pE.." ("..
44lcBL:encode(zC)..")"end
45if self.assert then self.assert(false,pE)else assert(false,pE)end end;lcBL.onDecodeOfNilError=lcBL.onDecodeError
46lcBL.onDecodeOfHTMLError=lcBL.onDecodeError
47function lcBL:onEncodeError(pfZ3SPy_,pDNa2ox6)
48if pDNa2ox6 ~=nil then pfZ3SPy_=pfZ3SPy_..
49" ("..lcBL:encode(pDNa2ox6)..")"end;if self.assert then self.assert(false,pfZ3SPy_)else
50assert(false,pfZ3SPy_)end end
51local function yA(Do6yo7nm,y06X3k,ivnJjrA,d3fMjkg)
52local el=y06X3k:match('^-?[1-9]%d*',ivnJjrA)or y06X3k:match("^-?0",ivnJjrA)if not el then
53Do6yo7nm:onDecodeError("expected number",y06X3k,ivnJjrA,d3fMjkg.etc)end
54local Wu_uIt=ivnJjrA+el:len()local w=y06X3k:match('^%.%d+',Wu_uIt)or""Wu_uIt=Wu_uIt+
55w:len()
56local sgeP=y06X3k:match('^[eE][-+]?%d+',Wu_uIt)or""Wu_uIt=Wu_uIt+sgeP:len()local CM=el..w..sgeP;if
57d3fMjkg.decodeNumbersAsObjects then return lcBL:asNumber(CM),Wu_uIt end
58if
59
60(
61d3fMjkg.decodeIntegerStringificationLength and(el:len()>=d3fMjkg.decodeIntegerStringificationLength or
62sgeP:len()>0))or
63(d3fMjkg.decodeDecimalStringificationLength and
64(
65w:len()>=d3fMjkg.decodeDecimalStringificationLength or sgeP:len()>0))then return CM,Wu_uIt end;local Qlmlet=tonumber(CM)if not Qlmlet then
66Do6yo7nm:onDecodeError("bad number",y06X3k,ivnJjrA,d3fMjkg.etc)end;return Qlmlet,Wu_uIt end
67local function XmVolesU(_,RkGFh6,hw18,nvCiFt7r)if RkGFh6:sub(hw18,hw18)~='"'then
68_:onDecodeError("expected string's opening quote",RkGFh6,hw18,nvCiFt7r.etc)end;local xSebv5Jc=hw18+1
69local mMp=RkGFh6:len()local rDtVf=""
70while xSebv5Jc<=mMp do local vj=RkGFh6:sub(xSebv5Jc,xSebv5Jc)if
71vj=='"'then return rDtVf,xSebv5Jc+1 end
72if vj~='\\'then rDtVf=rDtVf..vj;xSebv5Jc=
73xSebv5Jc+1 elseif RkGFh6:match('^\\b',xSebv5Jc)then rDtVf=rDtVf.."\b"xSebv5Jc=
74xSebv5Jc+2 elseif RkGFh6:match('^\\f',xSebv5Jc)then rDtVf=rDtVf.."\f"xSebv5Jc=
75xSebv5Jc+2 elseif RkGFh6:match('^\\n',xSebv5Jc)then rDtVf=rDtVf.."\n"xSebv5Jc=
76xSebv5Jc+2 elseif RkGFh6:match('^\\r',xSebv5Jc)then rDtVf=rDtVf.."\r"xSebv5Jc=
77xSebv5Jc+2 elseif RkGFh6:match('^\\t',xSebv5Jc)then rDtVf=rDtVf.."\t"xSebv5Jc=
78xSebv5Jc+2 else
79local z=RkGFh6:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])',xSebv5Jc)
80if z then xSebv5Jc=xSebv5Jc+6;local Zg=tonumber(z,16)
81if
82Zg>=0xD800 and Zg<=0xDBFF then
83local ykRppH=RkGFh6:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])',xSebv5Jc)
84if ykRppH then xSebv5Jc=xSebv5Jc+6;Zg=0x2400+ (Zg-0xD800)*0x400+
85tonumber(ykRppH,16)else end end;rDtVf=rDtVf..AI0R2TQ6(Zg)else rDtVf=rDtVf..
86RkGFh6:match('^\\(.)',xSebv5Jc)xSebv5Jc=xSebv5Jc+2 end end end
87_:onDecodeError("unclosed string",RkGFh6,hw18,nvCiFt7r.etc)end
88local function eZ0l3ch(WQ6,y36Aetn)local iPL3B4cr,GI2hz6SK=WQ6:find("^[ \n\r\t]+",y36Aetn)if GI2hz6SK then
89return GI2hz6SK+1 else return y36Aetn end end;local W_63_9
90local function h9dyA_4T(Oh,PG,n,O)if PG:sub(n,n)~='{'then
91Oh:onDecodeError("expected '{'",PG,n,O.etc)end;local N5UjTN=eZ0l3ch(PG,n+1)local qLH5=Oh.strictTypes and
92Oh:newObject{}or{}if
93PG:sub(N5UjTN,N5UjTN)=='}'then return qLH5,N5UjTN+1 end
94local tE=PG:len()
95while N5UjTN<=tE do local VcV0EuD,pX4gCR=XmVolesU(Oh,PG,N5UjTN,O)
96N5UjTN=eZ0l3ch(PG,pX4gCR)if PG:sub(N5UjTN,N5UjTN)~=':'then
97Oh:onDecodeError("expected colon",PG,N5UjTN,O.etc)end
98N5UjTN=eZ0l3ch(PG,N5UjTN+1)local gad4ZcL,pX4gCR=W_63_9(Oh,PG,N5UjTN,O)qLH5[VcV0EuD]=gad4ZcL
99N5UjTN=eZ0l3ch(PG,pX4gCR)local dk=PG:sub(N5UjTN,N5UjTN)
100if dk=='}'then return qLH5,N5UjTN+1 end;if PG:sub(N5UjTN,N5UjTN)~=','then
101Oh:onDecodeError("expected comma or '}'",PG,N5UjTN,O.etc)end
102N5UjTN=eZ0l3ch(PG,N5UjTN+1)end;Oh:onDecodeError("unclosed '{'",PG,n,O.etc)end
103local function oh(E,OO,y,cR6rJlAl)if OO:sub(y,y)~='['then
104E:onDecodeError("expected '['",OO,y,cR6rJlAl.etc)end;local M6ilzGJ=eZ0l3ch(OO,y+1)local iW6CD=E.strictTypes and
105E:newArray{}or{}if
106OO:sub(M6ilzGJ,M6ilzGJ)==']'then return iW6CD,M6ilzGJ+1 end
107local wZdg=1;local BaX=OO:len()
108while M6ilzGJ<=BaX do
109local SJsW11k,Ki1HJT=W_63_9(E,OO,M6ilzGJ,cR6rJlAl)iW6CD[wZdg]=SJsW11k;wZdg=wZdg+1;M6ilzGJ=eZ0l3ch(OO,Ki1HJT)
110local wjim8xCV=OO:sub(M6ilzGJ,M6ilzGJ)if wjim8xCV==']'then return iW6CD,M6ilzGJ+1 end;if
111OO:sub(M6ilzGJ,M6ilzGJ)~=','then
112E:onDecodeError("expected comma or '['",OO,M6ilzGJ,cR6rJlAl.etc)end
113M6ilzGJ=eZ0l3ch(OO,M6ilzGJ+1)end
114E:onDecodeError("unclosed '['",OO,y,cR6rJlAl.etc)end
115W_63_9=function(E,QLam,qTDt,v)qTDt=eZ0l3ch(QLam,qTDt)
116if qTDt>QLam:len()then E:onDecodeError("unexpected end of string",QLam,
117nil,v.etc)end
118if QLam:find('^"',qTDt)then return XmVolesU(E,QLam,qTDt,v.etc)elseif
119QLam:find('^[-0123456789 ]',qTDt)then return yA(E,QLam,qTDt,v)elseif QLam:find('^%{',qTDt)then
120return h9dyA_4T(E,QLam,qTDt,v)elseif QLam:find('^%[',qTDt)then return oh(E,QLam,qTDt,v)elseif QLam:find('^true',qTDt)then return true,
121qTDt+4 elseif QLam:find('^false',qTDt)then return false,qTDt+5 elseif
122QLam:find('^null',qTDt)then return nil,qTDt+4 else
123E:onDecodeError("can't parse JSON",QLam,qTDt,v.etc)end end
124function lcBL:decode(Ta,u,nArcvQl)if type(nArcvQl)~='table'then nArcvQl={}end;if u~=nil then
125nArcvQl.etc=u end;if
126type(self)~='table'or self.__index~=lcBL then
127lcBL:onDecodeError("JSON:decode must be called in method format",nil,nil,nArcvQl.etc)end
128if Ta==nil then
129self:onDecodeOfNilError(string.format("nil passed to JSON:decode()"),
130nil,nil,nArcvQl.etc)elseif type(Ta)~='string'then
131self:onDecodeError(string.format("expected string argument to JSON:decode(), got %s",type(Ta)),
132nil,nil,nArcvQl.etc)end;if Ta:match('^%s*$')then return nil end;if Ta:match('^%s*<')then
133self:onDecodeOfHTMLError(string.format("html passed to JSON:decode()"),Ta,
134nil,nArcvQl.etc)end
135if
136Ta:sub(1,1):byte()==0 or
137(Ta:len()>=2 and Ta:sub(2,2):byte()==0)then
138self:onDecodeError("JSON package groks only UTF-8, sorry",Ta,nil,nArcvQl.etc)end;if nArcvQl.decodeNumbersAsObjects==nil then
139nArcvQl.decodeNumbersAsObjects=self.decodeNumbersAsObjects end;if
140nArcvQl.decodeIntegerStringificationLength==nil then
141nArcvQl.decodeIntegerStringificationLength=self.decodeIntegerStringificationLength end;if
142nArcvQl.decodeDecimalStringificationLength==nil then
143nArcvQl.decodeDecimalStringificationLength=self.decodeDecimalStringificationLength end
144local h6Ub7U,Gm=pcall(W_63_9,self,Ta,1,nArcvQl)if h6Ub7U then return Gm else
145if self.assert then self.assert(false,Gm)else assert(false,Gm)end;return nil,Gm end end
146local function DZXGTh(YKA7cU)
147if YKA7cU=="\n"then return"\\n"elseif YKA7cU=="\r"then return"\\r"elseif YKA7cU=="\t"then return"\\t"elseif YKA7cU=="\b"then
148return"\\b"elseif YKA7cU=="\f"then return"\\f"elseif YKA7cU=='"'then return'\\"'elseif YKA7cU=='\\'then return'\\\\'else return
149string.format("\\u%04x",YKA7cU:byte())end end
150local Su9Koz='['..'"'..
151'%\\'..'%z'..'\001'..'-'..'\031'..']'local Uk7e=AI0R2TQ6(0x2028)local KwQCk_G=AI0R2TQ6(0x2029)
152local function ptZa(mCsewfX,yY)
153local Xf=mCsewfX:gsub(Su9Koz,DZXGTh)if yY.stringsAreUtf8 then
154Xf=Xf:gsub(Uk7e,'\\u2028'):gsub(KwQCk_G,'\\u2029')end;return'"'..Xf..'"'end
155local function PEqsd(UlFdiZ7v,U,wFeA)local JQgI={}local N={}local fs52REi=false;local PUNkgaiM
156for X in pairs(U)do
157if type(X)=='string'then
158table.insert(JQgI,X)elseif type(X)=='number'then table.insert(N,X)
159if X<=0 or X>=math.huge then
160fs52REi=true elseif not PUNkgaiM or X>PUNkgaiM then PUNkgaiM=X end else
161UlFdiZ7v:onEncodeError("can't encode table with a key of type "..type(X),wFeA)end end
162if#JQgI==0 and not fs52REi then
163if#N>0 then return nil,PUNkgaiM elseif
164tostring(U)=="JSON array"then return nil elseif tostring(U)=="JSON object"then return{}else return nil end end;table.sort(JQgI)local s6FbB
165if#N>0 then if UlFdiZ7v.noKeyConversion then
166UlFdiZ7v:onEncodeError("a table with both numeric and string keys could be an object or array; aborting",wFeA)end
167s6FbB={}for dc61,aguhyl in pairs(U)do s6FbB[dc61]=aguhyl end
168table.sort(N)
169for p,gOPDv in ipairs(N)do local aSdZU3=tostring(gOPDv)
170if s6FbB[aSdZU3]==nil then
171table.insert(JQgI,aSdZU3)s6FbB[aSdZU3]=U[gOPDv]else
172UlFdiZ7v:onEncodeError(
173"conflict converting table with mixed-type keys into a JSON object: key "..gOPDv.." exists both as a string and a number.",wFeA)end end end;return JQgI,nil,s6FbB end;local iSj
174function iSj(YKDL,oFyb6OLp,oGdh_mv,WjvvK,TASVwBgU,KjUncMB,XkT)
175if oFyb6OLp==nil or
176(not XkT and TASVwBgU and TASVwBgU.null and oFyb6OLp==
177TASVwBgU.null)then return'null'elseif
178type(oFyb6OLp)=='string'then return ptZa(oFyb6OLp,TASVwBgU)elseif type(oFyb6OLp)=='number'then
179if
180oFyb6OLp~=oFyb6OLp then return"null"elseif oFyb6OLp>=math.huge then return"1e+9999"elseif oFyb6OLp<=-math.huge then
181return"-1e+9999"else return tostring(oFyb6OLp)end elseif type(oFyb6OLp)=='boolean'then return tostring(oFyb6OLp)elseif type(oFyb6OLp)~=
182'table'then
183YKDL:onEncodeError("can't convert "..type(oFyb6OLp).." to JSON",WjvvK)elseif getmetatable(oFyb6OLp)==scRP0 then return tostring(oFyb6OLp)else
184local c3dr=oFyb6OLp;if type(TASVwBgU)~='table'then TASVwBgU={}end;if type(KjUncMB)~=
185'string'then KjUncMB=""end
186if oGdh_mv[c3dr]then
187YKDL:onEncodeError("table "..
188tostring(c3dr).." is a child of itself",WjvvK)else oGdh_mv[c3dr]=true end;local NGH;local tIc,MD2O,HQ=PEqsd(YKDL,c3dr,WjvvK)
189if MD2O then local cng={}for lE=1,MD2O do
190table.insert(cng,iSj(YKDL,c3dr[lE],oGdh_mv,WjvvK,TASVwBgU,KjUncMB))end
191if TASVwBgU.pretty then NGH="[ "..
192table.concat(cng,", ").." ]"else NGH="["..
193table.concat(cng,",").."]"end elseif tIc then local nI2F0id=HQ or c3dr
194if TASVwBgU.pretty then local N4aMD_P={}local pCi=0
195for lNOqUk8,YAnZNei in ipairs(tIc)do
196local h8YWR44E=iSj(YKDL,tostring(YAnZNei),oGdh_mv,WjvvK,TASVwBgU,KjUncMB,true)
197if TASVwBgU.align_keys then pCi=math.max(pCi,#h8YWR44E)end;table.insert(N4aMD_P,h8YWR44E)end
198local NzeoQJ=KjUncMB..tostring(TASVwBgU.indent or"")
199local AwGfFV=NzeoQJ..string.rep(" ",pCi)..
200(TASVwBgU.align_keys and" "or"")
201local wCRY="%s%"..string.format("%d",pCi).."s: %s"local d0uKSVw1={}
202for VF,fTrMe in ipairs(tIc)do
203local ypDndT8=iSj(YKDL,nI2F0id[fTrMe],oGdh_mv,WjvvK,TASVwBgU,AwGfFV)
204table.insert(d0uKSVw1,string.format(wCRY,NzeoQJ,N4aMD_P[VF],ypDndT8))end;NGH="{\n"..
205table.concat(d0uKSVw1,",\n").."\n"..KjUncMB.."}"else local MV65={}
206for Y3D66Ym9,q in
207ipairs(tIc)do
208local PhJ=iSj(YKDL,nI2F0id[q],oGdh_mv,WjvvK,TASVwBgU,KjUncMB)
209local h=iSj(YKDL,tostring(q),oGdh_mv,WjvvK,TASVwBgU,KjUncMB,true)
210table.insert(MV65,string.format("%s:%s",h,PhJ))end;NGH="{"..table.concat(MV65,",").."}"end else NGH="[]"end;oGdh_mv[c3dr]=false;return NGH end end
211function lcBL:encode(j2K,r8hgwQ,_6U)if type(self)~='table'or self.__index~=lcBL then
212lcBL:onEncodeError("JSON:encode must be called in method format",r8hgwQ)end;if
213type(_6U)~='table'then _6U={}end;return iSj(self,j2K,{},r8hgwQ,_6U)end
214function lcBL:encode_pretty(GLSzBQs,c,xg)if type(self)~='table'or self.__index~=lcBL then
215lcBL:onEncodeError("JSON:encode_pretty must be called in method format",c)end;if
216type(xg)~='table'then xg=dx end;return iSj(self,GLSzBQs,{},c,xg)end;function lcBL.__tostring()return"JSON encode/decode package"end
217lcBL.__index=lcBL
218function lcBL:new(Id2KoP_G)local Y2or={}if Id2KoP_G then
219for zN8ASHV5,iju in pairs(Id2KoP_G)do Y2or[zN8ASHV5]=iju end end
220return setmetatable(Y2or,lcBL)end;return lcBL:new()
221]===])()
222local semver = (function()
223 local _ = [[ Copyright (c) The python-semanticversion project
224 All rights reserved.
225
226 Redistribution and use in source and binary forms, with or without
227 modification, are permitted provided that the following conditions are met:
228
229 1. Redistributions of source code must retain the above copyright notice, this
230 list of conditions and the following disclaimer.
231 2. Redistributions in binary form must reproduce the above copyright notice,
232 this list of conditions and the following disclaimer in the documentation
233 and/or other materials provided with the distribution.
234
235 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
236 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
237 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
238 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
239 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
240 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
241 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
242 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
243 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
244 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
245 ]]
246 _ = [[ The use of the library is similar to the original one,
247 check the documentation here: https://python-semanticversion.readthedocs.io/en/latest/
248 ]]
249 local concat, insert, unpack
250 do
251 local _obj_0 = table
252 concat, insert, unpack = _obj_0.concat, _obj_0.insert, _obj_0.unpack
253 end
254 local toInt
255 toInt = function(value)
256 do
257 local tn = tonumber(value)
258 if tn then
259 return tn, true
260 else
261 return value, false
262 end
263 end
264 end
265 local hasLeadingZero
266 hasLeadingZero = function(value)
267 return value and value[1] == '0' and tonumber(value and value ~= '0')
268 end
269 local baseCmp
270 baseCmp = function(x, y)
271 if x == y then
272 return 0
273 end
274 if x > y then
275 return 1
276 end
277 if x < y then
278 return -1
279 end
280 end
281 local identifierCmp
282 identifierCmp = function(a, b)
283 local aCmp, aInt = toInt(a)
284 local bCmp, bInt = toInt(b)
285 if aInt and bInt then
286 return baseCmp(aCmp, bCmp)
287 elseif aInt then
288 return -1
289 elseif bInt then
290 return 1
291 else
292 return baseCmp(aCmp, bCmp)
293 end
294 end
295 local identifierListCmp
296 identifierListCmp = function(a, b)
297 local identifierPairs
298 do
299 local _tbl_0 = { }
300 for i = 1, #a do
301 if b[i] then
302 _tbl_0[a[i]] = b[i]
303 end
304 end
305 identifierPairs = _tbl_0
306 end
307 for idA, idB in pairs(identifierPairs) do
308 local cmpRes = identifierCmp(idA, idB)
309 if cmpRes ~= 0 then
310 return cmpRes
311 end
312 end
313 return baseCmp(#a, #b)
314 end
315 local Version
316 do
317 local _class_0
318 local _base_0 = {
319 _coerce = function(self, value, allowNil)
320 if allowNil == nil then
321 allowNil = false
322 end
323 if value == nil and allowNil then
324 return value
325 end
326 return tonumber(value)
327 end,
328 next_major = function(self)
329 if self.prerelease and self.minor == 0 and self.patch == 0 then
330 return Version(concat((function()
331 local _accum_0 = { }
332 local _len_0 = 1
333 local _list_0 = {
334 self.major,
335 self.minor,
336 self.patch
337 }
338 for _index_0 = 1, #_list_0 do
339 local x = _list_0[_index_0]
340 _accum_0[_len_0] = tostring(x)
341 _len_0 = _len_0 + 1
342 end
343 return _accum_0
344 end)(), '.'))
345 else
346 return Version(concat((function()
347 local _accum_0 = { }
348 local _len_0 = 1
349 local _list_0 = {
350 self.major + 1,
351 0,
352 0
353 }
354 for _index_0 = 1, #_list_0 do
355 local x = _list_0[_index_0]
356 _accum_0[_len_0] = tostring(x)
357 _len_0 = _len_0 + 1
358 end
359 return _accum_0
360 end)(), '.'))
361 end
362 end,
363 next_minor = function(self)
364 if not (self.minor) then
365 error("Partial version doesn't contain the minor component!")
366 end
367 if self.prerelease and self.patch == 0 then
368 return Version(concat((function()
369 local _accum_0 = { }
370 local _len_0 = 1
371 local _list_0 = {
372 self.major,
373 self.minor,
374 self.patch
375 }
376 for _index_0 = 1, #_list_0 do
377 local x = _list_0[_index_0]
378 _accum_0[_len_0] = tostring(x)
379 _len_0 = _len_0 + 1
380 end
381 return _accum_0
382 end)(), '.'))
383 else
384 return Version(concat((function()
385 local _accum_0 = { }
386 local _len_0 = 1
387 local _list_0 = {
388 self.major,
389 self.minor + 1,
390 0
391 }
392 for _index_0 = 1, #_list_0 do
393 local x = _list_0[_index_0]
394 _accum_0[_len_0] = tostring(x)
395 _len_0 = _len_0 + 1
396 end
397 return _accum_0
398 end)(), '.'))
399 end
400 end,
401 next_patch = function(self)
402 if not (self.patch) then
403 error("Partial version doesn't contain the patch component!")
404 end
405 if self.prerelease then
406 return Version(concat((function()
407 local _accum_0 = { }
408 local _len_0 = 1
409 local _list_0 = {
410 self.major,
411 self.minor,
412 self.patch
413 }
414 for _index_0 = 1, #_list_0 do
415 local x = _list_0[_index_0]
416 _accum_0[_len_0] = tostring(x)
417 _len_0 = _len_0 + 1
418 end
419 return _accum_0
420 end)(), '.'))
421 else
422 return Version(concat((function()
423 local _accum_0 = { }
424 local _len_0 = 1
425 local _list_0 = {
426 self.major,
427 self.minor,
428 self.patch + 1
429 }
430 for _index_0 = 1, #_list_0 do
431 local x = _list_0[_index_0]
432 _accum_0[_len_0] = tostring(x)
433 _len_0 = _len_0 + 1
434 end
435 return _accum_0
436 end)(), '.'))
437 end
438 end,
439 coerce = function(self, versionString, partial)
440 if partial == nil then
441 partial = false
442 end
443 local baseRe
444 baseRe = function(s)
445 local mjr, rmn = s:match('^(%d+)(.*)$')
446 if not (mjr) then
447 return nil
448 end
449 local t = mjr
450 local mnr, r = rmn:match('^%.(%d+)(.*)$')
451 if mnr then
452 rmn = r
453 t = t .. ('.' .. mnr)
454 end
455 local pch
456 pch, r = rmn:match('^%.(%d+)(.*)$')
457 if pch then
458 rmn = r
459 t = t .. ('.' .. pch)
460 end
461 return s, t
462 end
463 local match, matchEnd = baseRe(versionString)
464 if not (match) then
465 error("Version string lacks a numerical component: " .. tostring(versionString))
466 end
467 local version = versionString:sub(1, #matchEnd)
468 if not partial then
469 while ({
470 version:gsub('.', '')
471 })[2] < 2 do
472 version = version .. '.0'
473 end
474 end
475 if #matchEnd == #versionString then
476 return Version(version, partial)
477 end
478 local rest = versionString:sub(#matchEnd + 1)
479 rest = rest:gsub('[^a-zA-Z0-9+.-]', '-')
480 local prerelease, build = nil, nil
481 if rest:sub(1, 1) == '+' then
482 prerelease = ''
483 build = rest:sub(2)
484 elseif rest:sub(1, 1) == '.' then
485 prerelease = ''
486 build = rest:sub(2)
487 elseif rest:sub(1, 1) == '-' then
488 rest = rest:sub(2)
489 do
490 local p1 = rest:find('+')
491 if p1 then
492 prerelease, build = rest:sub(1, p1 - 1), rest:sub(p1 + 1, -1)
493 else
494 prerelease, build = rest, ''
495 end
496 end
497 else
498 do
499 local p1 = rest:find('+')
500 if p1 then
501 prerelease, build = rest:sub(1, p1 - 1), rest:sub(p1 + 1, -1)
502 else
503 prerelease, build = rest, ''
504 end
505 end
506 end
507 build = build:gsub('+', '.')
508 if prerelease and prerelease ~= '' then
509 version = version .. ('-' .. prerelease)
510 end
511 if build and build ~= '' then
512 version = version .. ('+' .. build)
513 end
514 return self.__class(version, partial)
515 end,
516 parse = function(self, versionString, partial, coerce)
517 if partial == nil then
518 partial = false
519 end
520 if coerce == nil then
521 coerce = false
522 end
523 if not versionString or type(versionString) ~= 'string' or versionString == '' then
524 error("Invalid empty version string: " .. tostring(tostring(versionString)))
525 end
526 local versionRe
527 if partial then
528 versionRe = self.__class.partialVersionRe
529 else
530 versionRe = self.__class.versionRe
531 end
532 local major, minor, patch, prerelease, build = versionRe(self.__class, versionString)
533 if not major then
534 error("Invalid version string: " .. tostring(versionString))
535 end
536 if hasLeadingZero(major) then
537 error("Invalid leading zero in major: " .. tostring(versionString))
538 end
539 if hasLeadingZero(minor) then
540 error("Invalid leading zero in minor: " .. tostring(versionString))
541 end
542 if hasLeadingZero(patch) then
543 error("Invalid leading zero in patch: " .. tostring(versionString))
544 end
545 major = tonumber(major)
546 minor = self:_coerce(minor, partial)
547 patch = self:_coerce(patch, partial)
548 if prerelease == nil then
549 if partial and build == nil then
550 return {
551 major,
552 minor,
553 patch,
554 nil,
555 nil
556 }
557 else
558 prerelease = { }
559 end
560 elseif prerelease == '' then
561 prerelease = { }
562 else
563 do
564 local _accum_0 = { }
565 local _len_0 = 1
566 for x in prerelease:gmatch('[^.]+') do
567 _accum_0[_len_0] = x
568 _len_0 = _len_0 + 1
569 end
570 prerelease = _accum_0
571 end
572 self:_validateIdentifiers(prerelease, false)
573 end
574 if build == nil then
575 if partial then
576 build = nil
577 else
578 build = { }
579 end
580 elseif build == '' then
581 build = { }
582 else
583 do
584 local _accum_0 = { }
585 local _len_0 = 1
586 for x in build:gmatch('[^.]+') do
587 _accum_0[_len_0] = x
588 _len_0 = _len_0 + 1
589 end
590 build = _accum_0
591 end
592 self:_validateIdentifiers(build, true)
593 end
594 return {
595 major,
596 minor,
597 patch,
598 prerelease,
599 build
600 }
601 end,
602 _validateIdentifiers = function(self, identifiers, allowLeadingZeroes)
603 if allowLeadingZeroes == nil then
604 allowLeadingZeroes = false
605 end
606 for _index_0 = 1, #identifiers do
607 local item = identifiers[_index_0]
608 if not item then
609 error("Invalid empty identifier " .. tostring(item) .. " in " .. tostring(concat(identifiers, '.')))
610 end
611 if item:sub(1, 1) == '0' and tonumber(item) and item ~= '0' and not allowLeadingZeroes then
612 error("Invalid leading zero in identifier " .. tostring(item))
613 end
614 end
615 end,
616 __pairs = function(self)
617 return pairs({
618 self.major,
619 self.minor,
620 self.patch,
621 self.prerelease,
622 self.build
623 })
624 end,
625 __ipairs = function(self)
626 return ipairs({
627 self.major,
628 self.minor,
629 self.patch,
630 self.prerelease,
631 self.build
632 })
633 end,
634 __tostring = function(self)
635 local version = tostring(self.major)
636 if self.minor ~= nil then
637 version = version .. ('.' .. self.minor)
638 end
639 if self.patch ~= nil then
640 version = version .. ('.' .. self.patch)
641 end
642 if self.prerelease and #self.prerelease > 0 or self.partial and self.prerelease and #self.prerelease == 0 and self.build == nil then
643 version = version .. ('-' .. concat(self.prerelease, '.'))
644 end
645 if self.build and #self.build > 0 or self.partial and self.build and #self.build == 0 then
646 version = version .. ('+' .. concat(self.build, '.'))
647 end
648 return version
649 end,
650 _comparsionFunctions = function(self, partial)
651 if partial == nil then
652 partial = false
653 end
654 local prereleaseCmp
655 prereleaseCmp = function(a, b)
656 if a and b then
657 return identifierListCmp(a, b)
658 elseif a then
659 return -1
660 elseif b then
661 return 1
662 else
663 return 0
664 end
665 end
666 local buildCmp
667 buildCmp = function(a, b)
668 if a == b then
669 return 0
670 else
671 return 'not implemented'
672 end
673 end
674 local makeOptional
675 makeOptional = function(origCmpFun)
676 local altCmpFun
677 altCmpFun = function(a, b)
678 if a == nil or b == nil then
679 return 0
680 else
681 return origCmpFun(a, b)
682 end
683 end
684 return altCmpFun
685 end
686 if partial then
687 return {
688 baseCmp,
689 makeOptional(baseCmp),
690 makeOptional(baseCmp),
691 makeOptional(prereleaseCmp),
692 makeOptional(buildCmp)
693 }
694 else
695 return {
696 baseCmp,
697 baseCmp,
698 baseCmp,
699 prereleaseCmp,
700 buildCmp
701 }
702 end
703 end,
704 __compare = function(self, other)
705 local comparsionFunctions = self:_comparsionFunctions(self.partial or other.partial)
706 local comparsions = {
707 {
708 comparsionFunctions[1],
709 self.major,
710 other.major
711 },
712 {
713 comparsionFunctions[2],
714 self.minor,
715 other.minor
716 },
717 {
718 comparsionFunctions[3],
719 self.patch,
720 other.patch
721 },
722 {
723 comparsionFunctions[4],
724 self.prerelease,
725 other.prerelease
726 },
727 {
728 comparsionFunctions[5],
729 self.build,
730 other.build
731 }
732 }
733 for _index_0 = 1, #comparsions do
734 local cmpField = comparsions[_index_0]
735 local cmpFun, selfField, otherField = unpack(cmpField)
736 local cmpRes = cmpFun(selfField, otherField)
737 if cmpRes ~= 0 then
738 return cmpRes
739 end
740 end
741 return 0
742 end,
743 __compareHelper = function(self, other, condition, notimplTarget)
744 local cmpRes = self:__compare(other)
745 if cmpRes == 'not implemented' then
746 return notimplTarget
747 end
748 return condition(cmpRes)
749 end,
750 __eq = function(self, other)
751 local c
752 c = function(x)
753 return x == 0
754 end
755 return self:__compareHelper(other, c, false)
756 end,
757 __lt = function(self, other)
758 local c
759 c = function(x)
760 return x < 0
761 end
762 return self:__compareHelper(other, c, false)
763 end,
764 __le = function(self, other)
765 local c
766 c = function(x)
767 return x <= 0
768 end
769 return self:__compareHelper(other, c, false)
770 end
771 }
772 _base_0.__index = _base_0
773 _class_0 = setmetatable({
774 __init = function(self, versionString, partial)
775 if partial == nil then
776 partial = false
777 end
778 local major, minor, patch, prerelease, build = unpack(self:parse(versionString, partial))
779 self.major, self.minor, self.patch, self.prerelease, self.build, self.partial = major, minor, patch, prerelease, build, partial
780 end,
781 __base = _base_0,
782 __name = "Version"
783 }, {
784 __index = _base_0,
785 __call = function(cls, ...)
786 local _self_0 = setmetatable({}, _base_0)
787 cls.__init(_self_0, ...)
788 return _self_0
789 end
790 })
791 _base_0.__class = _class_0
792 local self = _class_0
793 self.versionRe = function(self, s)
794 local mjr, mnr, pch, rmn = s:match('^(%d+)%.(%d+)%.(%d+)(.*)$')
795 if not (mjr) then
796 return nil
797 end
798 local add, r = rmn:match('^%-([0-9a-zA-z.-]+)(.*)$')
799 if add then
800 rmn = r
801 end
802 local meta
803 meta, r = rmn:match('^%+([0-9a-zA-Z.-]+)(.*)$')
804 if meta then
805 rmn = r
806 end
807 if #rmn > 0 then
808 return nil
809 end
810 return mjr, mnr, pch, add, meta
811 end
812 self.partialVersionRe = function(self, s)
813 local mjr, rmn = s:match('^(%d+)(.*)$')
814 if not (mjr) then
815 return nil
816 end
817 local mnr, r = rmn:match('^%.(%d+)(.*)$')
818 if mnr then
819 rmn = r
820 end
821 local pch
822 pch, r = rmn:match('^%.(%d+)(.*)$')
823 if pch then
824 rmn = r
825 end
826 local add
827 add, r = rmn:match('^%-([0-9a-zA-Z.-]*)(.*)$')
828 if add then
829 rmn = r
830 end
831 local meta
832 meta, r = rmn:match('^%+([0-9a-zA-Z.-]*)(.*)$')
833 if meta then
834 rmn = r
835 end
836 if #rmn > 0 then
837 return nil
838 end
839 return mjr, mnr, pch, add, meta
840 end
841 Version = _class_0
842 end
843 local SpecItem
844 do
845 local _class_0
846 local _base_0 = {
847 parse = function(self, requirementString)
848 if not requirementString or type(requirementString) ~= 'string' or requirementString == '' then
849 error("Invalid empty requirement specification: " .. tostring(tostring(requirementString)))
850 end
851 if requirementString == '*' then
852 return {
853 self.__class.KIND_ANY,
854 ''
855 }
856 end
857 local kind, version = self.__class:reSpec(requirementString)
858 if not kind then
859 error("Invalid requirement specification: " .. tostring(requirementString))
860 end
861 kind = self.__class.KIND_ALIASES[kind] or kind
862 local spec = Version(version, true)
863 if spec.build ~= nil and kind ~= self.__class.KIND_EQUAL and kind ~= self.__class.KIND_NEQ then
864 error("Invalid requirement specification " .. tostring(requirementString) .. ": build numbers have no ordering")
865 end
866 return {
867 kind,
868 spec
869 }
870 end,
871 match = function(self, version)
872 local _exp_0 = self.kind
873 if self.__class.KIND_ANY == _exp_0 then
874 return true
875 elseif self.__class.KIND_LT == _exp_0 then
876 return version < self.spec
877 elseif self.__class.KIND_LTE == _exp_0 then
878 return version <= self.spec
879 elseif self.__class.KIND_EQUAL == _exp_0 then
880 return version == self.spec
881 elseif self.__class.KIND_GTE == _exp_0 then
882 return version >= self.spec
883 elseif self.__class.KIND_GT == _exp_0 then
884 return version > self.spec
885 elseif self.__class.KIND_NEQ == _exp_0 then
886 return version ~= self.spec
887 elseif self.__class.KIND_CARET == _exp_0 then
888 return self.spec <= version and version < self.spec:next_major()
889 elseif self.__class.KIND_TILDE == _exp_0 then
890 return self.spec <= version and version < self.spec:next_minor()
891 else
892 return error("Unexpected match kind: " .. tostring(self.kind))
893 end
894 end,
895 __tostring = function(self)
896 return tostring(self.kind) .. tostring(self.spec)
897 end,
898 __eq = function(self, other)
899 return self.kind == other.kind and self.spec == other.spec
900 end
901 }
902 _base_0.__index = _base_0
903 _class_0 = setmetatable({
904 __init = function(self, requirementString)
905 self.kind, self.spec = unpack(self:parse(requirementString))
906 end,
907 __base = _base_0,
908 __name = "SpecItem"
909 }, {
910 __index = _base_0,
911 __call = function(cls, ...)
912 local _self_0 = setmetatable({}, _base_0)
913 cls.__init(_self_0, ...)
914 return _self_0
915 end
916 })
917 _base_0.__class = _class_0
918 local self = _class_0
919 self.KIND_ANY = '*'
920 self.KIND_LT = '<'
921 self.KIND_LTE = '<='
922 self.KIND_EQUAL = '=='
923 self.KIND_SHORTEQ = '='
924 self.KIND_EMPTY = ''
925 self.KIND_GTE = '>='
926 self.KIND_GT = '>'
927 self.KIND_NEQ = '!='
928 self.KIND_CARET = '^'
929 self.KIND_TILDE = '~'
930 self.KIND_ALIASES = {
931 [self.__class.KIND_SHORTEQ] = self.__class.KIND_EQUAL,
932 [self.__class.KIND_EMPTY] = self.__class.KIND_EQUAL
933 }
934 self.reSpec = function(self, s)
935 local chr, v = s:match('^(.-)(%d.*)$')
936 if not (chr == '<' or chr == '<=' or chr == '' or chr == '=' or chr == '==' or chr == '>=' or chr == '>' or chr == '!=' or chr == '^' or chr == '~') then
937 return nil
938 else
939 return chr, v
940 end
941 end
942 SpecItem = _class_0
943 end
944 local Spec
945 do
946 local _class_0
947 local _base_0 = {
948 parse = function(self, specsString)
949 local _accum_0 = { }
950 local _len_0 = 1
951 for x in specsString:gmatch('[^,]+') do
952 _accum_0[_len_0] = SpecItem(x)
953 _len_0 = _len_0 + 1
954 end
955 return _accum_0
956 end,
957 match = function(self, version)
958 local _list_0 = self.specs
959 for _index_0 = 1, #_list_0 do
960 local spec = _list_0[_index_0]
961 if not spec:match(version) then
962 return false
963 end
964 end
965 return true
966 end,
967 filter = function(self, versions)
968 local i = 0
969 return function()
970 while true do
971 i = i + 1
972 local version = versions[i]
973 if not (version) then
974 return nil
975 end
976 if self:match(version) then
977 return version
978 end
979 end
980 end
981 end,
982 select = function(self, versions)
983 local options
984 do
985 local _accum_0 = { }
986 local _len_0 = 1
987 for x in self:filter(versions) do
988 _accum_0[_len_0] = x
989 _len_0 = _len_0 + 1
990 end
991 options = _accum_0
992 end
993 if #options > 0 then
994 local max = options[1]
995 for _index_0 = 1, #options do
996 local ver = options[_index_0]
997 if max < ver then
998 max = ver
999 end
1000 end
1001 return max
1002 else
1003 return nil
1004 end
1005 end,
1006 __index = function(self, k)
1007 if self:match(k) then
1008 return true
1009 else
1010 return nil
1011 end
1012 end,
1013 __pairs = function(self)
1014 return pairs(self.specs)
1015 end,
1016 __ipairs = function(self)
1017 return ipairs(self.specs)
1018 end,
1019 __tostring = function(self)
1020 return concat((function()
1021 local _accum_0 = { }
1022 local _len_0 = 1
1023 local _list_0 = self.specs
1024 for _index_0 = 1, #_list_0 do
1025 local spec = _list_0[_index_0]
1026 _accum_0[_len_0] = tostring(spec)
1027 _len_0 = _len_0 + 1
1028 end
1029 return _accum_0
1030 end)(), ',')
1031 end,
1032 __eq = function(self, other)
1033 local _list_0 = self.specs
1034 for _index_0 = 1, #_list_0 do
1035 local selfSpec = _list_0[_index_0]
1036 local s = false
1037 local _list_1 = other.specs
1038 for _index_1 = 1, #_list_1 do
1039 local otherSpec = _list_1[_index_1]
1040 if selfSpec == otherSpec then
1041 s = true
1042 break
1043 end
1044 end
1045 if not s then
1046 return false
1047 end
1048 end
1049 return true
1050 end
1051 }
1052 _base_0.__index = _base_0
1053 _class_0 = setmetatable({
1054 __init = function(self, specsStrings)
1055 if type(specsStrings) == 'string' then
1056 specsStrings = {
1057 specsStrings
1058 }
1059 end
1060 local subspecs
1061 do
1062 local _accum_0 = { }
1063 local _len_0 = 1
1064 for _index_0 = 1, #specsStrings do
1065 local spec = specsStrings[_index_0]
1066 _accum_0[_len_0] = self:parse(spec)
1067 _len_0 = _len_0 + 1
1068 end
1069 subspecs = _accum_0
1070 end
1071 self.specs = { }
1072 for _index_0 = 1, #subspecs do
1073 local subspec = subspecs[_index_0]
1074 for _index_1 = 1, #subspec do
1075 local spec = subspec[_index_1]
1076 insert(self.specs, spec)
1077 end
1078 end
1079 end,
1080 __base = _base_0,
1081 __name = "Spec"
1082 }, {
1083 __index = _base_0,
1084 __call = function(cls, ...)
1085 local _self_0 = setmetatable({}, _base_0)
1086 cls.__init(_self_0, ...)
1087 return _self_0
1088 end
1089 })
1090 _base_0.__class = _class_0
1091 Spec = _class_0
1092 end
1093 local compare
1094 compare = function(v1, v2)
1095 return baseCmp(Version(v1, Version(v2)))
1096 end
1097 local match
1098 match = function(spec, version)
1099 return Spec(spec):match(Version(version))
1100 end
1101 local validate
1102 validate = function(versionString)
1103 return ({
1104 Version:parse(versionString)
1105 })[1]
1106 end
1107 return {
1108 Spec = Spec,
1109 SpecItem = SpecItem,
1110 Version = Version,
1111 compare = compare,
1112 match = match,
1113 validate = validate
1114 }
1115end)()
1116local isAvailable
1117isAvailable = require("component").isAvailable
1118local parse, getWorkingDirectory
1119do
1120 local _obj_0 = require("shell")
1121 parse, getWorkingDirectory = _obj_0.parse, _obj_0.getWorkingDirectory
1122end
1123local shell = require("shell")
1124local isDirectory, exists, makeDirectory, concat, copy, lastModified
1125do
1126 local _obj_0 = require("filesystem")
1127 isDirectory, exists, makeDirectory, concat, copy, lastModified = _obj_0.isDirectory, _obj_0.exists, _obj_0.makeDirectory, _obj_0.concat, _obj_0.copy, _obj_0.lastModified
1128end
1129local fs = require("filesystem")
1130local serialize, unserialize
1131do
1132 local _obj_0 = require("serialization")
1133 serialize, unserialize = _obj_0.serialize, _obj_0.unserialize
1134end
1135local pull
1136pull = require("event").pull
1137local clearLine, getCursor, clear
1138do
1139 local _obj_0 = require("term")
1140 clearLine, getCursor, clear = _obj_0.clearLine, _obj_0.getCursor, _obj_0.clear
1141end
1142local exit
1143exit = os.exit
1144local write, stderr
1145do
1146 local _obj_0 = io
1147 write, stderr = _obj_0.write, _obj_0.stderr
1148end
1149local insert, unpack
1150do
1151 local _obj_0 = table
1152 insert, unpack = _obj_0.insert, _obj_0.unpack
1153end
1154local listFiles = fs.list
1155local options, args = { }, { }
1156local request = nil
1157local modules = { }
1158local config = { }
1159local env = { }
1160local modulePath = "/etc/hpm/module/"
1161local distPath = "/var/lib/hpm/dist/"
1162local exitCode = 0
1163local CONFIG_PATH = "/etc/hpm/hpm.cfg"
1164local USAGE = [[Usage: hpm OPTIONS COMMAND
1165See `man hpm` for more info.]]
1166local DEFAULT_CONFIG = [[-- << Global settings >> -------------------------------------------------------
1167-- A directory where package manifests will be placed.
1168-- It will be created if it doesn't exist.
1169dist = "/var/lib/hpm/dist"
1170
1171-- A place where to search for custom hpm modules.
1172-- It will be created if it doesn't exist.
1173modules = "/etc/hpm/module"
1174
1175-- << Settings related to the hel module >> ------------------------------------
1176hel = {}
1177
1178-- If set to `false`, hpm will *only* remove a package that hpm is told to
1179-- remove. Otherwise, all of its dependants will be also removed.
1180hel.remove_dependants = true
1181
1182-- << Settings related to the oppm module >> -----------------------------------
1183oppm = {}
1184
1185-- A cache file where package manifests will be stored for faster access.
1186oppm.cache_file = "/var/cache/hpm/oppm"
1187
1188-- See hel.remove_dependants above.
1189oppm.remove_dependants = true
1190
1191-- Connect additional GitHub repositories not present in OpenPrograms' list.
1192-- The format is the same as in the oppm.cfg file at OpenPrograms:
1193-- https://github.com/OpenPrograms/openprograms.github.io/blob/master/repos.cfg
1194oppm.custom_repos = {
1195 -- ["My-Super-Repository"] = {
1196 -- repo="My-GitHub-Username/my-programs"
1197 -- }
1198}
1199]]
1200local log = {
1201 info = function(...)
1202 if options.v then
1203 return print(table.concat((function(...)
1204 local _accum_0 = { }
1205 local _len_0 = 1
1206 local _list_0 = {
1207 ...
1208 }
1209 for _index_0 = 1, #_list_0 do
1210 local x = _list_0[_index_0]
1211 _accum_0[_len_0] = tostring(x)
1212 _len_0 = _len_0 + 1
1213 end
1214 return _accum_0
1215 end)(...), "\t"))
1216 end
1217 end,
1218 print = function(...)
1219 if not (options.q) then
1220 return print(table.concat((function(...)
1221 local _accum_0 = { }
1222 local _len_0 = 1
1223 local _list_0 = {
1224 ...
1225 }
1226 for _index_0 = 1, #_list_0 do
1227 local x = _list_0[_index_0]
1228 _accum_0[_len_0] = tostring(x)
1229 _len_0 = _len_0 + 1
1230 end
1231 return _accum_0
1232 end)(...), "\t"))
1233 end
1234 end,
1235 error = function(...)
1236 if not (options.q) then
1237 return stderr:write(table.concat((function(...)
1238 local _accum_0 = { }
1239 local _len_0 = 1
1240 local _list_0 = {
1241 ...
1242 }
1243 for _index_0 = 1, #_list_0 do
1244 local x = _list_0[_index_0]
1245 _accum_0[_len_0] = tostring(x)
1246 _len_0 = _len_0 + 1
1247 end
1248 return _accum_0
1249 end)(...), "\t") .. '\n')
1250 end
1251 end,
1252 fatal = function(...)
1253 if not (options.q) then
1254 stderr:write(table.concat((function(...)
1255 local _accum_0 = { }
1256 local _len_0 = 1
1257 local _list_0 = {
1258 ...
1259 }
1260 for _index_0 = 1, #_list_0 do
1261 local x = _list_0[_index_0]
1262 _accum_0[_len_0] = tostring(x)
1263 _len_0 = _len_0 + 1
1264 end
1265 return _accum_0
1266 end)(...), "\t") .. '\n')
1267 end
1268 return exit(1)
1269 end
1270}
1271local assert
1272assert = function(statement, message)
1273 if not (statement) then
1274 return log.fatal(message)
1275 end
1276end
1277local unimplemented
1278unimplemented = function(what)
1279 return log.fatal((tostring(what)) .. ": Not implemented yet!")
1280end
1281local printUsage
1282printUsage = function()
1283 write(USAGE)
1284 return exit(0)
1285end
1286local try
1287try = function(result, reason)
1288 if not (result) then
1289 log.fatal(reason)
1290 end
1291 return result
1292end
1293local checkType
1294checkType = function(v, t, c)
1295 if not (type(v == t)) then
1296 log.fatal("Value '" .. tostring(v) .. "' is " .. tostring(type(c)) .. ", however, a " .. tostring(t) .. " is expected.")
1297 end
1298 return c
1299end
1300local argNumber
1301argNumber = function(v)
1302 return checkType(v, "number", tonumber(v))
1303end
1304local argString
1305argString = function(v)
1306 return checkType(v, "string", tostring(v))
1307end
1308local isin
1309isin = function(v, tbl)
1310 for k, value in pairs(tbl) do
1311 if value == v then
1312 return true, k
1313 end
1314 end
1315 return false
1316end
1317local tableLen
1318tableLen = function(tbl)
1319 local result = 0
1320 for k, v in pairs(tbl) do
1321 result = result + 1
1322 end
1323 return result
1324end
1325local empty
1326empty = function(v)
1327 if type(v) == "nil" then
1328 return true
1329 elseif type(v) == "string" then
1330 return not v or #v < 1
1331 elseif type(v) == "table" then
1332 return not v or tableLen(v) < 1
1333 else
1334 return true
1335 end
1336end
1337local all
1338all = function(vals)
1339 for _index_0 = 1, #vals do
1340 local v = vals[_index_0]
1341 if not v then
1342 return false
1343 end
1344 end
1345 return true
1346end
1347local existsDir
1348existsDir = function(path)
1349 return exists(path) and isDirectory(path)
1350end
1351local existsFile
1352existsFile = function(path)
1353 return exists(path) and not isDirectory(path)
1354end
1355local plural
1356plural = function(amount)
1357 return amount == 1 and "" or "s"
1358end
1359local singular
1360singular = function(amount)
1361 return amount ~= 1 and "" or "s"
1362end
1363local linkingVerb
1364linkingVerb = function(amount)
1365 return amount == 1 and "is" or "are"
1366end
1367local deepPatch
1368deepPatch = function(base, head)
1369 for k, v in pairs(head) do
1370 if type(v) ~= "table" or type(base[k]) ~= "table" then
1371 base[k] = v
1372 else
1373 deepPatch(base[k], v)
1374 end
1375 end
1376end
1377local deepCopy
1378deepCopy = function(tbl)
1379 if type(tbl) ~= "table" then
1380 return tbl
1381 end
1382 local result = { }
1383 for k, v in pairs(tbl) do
1384 result[k] = deepCopy(v)
1385 end
1386 return result
1387end
1388local getRealTime
1389getRealTime = function()
1390 do
1391 local file = io.open("/tmp/hpm-time", "w")
1392 file:write("")
1393 file:close()
1394 end
1395 local result = lastModified("/tmp/hpm-time")
1396 fs.remove("/tmp/hpm-time")
1397 return result
1398end
1399local remove
1400remove = function(path)
1401 if fs.get(shell.resolve(path)).isReadOnly() then
1402 return false, "the path is readonly!"
1403 elseif not exists(path) then
1404 return false, "the filesystem node doesn't exist."
1405 else
1406 if not (isDirectory(path) or fs.isLink(path)) then
1407 return fs.remove(path)
1408 else
1409 for file in try(listFiles(path)) do
1410 remove(concat(path, file))
1411 end
1412 return fs.remove(path)
1413 end
1414 end
1415end
1416local loadConfig
1417loadConfig = function()
1418 local path = options.c or options.config or CONFIG_PATH
1419 if not existsFile(path) then
1420 local dirPath = fs.path(path)
1421 if not existsDir(dirPath) then
1422 local result, reason = makeDirectory(dirPath)
1423 if not result then
1424 return false, "Failed to create '" .. tostring(dirPath) .. "' directory for the config file: " .. tostring(reason)
1425 end
1426 end
1427 local file, reason = io.open(path, "w")
1428 if file then
1429 file:write(DEFAULT_CONFIG)
1430 file:close()
1431 else
1432 return false, "Failed to open config file for writing: " .. tostring(reason)
1433 end
1434 end
1435 local file, reason = io.open(path, "r")
1436 if file then
1437 local content = file:read("*all")
1438 file:close()
1439 local globals = { }
1440 (load(content, "config", "t", globals))()
1441 local newUndecl
1442 newUndecl = function(base)
1443 if base == nil then
1444 base = { }
1445 end
1446 return setmetatable(base, {
1447 __index = {
1448 get = function(k, v, createNewUndecl)
1449 if type(base[k]) ~= "nil" then
1450 if type(base[k]) == "table" then
1451 return newUndecl(base[k])
1452 end
1453 return base[k]
1454 end
1455 log.error("Attempt to access undeclared config field '" .. tostring(k) .. "'!")
1456 if not createNewUndecl then
1457 return v
1458 else
1459 return newUndecl(v)
1460 end
1461 end
1462 }
1463 })
1464 end
1465 config = newUndecl(globals)
1466 modulePath = config.get("modules", modulePath)
1467 distPath = config.get("dist", distPath)
1468 return config
1469 else
1470 return false, "Failed to open config file for reading: " .. tostring(reason)
1471 end
1472end
1473local checkInternet
1474checkInternet = function()
1475 if not (isAvailable("internet")) then
1476 log.fatal("This command requires an internet card to run!")
1477 end
1478 request = request or require("internet").request
1479end
1480local download
1481download = function(url)
1482 checkInternet()
1483 return pcall(request, url)
1484end
1485local loadCustomModules
1486loadCustomModules = function()
1487 if not existsDir(modulePath) then
1488 local result, reason = makeDirectory(modulePath)
1489 if not result then
1490 return false, "Failed to create '" .. tostring(modulePath) .. "' directory for custom modules: " .. tostring(reason)
1491 end
1492 end
1493 local list = try(listFiles(modulePath))
1494 for file in list do
1495 env.name = file:match("^(.+)%..+$")
1496 local mod = try(loadfile(concat(modulePath, file), "t", env))
1497 mod()
1498 env.name = nil
1499 end
1500 return true
1501end
1502local findCustomCommand
1503findCustomCommand = function(name)
1504 local command = name
1505 local mod
1506 do
1507 local p1 = name:find(':')
1508 if p1 then
1509 command = name:sub(p1 + 1)
1510 mod = name:sub(1, p1 - 1)
1511 end
1512 end
1513 if not mod then
1514 local candidates = { }
1515 for modName, mod in pairs(modules) do
1516 if mod[command] then
1517 if type(mod[command]) == "table" and mod[command].__public == true then
1518 insert(candidates, {
1519 class = mod,
1520 module = modName,
1521 method = mod[command]
1522 })
1523 end
1524 end
1525 end
1526 if #candidates > 1 then
1527 local pos = nil
1528 for k, mod in pairs(candidates) do
1529 if mod.module == "hel" then
1530 pos = k
1531 break
1532 end
1533 end
1534 if pos then
1535 candidates = {
1536 candidates[pos]
1537 }
1538 end
1539 end
1540 if #candidates > 1 then
1541 log.print("Ambiguous choice: method " .. tostring(command) .. " is implemented in the following modules:")
1542 for _index_0 = 1, #candidates do
1543 local mod = candidates[_index_0]
1544 log.print(" * " .. tostring(mod.module))
1545 end
1546 log.print("Choose a specific module by prepending its name with a colon, e.g., " .. tostring(candidates[1].module) .. ":" .. tostring(command) .. ".")
1547 return false
1548 elseif #candidates == 0 then
1549 log.error("Unknown command: " .. tostring(command))
1550 return false
1551 else
1552 mod = candidates[1].module
1553 log.info("Note, using " .. tostring(mod) .. ":" .. tostring(command) .. ".")
1554 return function(...)
1555 return candidates[1].method(candidates[1].class, ...)
1556 end
1557 end
1558 else
1559 if modules[mod] and empty(command) then
1560 local modSpecMths = { }
1561 for k, v in pairs(modules[mod]) do
1562 if type(v) == "table" and v.__public == true then
1563 insert(modSpecMths, tostring(k))
1564 end
1565 end
1566 log.print("Available module-specific commands: " .. tostring(table.concat(modSpecMths, ", ")))
1567 return false
1568 end
1569 if not modules[mod] or not modules[mod][command] or modules[mod][command] and (type(modules[mod][command]) ~= "table" or modules[mod][command].__public ~= true) then
1570 log.error("Unknown command: " .. tostring(mod) .. ":" .. tostring(command))
1571 return false
1572 else
1573 return function(...)
1574 return modules[mod][command](modules[mod], ...)
1575 end
1576 end
1577 end
1578end
1579local getModuleBy
1580getModuleBy = function(source)
1581 if not source or source == "" then
1582 source = "hel"
1583 else
1584 source = source
1585 end
1586 return modules[source] or modules.default
1587end
1588local callModuleMethod
1589callModuleMethod = function(mod, name, ...)
1590 if mod == nil then
1591 mod = modules.default
1592 end
1593 if mod[name] then
1594 return mod[name](mod, ...)
1595 else
1596 return modules.default[name](modules.default, ...)
1597 end
1598end
1599local saveManifest
1600saveManifest = function(manifest, mod, path, name)
1601 if mod == nil then
1602 mod = "hel"
1603 end
1604 if path == nil then
1605 path = concat(distPath, mod)
1606 end
1607 if name == nil then
1608 name = manifest.name
1609 end
1610 if not manifest then
1611 return false, "'nil' given"
1612 end
1613 if not existsDir(path) then
1614 local result, reason = makeDirectory(path)
1615 if not result then
1616 return false, "Failed to create '" .. tostring(path) .. "' directory for manifest files: " .. tostring(reason)
1617 end
1618 end
1619 local file, reason = io.open(concat(path, name), "w")
1620 if file then
1621 file:write(serialize(manifest))
1622 file:close()
1623 return true
1624 else
1625 return false, "Failed to open file for writing: " .. tostring(reason)
1626 end
1627end
1628local loadManifest
1629loadManifest = function(name, path, mod)
1630 if mod == nil then
1631 mod = "hel"
1632 end
1633 path = path or concat(distPath, mod, name)
1634 if existsFile(path) then
1635 local file, reason = io.open(path, "rb")
1636 if file then
1637 local manifest = try(unserialize(file:read("*all")))
1638 file:close()
1639 return manifest
1640 else
1641 return false, "Failed to open manifest for '" .. tostring(name) .. "' package: " .. tostring(reason)
1642 end
1643 else
1644 return false, "No manifest found for '" .. tostring(name) .. "' package"
1645 end
1646end
1647local removeManifest
1648removeManifest = function(name, mod)
1649 if mod == nil then
1650 mod = "hel"
1651 end
1652 local path = concat(distPath, mod, name)
1653 if existsFile(path) then
1654 return remove(path)
1655 else
1656 return false, "No manifest found for '" .. tostring(name) .. "' package"
1657 end
1658end
1659local public
1660public = function(func)
1661 return setmetatable({
1662 __public = true
1663 }, {
1664 __call = function(self, ...)
1665 return func(...)
1666 end
1667 })
1668end
1669local wrapResponse
1670wrapResponse = function(resp, file)
1671 return function()
1672 local result, chunk = pcall(resp)
1673 if not (result) then
1674 return false, tostring(chunk)
1675 else
1676 return chunk
1677 end
1678 end
1679end
1680local recv
1681recv = function(url, connectError, downloadError)
1682 if connectError == nil then
1683 connectError = "Could not download '%s': %s"
1684 end
1685 if downloadError == nil then
1686 downloadError = "Could not download '%s': %s"
1687 end
1688 local result, response, reason = download(url)
1689 if not (result and response) then
1690 return false, connectError:format(url, reason)
1691 end
1692 local data = ""
1693 for chunk, reason in wrapResponse(response) do
1694 if chunk then
1695 data = data .. chunk
1696 else
1697 return false, downloadError:format(url, reason)
1698 end
1699 end
1700 return data
1701end
1702local confirm
1703confirm = function()
1704 if not (options.y) then
1705 io.write("Press [ENTER] to continue...")
1706 local key = select(3, pull("key_down"))
1707 if key == 13 then
1708 clearLine()
1709 return true
1710 else
1711 io.write("\n")
1712 return false
1713 end
1714 else
1715 return true
1716 end
1717end
1718local pkgPlan
1719pkgPlan = function(plan)
1720 local complexity = 0
1721 local msg = { }
1722 if not (empty(plan.install)) then
1723 local m = {
1724 "Packages to INSTALL:",
1725 table.concat(plan.install, " ")
1726 }
1727 insert(msg, m)
1728 complexity = complexity + #plan.install
1729 else
1730 plan.install = { }
1731 end
1732 if not (empty(plan.reinstall)) then
1733 local m = {
1734 "Packages to REINSTALL:",
1735 table.concat(plan.reinstall, " ")
1736 }
1737 insert(msg, m)
1738 complexity = complexity + #plan.reinstall
1739 else
1740 plan.reinstall = { }
1741 end
1742 if not (empty(plan.upgrade)) then
1743 local m = {
1744 "Packages to UPGRADE:",
1745 table.concat(plan.upgrade, " ")
1746 }
1747 insert(msg, m)
1748 complexity = complexity + #plan.upgrade
1749 else
1750 plan.upgrade = { }
1751 end
1752 if not (empty(plan.remove)) then
1753 local m = {
1754 "Packages to REMOVE:",
1755 table.concat(plan.remove, " ")
1756 }
1757 insert(msg, m)
1758 complexity = complexity + #plan.remove
1759 else
1760 plan.remove = { }
1761 end
1762 do
1763 local m = {
1764 tostring(#plan.install) .. " to INSTALL, " .. tostring(#plan.reinstall) .. " to REINSTALL, " .. tostring(#plan.upgrade) .. " to UPGRADE, " .. tostring(#plan.remove) .. " to REMOVE."
1765 }
1766 insert(msg, m)
1767 end
1768 for num, i in pairs(msg) do
1769 for num, line in pairs(i) do
1770 if num == 1 then
1771 log.print(line)
1772 else
1773 log.print(" " .. tostring(line))
1774 end
1775 end
1776 if num ~= #msg then
1777 log.print("")
1778 end
1779 end
1780 if complexity > 0 then
1781 if not (confirm()) then
1782 return exit(7)
1783 end
1784 end
1785end
1786do
1787 local _class_0
1788 local _base_0 = { }
1789 _base_0.__index = _base_0
1790 _class_0 = setmetatable({
1791 __init = function() end,
1792 __base = _base_0,
1793 __name = "default"
1794 }, {
1795 __index = _base_0,
1796 __call = function(cls, ...)
1797 local _self_0 = setmetatable({}, _base_0)
1798 cls.__init(_self_0, ...)
1799 return _self_0
1800 end
1801 })
1802 _base_0.__class = _class_0
1803 local self = _class_0
1804 self.install = function()
1805 return log.fatal("Incorrect source is provided! No default 'install' implementation.")
1806 end
1807 self.remove = function(self, manifest, mod)
1808 if mod == nil then
1809 mod = "hel"
1810 end
1811 if manifest then
1812 if manifest.files then
1813 for i, file in pairs(manifest.files) do
1814 if file.path then
1815 local result, reason = remove(file.path)
1816 if not (result) then
1817 log:error("Failed to remove '" .. tostring(file.path) .. "': " .. tostring(reason))
1818 end
1819 else
1820 local result, reason = remove(concat(file.dir, file.name))
1821 if not (result) then
1822 log:error("Failed to remove '" .. tostring(concat(file.dir, file.name)) .. "': " .. tostring(reason))
1823 end
1824 end
1825 end
1826 end
1827 return removeManifest(manifest.name, mod)
1828 else
1829 return false, "Package can't be removed: the manifest is empty."
1830 end
1831 end
1832 modules.default = _class_0
1833end
1834do
1835 local _class_0
1836 local _parent_0 = modules.default
1837 local _base_0 = { }
1838 _base_0.__index = _base_0
1839 setmetatable(_base_0, _parent_0.__base)
1840 _class_0 = setmetatable({
1841 __init = function(self, ...)
1842 return _class_0.__parent.__init(self, ...)
1843 end,
1844 __base = _base_0,
1845 __name = "hel",
1846 __parent = _parent_0
1847 }, {
1848 __index = function(cls, name)
1849 local val = rawget(_base_0, name)
1850 if val == nil then
1851 local parent = rawget(cls, "__parent")
1852 if parent then
1853 return parent[name]
1854 end
1855 else
1856 return val
1857 end
1858 end,
1859 __call = function(cls, ...)
1860 local _self_0 = setmetatable({}, _base_0)
1861 cls.__init(_self_0, ...)
1862 return _self_0
1863 end
1864 })
1865 _base_0.__class = _class_0
1866 local self = _class_0
1867 self.URL = "https://api.fomalhaut.me/"
1868 self.parsePackageJSON = function(self, decoded, spec)
1869 if spec == nil then
1870 spec = semver.Spec("*")
1871 end
1872 local selectedVersion = nil
1873 local versions = { }
1874 for number, data in pairs(decoded.versions) do
1875 local v = semver.Version(number)
1876 if not (v) then
1877 log.fatal("Could not parse the version in package: " .. tostring(v))
1878 end
1879 versions[v] = data
1880 end
1881 local success, bestMatch = pcall(function()
1882 return spec:select((function()
1883 local _accum_0 = { }
1884 local _len_0 = 1
1885 for version, data in pairs(versions) do
1886 _accum_0[_len_0] = version
1887 _len_0 = _len_0 + 1
1888 end
1889 return _accum_0
1890 end)())
1891 end)
1892 if not (success) then
1893 log.fatal("Could not select the best version: " .. tostring(bestMatch))
1894 end
1895 selectedVersion = tostring(bestMatch)
1896 if not (bestMatch) then
1897 log.fatal("No candidate for version specification '" .. tostring(spec) .. "' found!")
1898 end
1899 local data = {
1900 name = decoded.name,
1901 version = selectedVersion,
1902 files = { },
1903 dependencies = { }
1904 }
1905 for url, file in pairs(versions[bestMatch].files) do
1906 local path = file.path
1907 insert(data.files, {
1908 url = url,
1909 path = path
1910 })
1911 end
1912 for depName, depData in pairs(versions[bestMatch].depends) do
1913 local version = depData.version
1914 local depType = depData.type
1915 insert(data.dependencies, {
1916 name = depName,
1917 version = version,
1918 type = depType
1919 })
1920 end
1921 return data
1922 end
1923 self.getPackageSpec = function(self, name)
1924 log.info("Downloading package data for " .. tostring(name) .. " ...")
1925 local status, response = download(self.URL .. "packages/" .. name)
1926 if not (status) then
1927 log.fatal("HTTP request error: " .. response)
1928 end
1929 local jsonData = ""
1930 local success, reason = xpcall(function()
1931 for chunk in response do
1932 jsonData = jsonData .. chunk
1933 end
1934 end, debug.traceback)
1935 if not (success) then
1936 local output = {
1937 "HTTP request error.",
1938 "Perhaps the package " .. tostring(name) .. " doesn't exist, or the Hel Repository went down.",
1939 "Rerun with -v to see traceback.",
1940 "\nError details:\n" .. tostring(reason)
1941 }
1942 if options.v then
1943 table.remove(output, 3)
1944 else
1945 table.remove(output, 4)
1946 end
1947 log.fatal(table.concat(output, "\n"))
1948 end
1949 local decoded = json:decode(jsonData)
1950 if not (decoded) then
1951 log.fatal("Incorrect JSON format!\n" .. tostring(jsonData))
1952 end
1953 return decoded.data
1954 end
1955 self.rawInstall = function(self, pkgData, isManuallyInstalled, save)
1956 if isManuallyInstalled == nil then
1957 isManuallyInstalled = false
1958 end
1959 if save == nil then
1960 save = false
1961 end
1962 local prefix
1963 if save then
1964 prefix = concat(getWorkingDirectory(), pkgData.name)
1965 else
1966 prefix = "/"
1967 end
1968 if save and not existsDir(prefix) then
1969 local result, response = makeDirectory(prefix)
1970 if not (result) then
1971 log.fatal("Failed creating '" .. tostring(prefix) .. "' directory for package '" .. tostring(pkgData.name) .. "'! \n" .. tostring(response))
1972 end
1973 elseif not save then
1974 local manifest = loadManifest(pkgData.name, nil, "hel")
1975 if manifest then
1976 if manifest.version == tostring(pkgData.version) then
1977 log.print("'" .. tostring(pkgData.name) .. "@" .. tostring(manifest.version) .. "' is already installed, skipping...")
1978 return manifest
1979 else
1980 log.fatal("'" .. tostring(pkgData.name) .. "@" .. tostring(pkgData.version) .. "' was attempted to install, however, another version of the same package is already installed: '" .. tostring(pkgData.name) .. "@" .. tostring(manifest.version) .. "'")
1981 end
1982 end
1983 end
1984 for key, file in pairs(pkgData.files) do
1985 log.info("Fetching '" .. tostring(fs.name(file.path)) .. "' ...")
1986 local contents = try(recv(file.url))
1987 local path = concat(prefix, fs.path(file.path))
1988 if not existsDir(path) then
1989 local result, response = makeDirectory(path)
1990 if not (result) then
1991 log.fatal("Failed to create '" .. tostring(path) .. "' directory for '" .. tostring(fs.name(file.path)) .. "'! \n" .. tostring(response))
1992 end
1993 end
1994 do
1995 local reason
1996 file, reason = io.open(concat(path, fs.name(file.path)), "w")
1997 if not (file) then
1998 log.fatal("Could not open '" .. tostring(concat(path, fs.name(file.path))) .. "' for writing: " .. tostring(reason))
1999 end
2000 file:write(contents)
2001 file:close()
2002 end
2003 end
2004 return {
2005 name = pkgData.name,
2006 version = tostring(pkgData.version),
2007 files = pkgData.files,
2008 dependencies = pkgData.dependencies,
2009 manual = isManuallyInstalled
2010 }
2011 end
2012 self.resolveDependencies = function(self, packages, isReinstalling, resolved, unresolved, result)
2013 if resolved == nil then
2014 resolved = { }
2015 end
2016 if unresolved == nil then
2017 unresolved = { }
2018 end
2019 if result == nil then
2020 result = { }
2021 end
2022 for _index_0 = 1, #packages do
2023 local _des_0 = packages[_index_0]
2024 local name, version
2025 name, version = _des_0.name, _des_0.version
2026 local isResolved = false
2027 for _index_1 = 1, #resolved do
2028 local pkg = resolved[_index_1]
2029 if pkg.pkg.name == name then
2030 isResolved = true
2031 break
2032 end
2033 end
2034 if not (isResolved) then
2035 insert(unresolved, {
2036 name = name,
2037 version = ""
2038 })
2039 local manifest = loadManifest(name, nil, "hel")
2040 if not manifest or not version:match(semver.Version(manifest.version)) then
2041 local spec = self:getPackageSpec(name)
2042 local data = self:parsePackageJSON(spec, version)
2043 unresolved[#unresolved].version = data.version
2044 local _list_0 = data.dependencies
2045 for _index_1 = 1, #_list_0 do
2046 local dep = _list_0[_index_1]
2047 isResolved = false
2048 for _index_2 = 1, #resolved do
2049 local pkg = resolved[_index_2]
2050 if pkg.pkg.name == dep.name then
2051 isResolved = true
2052 break
2053 end
2054 end
2055 if not isResolved then
2056 local key = nil
2057 for k, pkg in pairs(unresolved) do
2058 if pkg.name == dep.name then
2059 key = k
2060 break
2061 end
2062 end
2063 if key then
2064 if unresolved[key].version == dep.version then
2065 log.fatal("Circular dependencies detected: '" .. tostring(name) .. "@" .. tostring(data.version) .. "' depends on '" .. tostring(dep.name) .. "@" .. tostring(dep.version) .. "', and '" .. tostring(unresolved[key].name) .. "@" .. tostring(unresolved[key].version) .. "' depends on '" .. tostring(name) .. "@" .. tostring(data.version) .. "'.")
2066 else
2067 log.fatal("Attempted to install two versions of the same package: '" .. tostring(dep.name) .. "@" .. tostring(dep.version) .. "' and '" .. tostring(unresolved[key].name) .. "@" .. tostring(unresolved[key].version) .. "' when resolving dependencies for '" .. tostring(name) .. "@" .. tostring(data.version) .. "'.")
2068 end
2069 end
2070 self:resolveDependencies({
2071 {
2072 name = dep.name,
2073 version = semver.Spec(dep.version)
2074 }
2075 }, false, resolved, unresolved, result)
2076 end
2077 end
2078 insert(resolved, {
2079 pkg = data
2080 })
2081 insert(result, {
2082 pkg = data
2083 })
2084 else
2085 insert(resolved, {
2086 pkg = manifest
2087 })
2088 if isReinstalling then
2089 insert(result, {
2090 pkg = manifest
2091 })
2092 end
2093 end
2094 unresolved[#unresolved] = nil
2095 end
2096 end
2097 return result
2098 end
2099 self.getPackageDependants = function(self, packages, resolved, unresolved)
2100 if resolved == nil then
2101 resolved = { }
2102 end
2103 if unresolved == nil then
2104 unresolved = { }
2105 end
2106 for _index_0 = 1, #packages do
2107 local name = packages[_index_0]
2108 local isResolved = false
2109 for _index_1 = 1, #resolved do
2110 local pkg = resolved[_index_1]
2111 if pkg.name == name then
2112 isResolved = true
2113 break
2114 end
2115 end
2116 if not (isResolved) then
2117 insert(unresolved, {
2118 name = name
2119 })
2120 local manifest = loadManifest(name, nil, "hel")
2121 if manifest then
2122 insert(resolved, {
2123 name = name,
2124 manifest = manifest
2125 })
2126 local list = try(listFiles(concat(distPath, "hel")))
2127 for file in list do
2128 manifest = try(loadManifest(file, nil, "hel"))
2129 local _list_0 = manifest.dependencies
2130 for _index_1 = 1, #_list_0 do
2131 local dep = _list_0[_index_1]
2132 if dep.name == name then
2133 isResolved = false
2134 for _index_2 = 1, #resolved do
2135 local pkg = resolved[_index_2]
2136 if pkg.name == file then
2137 isResolved = true
2138 break
2139 end
2140 end
2141 if not isResolved then
2142 for _index_2 = 1, #unresolved do
2143 local pkg = unresolved[_index_2]
2144 if pkg.name == file then
2145 log.fatal("Circular dependencies detected: " .. tostring(file))
2146 end
2147 end
2148 self:getPackageDependants({
2149 file
2150 }, resolved, unresolved)
2151 end
2152 end
2153 end
2154 end
2155 else
2156 log.fatal("Package " .. tostring(name) .. " is referenced as a dependant of another package, however, this package isn't installed.")
2157 end
2158 unresolved[#unresolved] = nil
2159 end
2160 end
2161 return resolved
2162 end
2163 self.fileConflicts = function(self, packages)
2164 if options.f or options.force then
2165 log.info("File conflict checking skipped: --force option given.")
2166 return true
2167 end
2168 log.info("Checking for file conflicts...")
2169 local conflictsDetected = false
2170 for _index_0 = 1, #packages do
2171 local pkg = packages[_index_0]
2172 local _list_0 = pkg.files
2173 for _index_1 = 1, #_list_0 do
2174 local file = _list_0[_index_1]
2175 if exists(file.path) then
2176 log.error("'" .. tostring(pkg.name) .. "' wants to override node at '" .. tostring(file.path) .. "'")
2177 conflictsDetected = true
2178 end
2179 end
2180 end
2181 if conflictsDetected then
2182 log.fatal("File conflicts detected; terminating.")
2183 end
2184 return true
2185 end
2186 self.install = public(function(self, ...)
2187 if options.l or options["local"] then
2188 local path = shell.resolve(...)
2189 local manifest = try(loadManifest(path, concat(path, "manifest")))
2190 local dependencyGraph = self:resolveDependencies((function()
2191 local _accum_0 = { }
2192 local _len_0 = 1
2193 local _list_0 = manifest.dependencies
2194 for _index_0 = 1, #_list_0 do
2195 local _des_0 = _list_0[_index_0]
2196 local name, version
2197 name, version = _des_0.name, _des_0.version
2198 _accum_0[_len_0] = {
2199 name = name,
2200 version = semver.Spec(version)
2201 }
2202 _len_0 = _len_0 + 1
2203 end
2204 return _accum_0
2205 end)())
2206 local onlyDeps = options.d or options.onlyDeps
2207 local toInstall = { }
2208 for _index_0 = 1, #dependencyGraph do
2209 local node = dependencyGraph[_index_0]
2210 insert(toInstall, tostring(node.pkg.name) .. "@" .. tostring(node.pkg.version))
2211 end
2212 if not (onlyDeps) then
2213 insert(toInstall, tostring(manifest.name) .. "@" .. tostring(manifest.version))
2214 end
2215 pkgPlan({
2216 install = toInstall
2217 })
2218 self:fileConflicts((function()
2219 local _accum_0 = { }
2220 local _len_0 = 1
2221 for _index_0 = 1, #dependencyGraph do
2222 local x = dependencyGraph[_index_0]
2223 _accum_0[_len_0] = x.pkg
2224 _len_0 = _len_0 + 1
2225 end
2226 return _accum_0
2227 end)())
2228 for i = 1, #dependencyGraph, 1 do
2229 local node = dependencyGraph[i]
2230 log.print("Installing '" .. tostring(node.pkg.name) .. "@" .. tostring(node.pkg.version) .. "'...")
2231 local _manifest = self:rawInstall(node.pkg, false, false)
2232 local success, reason = saveManifest(_manifest, "hel")
2233 if success then
2234 log.info("Saved the manifest of '" .. tostring(_manifest.name) .. "'.")
2235 else
2236 log.fatal("Couldn't save the manifest of '" .. tostring(_manifest.name) .. "': " .. tostring(reason) .. ".")
2237 end
2238 end
2239 if not onlyDeps then
2240 log.print("Installing '" .. tostring(manifest.name) .. "@" .. tostring(manifest.version) .. "'...")
2241 for key, file in pairs(manifest.files) do
2242 local filePath = file.path or concat(file.dir, file.name)
2243 if not existsDir(fs.path(filePath)) then
2244 makeDirectory(fs.path(filePath))
2245 end
2246 local result, reason = copy(concat(path, file.url), filePath)
2247 if not (result) then
2248 log.fatal("Cannot copy file '" .. tostring(file.name) .. "': " .. tostring(reason))
2249 end
2250 end
2251 manifest["local"] = true
2252 local success, reason = saveManifest(manifest, "hel")
2253 if success then
2254 log.info("Saved the manifest of '" .. tostring(manifest.name) .. "'.")
2255 else
2256 log.fatal("Couldn't save the manifest of '" .. tostring(manifest.name) .. "': " .. tostring(reason) .. ".")
2257 end
2258 end
2259 log.print("Done.")
2260 return true
2261 end
2262 local packages = { }
2263 local _list_0 = {
2264 ...
2265 }
2266 for _index_0 = 1, #_list_0 do
2267 local x = _list_0[_index_0]
2268 local name, version = x:match("^(.+)@(.+)$") or x
2269 if empty(version) then
2270 version = "*"
2271 end
2272 log.info("Creating version specification for " .. tostring(version) .. " ...")
2273 local success, spec = pcall(function()
2274 return semver.Spec(version)
2275 end)
2276 if not (success) then
2277 log.fatal("Could not parse the version specification: " .. tostring(spec) .. "!")
2278 end
2279 insert(packages, {
2280 name = name,
2281 version = spec
2282 })
2283 end
2284 local reinstall = options.r or options.reinstall
2285 local save = options.s or options.save
2286 local dependencyGraph = self:resolveDependencies(packages, reinstall)
2287 local toReinstall = { }
2288 local toInstall = { }
2289 for _index_0 = 1, #dependencyGraph do
2290 local _continue_0 = false
2291 repeat
2292 local node = dependencyGraph[_index_0]
2293 if reinstall then
2294 local found = false
2295 for _index_1 = 1, #packages do
2296 local pkg = packages[_index_1]
2297 if pkg.name == node.pkg.name then
2298 found = true
2299 break
2300 end
2301 end
2302 if found then
2303 insert(toReinstall, tostring(node.pkg.name) .. "@" .. tostring(node.pkg.version))
2304 _continue_0 = true
2305 break
2306 end
2307 end
2308 insert(toInstall, tostring(node.pkg.name) .. "@" .. tostring(node.pkg.version))
2309 _continue_0 = true
2310 until true
2311 if not _continue_0 then
2312 break
2313 end
2314 end
2315 pkgPlan({
2316 install = toInstall,
2317 reinstall = toReinstall
2318 })
2319 self:fileConflicts((function()
2320 local _accum_0 = { }
2321 local _len_0 = 1
2322 for _index_0 = 1, #dependencyGraph do
2323 local x = dependencyGraph[_index_0]
2324 if not isin(tostring(x.pkg.name) .. "@" .. tostring(x.pkg.version), toReinstall) then
2325 _accum_0[_len_0] = x.pkg
2326 _len_0 = _len_0 + 1
2327 end
2328 end
2329 return _accum_0
2330 end)())
2331 if reinstall then
2332 local manifests
2333 do
2334 local _accum_0 = { }
2335 local _len_0 = 1
2336 for _index_0 = 1, #packages do
2337 local pkg = packages[_index_0]
2338 _accum_0[_len_0] = try(loadManifest(pkg.name, nil, "hel"))
2339 _len_0 = _len_0 + 1
2340 end
2341 manifests = _accum_0
2342 end
2343 self:_remove(manifests, true, false)
2344 end
2345 for _index_0 = 1, #dependencyGraph do
2346 local node = dependencyGraph[_index_0]
2347 log.print("Installing '" .. tostring(node.pkg.name) .. "@" .. tostring(node.pkg.version) .. "'...")
2348 local manual = false
2349 for _index_1 = 1, #packages do
2350 local pkg = packages[_index_1]
2351 if pkg.name == node.pkg.name then
2352 manual = true
2353 break
2354 end
2355 end
2356 local manifest = self:rawInstall(node.pkg, manual, save)
2357 local manifestPath
2358 if save then
2359 manifestPath = concat(getWorkingDirectory(), manifest.name)
2360 end
2361 local success, reason = saveManifest(manifest, "hel", manifestPath)
2362 if success then
2363 log.info("Saved the manifest of '" .. tostring(manifest.name) .. "'.")
2364 else
2365 log.fatal("Couldn't save the manifest of '" .. tostring(manifest.name) .. "': " .. tostring(reason) .. ".")
2366 end
2367 end
2368 return log.print("Done.")
2369 end)
2370 self.remove = public(function(self, ...)
2371 local packages = {
2372 ...
2373 }
2374 local manifests = { }
2375 for _index_0 = 1, #packages do
2376 local pkg = packages[_index_0]
2377 local manifest = try(loadManifest(pkg, nil, "hel"))
2378 insert(manifests, manifest)
2379 end
2380 self:_remove(manifests, false)
2381 return log.print("Done.")
2382 end)
2383 self._remove = function(self, manifests, noPlan, removeDeps)
2384 if noPlan == nil then
2385 noPlan = false
2386 end
2387 if removeDeps == nil then
2388 removeDeps = true
2389 end
2390 local deps
2391 if not config.get("hel", { }, true).get("remove_dependants", true) or not removeDeps then
2392 do
2393 local _accum_0 = { }
2394 local _len_0 = 1
2395 for _index_0 = 1, #manifests do
2396 local manifest = manifests[_index_0]
2397 _accum_0[_len_0] = {
2398 name = manifest.name,
2399 manifest = manifest
2400 }
2401 _len_0 = _len_0 + 1
2402 end
2403 deps = _accum_0
2404 end
2405 else
2406 deps = self:getPackageDependants((function()
2407 local _accum_0 = { }
2408 local _len_0 = 1
2409 for _index_0 = 1, #manifests do
2410 local manifest = manifests[_index_0]
2411 _accum_0[_len_0] = manifest.name
2412 _len_0 = _len_0 + 1
2413 end
2414 return _accum_0
2415 end)())
2416 end
2417 if not (noPlan) then
2418 pkgPlan({
2419 remove = (function()
2420 local _accum_0 = { }
2421 local _len_0 = 1
2422 for _index_0 = 1, #deps do
2423 local node = deps[_index_0]
2424 _accum_0[_len_0] = "hel:" .. tostring(node.manifest.name) .. "@" .. tostring(node.manifest.version)
2425 _len_0 = _len_0 + 1
2426 end
2427 return _accum_0
2428 end)()
2429 })
2430 end
2431 for _index_0 = 1, #deps do
2432 local dep = deps[_index_0]
2433 log.print("Removing '" .. tostring(dep.manifest.name) .. "@" .. tostring(dep.manifest.version) .. "' ...")
2434 try(_class_0.__parent.remove(self, dep.manifest, "hel"))
2435 end
2436 return true
2437 end
2438 self.upgrade = public(function(self)
2439 local installed = { }
2440 for file in try(listFiles(concat(distPath, "hel"))) do
2441 if not (isDirectory(concat(distPath, "hel", file))) then
2442 local manifest = try(loadManifest(file, nil, "hel"))
2443 if not (manifest["local"]) then
2444 insert(installed, manifest)
2445 end
2446 end
2447 end
2448 local upgradable = { }
2449 for _index_0 = 1, #installed do
2450 local pkg = installed[_index_0]
2451 local success, spec = pcall(self.getPackageSpec, self, pkg.name)
2452 if success then
2453 local data = self:parsePackageJSON(spec)
2454 pkg.latest = {
2455 spec = spec,
2456 data = data
2457 }
2458 if semver.Version(pkg.latest.data.version) > semver.Version(pkg.version) then
2459 insert(upgradable, pkg)
2460 end
2461 end
2462 end
2463 local deps = self:resolveDependencies((function()
2464 local _accum_0 = { }
2465 local _len_0 = 1
2466 for _index_0 = 1, #upgradable do
2467 local pkg = upgradable[_index_0]
2468 _accum_0[_len_0] = {
2469 name = pkg.name,
2470 version = semver.Spec(pkg.latest.data.version)
2471 }
2472 _len_0 = _len_0 + 1
2473 end
2474 return _accum_0
2475 end)())
2476 local toUpgrade
2477 do
2478 local _accum_0 = { }
2479 local _len_0 = 1
2480 for _index_0 = 1, #upgradable do
2481 local pkg = upgradable[_index_0]
2482 _accum_0[_len_0] = tostring(pkg.name) .. "@{" .. tostring(pkg.version) .. " => " .. tostring(pkg.latest.data.version) .. "}"
2483 _len_0 = _len_0 + 1
2484 end
2485 toUpgrade = _accum_0
2486 end
2487 local toInstall = { }
2488 for _index_0 = 1, #deps do
2489 local node = deps[_index_0]
2490 local isNew = true
2491 for _index_1 = 1, #upgradable do
2492 local pkg = upgradable[_index_1]
2493 if pkg.name == node.pkg.name then
2494 isNew = false
2495 break
2496 end
2497 end
2498 if isNew then
2499 insert(toInstall, tostring(node.pkg.name) .. "@" .. tostring(node.pkg.version))
2500 end
2501 end
2502 pkgPlan({
2503 upgrade = toUpgrade,
2504 install = toInstall
2505 })
2506 for _index_0 = 1, #deps do
2507 local node = deps[_index_0]
2508 local shouldRemove = false
2509 local manual = false
2510 for _index_1 = 1, #upgradable do
2511 local pkg = upgradable[_index_1]
2512 if pkg.name == node.pkg.name then
2513 shouldRemove = pkg
2514 manual = pkg.manual
2515 break
2516 end
2517 end
2518 if shouldRemove then
2519 self:_remove({
2520 shouldRemove
2521 }, true, false)
2522 end
2523 log.print("Installing '" .. tostring(node.pkg.name) .. "@" .. tostring(node.pkg.version) .. "'...")
2524 local manifest = self:rawInstall(node.pkg, manual, false)
2525 local success, reason = saveManifest(manifest, "hel")
2526 if success then
2527 log.info("Saved the manifest of '" .. tostring(manifest.name) .. "'.")
2528 else
2529 log.fatal("Couldn't save the manifest of '" .. tostring(manifest.name) .. "': " .. tostring(reason) .. ".")
2530 end
2531 end
2532 return log.print("Done.")
2533 end)
2534 self.info = public(function(self, pkg, specString)
2535 if specString == nil then
2536 specString = "*"
2537 end
2538 if empty(pkg) then
2539 log.fatal("Usage: hpm hel:info <package name> [<version specification>]")
2540 end
2541 if empty(specString) then
2542 specString = "*"
2543 end
2544 log.print("Creating version specification for " .. tostring(specString) .. " ...")
2545 local success, versionSpec = pcall(function()
2546 return semver.Spec(specString)
2547 end)
2548 if not (success) then
2549 log.fatal("Could not parse the version specification: " .. tostring(versionSpec) .. "!")
2550 end
2551 local spec = self:getPackageSpec(pkg)
2552 local data = self:parsePackageJSON(spec, versionSpec)
2553 local message = { }
2554 insert(message, "- Package name: " .. tostring(spec.name))
2555 insert(message, "- Description:\n" .. tostring(spec.description))
2556 insert(message, "- Package owners: " .. tostring(table.concat(spec.owners, ", ")))
2557 insert(message, "- Authors:\n" .. tostring(table.concat((function()
2558 local _accum_0 = { }
2559 local _len_0 = 1
2560 local _list_0 = spec.authors
2561 for _index_0 = 1, #_list_0 do
2562 local x = _list_0[_index_0]
2563 _accum_0[_len_0] = " - " .. tostring(x)
2564 _len_0 = _len_0 + 1
2565 end
2566 return _accum_0
2567 end)(), "\n")))
2568 insert(message, "- License: " .. tostring(spec.license))
2569 insert(message, "- Versions: " .. tostring(tableLen(spec.versions)) .. ", latest: " .. tostring(data.version))
2570 insert(message, " - Files: " .. tostring(#data.files))
2571 insert(message, " - Depends: " .. tostring(table.concat((function()
2572 local _accum_0 = { }
2573 local _len_0 = 1
2574 local _list_0 = data.dependencies
2575 for _index_0 = 1, #_list_0 do
2576 local x = _list_0[_index_0]
2577 _accum_0[_len_0] = tostring(x.name) .. "@" .. tostring(x.version)
2578 _len_0 = _len_0 + 1
2579 end
2580 return _accum_0
2581 end)(), " ")))
2582 insert(message, " - Changes:\n" .. tostring(spec.versions[data.version].changes))
2583 insert(message, "- Stats:")
2584 insert(message, " - Views: " .. tostring(spec.stats.views))
2585 insert(message, "- Creation date: " .. tostring(spec.stats.date.created) .. " UTC")
2586 insert(message, "- Last updated: " .. tostring(spec.stats.date["last-updated"]) .. " UTC")
2587 return log.print(table.concat(message, "\n"))
2588 end)
2589 self.search = public(function(self, ...)
2590 local offset = 0
2591 while true do
2592 local list = { }
2593 local url = self.URL .. "packages"
2594 url = url .. "?offset=" .. tostring(offset)
2595 if ... then
2596 url = url .. ("&q=" .. table.concat((function(...)
2597 local _accum_0 = { }
2598 local _len_0 = 1
2599 local _list_0 = {
2600 ...
2601 }
2602 for _index_0 = 1, #_list_0 do
2603 local x = _list_0[_index_0]
2604 _accum_0[_len_0] = '"' .. x:gsub("\"", "") .. '"'
2605 _len_0 = _len_0 + 1
2606 end
2607 return _accum_0
2608 end)(...), " "):gsub("&", ""))
2609 end
2610 local status, response = download(url)
2611 if not (status) then
2612 log.fatal("HTTP request error: " .. response)
2613 end
2614 local jsonData = ""
2615 for chunk in response do
2616 jsonData = jsonData .. chunk
2617 end
2618 local decoded = json:decode(jsonData)
2619 if not (decoded) then
2620 log.fatal("Incorrect JSON format!\n" .. tostring(jsonData))
2621 end
2622 list = decoded.data.list
2623 for _index_0 = 1, #list do
2624 local pkg = list[_index_0]
2625 log.print(tostring(pkg.name) .. ": " .. tostring(pkg.short_description))
2626 end
2627 if #list == 0 and offset == 0 then
2628 log.print("No packages found.")
2629 break
2630 end
2631 if decoded.data.truncated and decoded.data.sent + decoded.data.offset < decoded.data.total then
2632 offset = decoded.data.offset + decoded.data.sent
2633 else
2634 break
2635 end
2636 end
2637 end)
2638 if _parent_0.__inherited then
2639 _parent_0.__inherited(_parent_0, _class_0)
2640 end
2641 modules.hel = _class_0
2642end
2643do
2644 local _class_0
2645 local _parent_0 = modules.default
2646 local _base_0 = { }
2647 _base_0.__index = _base_0
2648 setmetatable(_base_0, _parent_0.__base)
2649 _class_0 = setmetatable({
2650 __init = function(self, ...)
2651 return _class_0.__parent.__init(self, ...)
2652 end,
2653 __base = _base_0,
2654 __name = "oppm",
2655 __parent = _parent_0
2656 }, {
2657 __index = function(cls, name)
2658 local val = rawget(_base_0, name)
2659 if val == nil then
2660 local parent = rawget(cls, "__parent")
2661 if parent then
2662 return parent[name]
2663 end
2664 else
2665 return val
2666 end
2667 end,
2668 __call = function(cls, ...)
2669 local _self_0 = setmetatable({}, _base_0)
2670 cls.__init(_self_0, ...)
2671 return _self_0
2672 end
2673 })
2674 _base_0.__class = _class_0
2675 local self = _class_0
2676 self.REPOS = "https://raw.githubusercontent.com/OpenPrograms/openprograms.github.io/master/repos.cfg"
2677 self.PACKAGES = "https://raw.githubusercontent.com/%s/master/programs.cfg"
2678 self.FILES = "https://raw.githubusercontent.com/%s/%s"
2679 self.DIRECTORY = "https://api.github.com/repos/%s/contents/%s?ref=%s"
2680 self.DEFAULT_CACHE_FILE = "/var/cache/hpm/oppm"
2681 self.cacheFile = function(self)
2682 local path = config.get("oppm", { }, true).get("cache_file", self.DEFAULT_CACHE_FILE)
2683 if not (existsDir(fs.path(path))) then
2684 local result, reason = makeDirectory(fs.path(path))
2685 if not (result) then
2686 log.fatal("Could not create the cache directory at " .. tostring(fs.path(path)) .. ": " .. tostring(reason))
2687 end
2688 end
2689 if not (existsFile(path)) then
2690 local file, reason = io.open(path, "w")
2691 if not (file) then
2692 log.fatal("Could not open '" .. tostring(path) .. "' for writing: " .. tostring(reason))
2693 end
2694 file:write("{}")
2695 file:close()
2696 end
2697 return path
2698 end
2699 self.notifyCacheUpdate = function(self, updated)
2700 local currentTime = getRealTime()
2701 local diff = currentTime - updated
2702 if diff > 24 * 60 * 60 * 1000 then
2703 log.print("Cache was last updated more than a day ago.")
2704 return log.print("Consider running hpm oppm:cache update to update it.")
2705 end
2706 end
2707 self.listCache = function(self)
2708 local list
2709 local cachePath = self:cacheFile()
2710 do
2711 local file, reason = io.open(cachePath, "r")
2712 if not (file) then
2713 return false, "Could not open '" .. tostring(cachePath) .. "' for reading: " .. tostring(reason)
2714 end
2715 all = file:read("*all")
2716 list, reason = unserialize(all)
2717 if not (list) then
2718 return false, "Cache is malformed: " .. tostring(reason)
2719 end
2720 file:close()
2721 end
2722 if not list.updated or not list.cache then
2723 list = {
2724 updated = 0,
2725 cache = list
2726 }
2727 end
2728 return list
2729 end
2730 self.resolveDirectory = function(self, repo, branch, path)
2731 local data = try(recv(self.DIRECTORY:format(repo, path, branch)))
2732 data = json:decode(data)
2733 if data.message then
2734 return false, "Could not fetch " .. tostring(repo) .. ":" .. tostring(branch) .. "/" .. tostring(path) .. ": " .. tostring(data.message)
2735 end
2736 local _accum_0 = { }
2737 local _len_0 = 1
2738 for _index_0 = 1, #data do
2739 local file = data[_index_0]
2740 if file.type == "file" then
2741 _accum_0[_len_0] = {
2742 name = file.name,
2743 url = file.download_url,
2744 path = file.path
2745 }
2746 _len_0 = _len_0 + 1
2747 end
2748 end
2749 return _accum_0
2750 end
2751 self.updateCache = function(self)
2752 local cacheFile = self:cacheFile()
2753 local oldFiles, reason = self:listCache()
2754 if not (oldFiles) then
2755 log.error("Old cache is malformed: " .. tostring(oldFiles))
2756 oldFiles = { }
2757 else
2758 oldFiles = oldFiles.cache
2759 end
2760 local repos
2761 repos, reason = recv(self.REPOS)
2762 if not (repos) then
2763 return false, "Could not fetch " .. tostring(self.REPOS) .. ": " .. tostring(reason)
2764 end
2765 repos = unserialize(repos)
2766 local customRepos = { }
2767 for k, v in pairs(deepCopy(config.get("oppm", { }, true).get("custom_repos", { }))) do
2768 customRepos["local/" .. tostring(k)] = v
2769 end
2770 deepPatch(repos, customRepos)
2771 local programs = { }
2772 for repo, repoData in pairs(repos) do
2773 local _continue_0 = false
2774 repeat
2775 if repoData.repo then
2776 log.info("Fetching '" .. tostring(repo) .. "' at '" .. tostring(repoData.repo) .. "' ...")
2777 local result, response
2778 result, response, reason = download(self.PACKAGES:format(repoData.repo))
2779 if not (result and response) then
2780 log.error("Could not fetch '" .. tostring(repo) .. "' at '" .. tostring(repoData.repo) .. "': " .. tostring(reason))
2781 _continue_0 = true
2782 break
2783 end
2784 local data = ""
2785 for result, chunk in function()
2786 return pcall(response)
2787 end do
2788 if not result then
2789 log.error("Could not fetch '" .. tostring(repo) .. "' at '" .. tostring(repoData.repo) .. "': " .. tostring(chunk))
2790 data = false
2791 break
2792 else
2793 if not chunk then
2794 break
2795 end
2796 data = data .. chunk
2797 end
2798 end
2799 if data == false then
2800 _continue_0 = true
2801 break
2802 end
2803 if empty(data) then
2804 log.error("Could not fetch '" .. tostring(repo) .. "' at '" .. tostring(repoData.repo) .. "'")
2805 _continue_0 = true
2806 break
2807 end
2808 local repoPrograms
2809 repoPrograms, reason = unserialize(data)
2810 if not repoPrograms then
2811 log.error("Manifest '" .. tostring(repo) .. "' at '" .. tostring(repoData.repo) .. "' is malformed: " .. tostring(reason))
2812 _continue_0 = true
2813 break
2814 end
2815 for prg, prgData in pairs(repoPrograms) do
2816 local _continue_1 = false
2817 repeat
2818 if prg:match("[^A-Za-z0-9._-]") then
2819 log.error("Package name contains illegal characters: " .. tostring(repo) .. ":" .. tostring(prg) .. "!")
2820 _continue_1 = true
2821 break
2822 end
2823 insert(programs, {
2824 repo = repoData.repo,
2825 name = prg,
2826 data = prgData
2827 })
2828 _continue_1 = true
2829 until true
2830 if not _continue_1 then
2831 break
2832 end
2833 end
2834 end
2835 _continue_0 = true
2836 until true
2837 if not _continue_0 then
2838 break
2839 end
2840 end
2841 local newFiles = { }
2842 local newCache = { }
2843 for _index_0 = 1, #programs do
2844 local _des_0 = programs[_index_0]
2845 local name, repo, data
2846 name, repo, data = _des_0.name, _des_0.repo, _des_0.data
2847 if isin(concat(repo, name), newFiles) then
2848 log.error("There're multiple packages under the same name: " .. tostring(name) .. "!")
2849 end
2850 insert(newCache, {
2851 pkg = name,
2852 repo = repo,
2853 data = {
2854 name = name,
2855 repo = repo,
2856 data = data
2857 }
2858 })
2859 local k
2860 do
2861 for key, v in pairs(oldFiles) do
2862 if v.repo == repo and v.pkg == name then
2863 k = key
2864 break
2865 end
2866 end
2867 end
2868 if k then
2869 table.remove(oldFiles, k)
2870 else
2871 insert(newFiles, concat(repo, name))
2872 end
2873 end
2874 local file
2875 file, reason = io.open(cacheFile, "w")
2876 if not (file) then
2877 return false, "Could not open '" .. tostring(cacheFile) .. "' for writing: " .. tostring(reason)
2878 end
2879 do
2880 file:write(serialize({
2881 updated = getRealTime(),
2882 cache = newCache
2883 }))
2884 file:close()
2885 end
2886 log.print("- " .. tostring(#programs) .. " program" .. tostring(plural(#programs)) .. " cached.")
2887 log.print("- " .. tostring(#newFiles) .. " package" .. tostring(plural(#newFiles)) .. " " .. tostring(linkingVerb(#newFiles)) .. " new.")
2888 log.print("- " .. tostring(#oldFiles) .. " package" .. tostring(plural(#oldFiles)) .. " no longer exist" .. tostring(singular(#oldFiles)) .. ".")
2889 return true
2890 end
2891 self.parseLocalPath = function(self, prefix, lPath)
2892 if lPath:sub(1, 2) == "//" then
2893 return concat(prefix, lPath:sub(3))
2894 else
2895 return concat(prefix, "usr", lPath)
2896 end
2897 end
2898 self.rawInstall = function(self, name, prefix, isManuallyInstalled, save)
2899 if prefix == nil then
2900 prefix = "/"
2901 end
2902 if isManuallyInstalled == nil then
2903 isManuallyInstalled = false
2904 end
2905 if save == nil then
2906 save = false
2907 end
2908 local cacheList = try(self:listCache())
2909 cacheList = cacheList.cache
2910 local stats = {
2911 filesInstalled = 0,
2912 packagesInstalled = 0
2913 }
2914 if save and not existsDir(prefix) then
2915 local result, reason = makeDirectory(prefix)
2916 if not (result) then
2917 log.fatal("Failed to create '" .. tostring(prefix) .. "' directory for package '" .. tostring(name) .. "'! \n" .. tostring(reason))
2918 end
2919 elseif not save then
2920 local manifest = loadManifest(name, nil, "oppm")
2921 if manifest then
2922 log.print("'" .. tostring(name) .. "' is already installed, skipping...")
2923 return manifest, stats
2924 end
2925 end
2926 local manifest
2927 for _index_0 = 1, #cacheList do
2928 local package = cacheList[_index_0]
2929 local pkg, repo, data
2930 pkg, repo, data = package.pkg, package.repo, package.data
2931 if pkg == name then
2932 manifest = package
2933 break
2934 end
2935 end
2936 if not (manifest) then
2937 log.fatal("No such package: " .. tostring(name))
2938 end
2939 local files = { }
2940 local repo = manifest.repo
2941 for rPath, lPath in pairs(manifest.data.data.files) do
2942 local rFiles = { }
2943 if rPath:sub(1, 1) == ":" then
2944 rFiles = self:resolveDirectory(repo, rPath:sub(2, rPath:find("/") - 1, nil), rPath:sub(rPath:find("/") + 1))
2945 elseif rPath:sub(1, 1) == "?" then
2946 if exists(concat(self:parseLocalPath(prefix, lPath), fs.name(rPath))) then
2947 rFiles = { }
2948 else
2949 local remotePath = rPath:sub(2, -1)
2950 rFiles = {
2951 {
2952 name = fs.name(remotePath),
2953 path = remotePath,
2954 url = self.FILES:format(repo, remotePath)
2955 }
2956 }
2957 end
2958 else
2959 rFiles = {
2960 {
2961 name = fs.name(rPath),
2962 path = rPath,
2963 url = self.FILES:format(repo, rPath)
2964 }
2965 }
2966 end
2967 local name
2968 for _index_0 = 1, #rFiles do
2969 local _des_0 = rFiles[_index_0]
2970 local path, url
2971 name, path, url = _des_0.name, _des_0.path, _des_0.url
2972 local contents = try(recv(url))
2973 local localPath = self:parseLocalPath(prefix, lPath)
2974 if not (existsDir(localPath)) then
2975 makeDirectory(localPath)
2976 end
2977 do
2978 local file, reason = io.open(concat(localPath, name), "w")
2979 if not file then
2980 log.fatal("Could not open file for writing: " .. tostring(reason))
2981 end
2982 file:write(contents)
2983 file:close()
2984 end
2985 stats.filesInstalled = stats.filesInstalled + 1
2986 insert(files, {
2987 name = name,
2988 url = url,
2989 dir = localPath
2990 })
2991 end
2992 end
2993 local dependencies = { }
2994 if manifest.data.data.dependencies then
2995 for dep in pairs(manifest.data.data.dependencies) do
2996 insert(dependencies, {
2997 name = dep
2998 })
2999 end
3000 end
3001 stats.packagesInstalled = stats.packagesInstalled + 1
3002 return {
3003 name = name,
3004 files = files,
3005 dependencies = dependencies,
3006 manual = isManuallyInstalled
3007 }, stats
3008 end
3009 self.resolveDependencies = function(self, packages, isReinstalling, resolved, unresolved, result)
3010 if resolved == nil then
3011 resolved = { }
3012 end
3013 if unresolved == nil then
3014 unresolved = { }
3015 end
3016 if result == nil then
3017 result = { }
3018 end
3019 local cacheList = try(self:listCache())
3020 cacheList = cacheList.cache
3021 for _index_0 = 1, #packages do
3022 local name = packages[_index_0]
3023 local isResolved = false
3024 for _index_1 = 1, #resolved do
3025 local pkg = resolved[_index_1]
3026 if pkg == name then
3027 isResolved = true
3028 break
3029 end
3030 end
3031 if not (isResolved) then
3032 unresolved[name] = true
3033 local manifest = loadManifest(name, nil, "oppm")
3034 if not manifest then
3035 local data
3036 for _index_1 = 1, #cacheList do
3037 local package = cacheList[_index_1]
3038 local pkg
3039 pkg = package.pkg
3040 if pkg == name then
3041 data = package
3042 break
3043 end
3044 end
3045 if not (data) then
3046 return false, "Unknown package: " .. tostring(name)
3047 end
3048 if data.data.data.dependencies then
3049 for dep in pairs(data.data.data.dependencies) do
3050 isResolved = false
3051 for _index_1 = 1, #resolved do
3052 local pkg = resolved[_index_1]
3053 if pkg == dep then
3054 isResolved = true
3055 break
3056 end
3057 end
3058 if not (isResolved) then
3059 if unresolved[dep] then
3060 log.fatal("Circular dependencies detected: '" .. tostring(name) .. "' depends on '" .. tostring(dep) .. "', and '" .. tostring(dep) .. "' depends on '" .. tostring(name) .. "'.")
3061 end
3062 self:resolveDependencies({
3063 dep
3064 }, false, resolved, unresolved, result)
3065 end
3066 end
3067 end
3068 insert(result, name)
3069 else
3070 if isReinstalling then
3071 insert(result, name)
3072 end
3073 end
3074 insert(resolved, name)
3075 unresolved[name] = nil
3076 end
3077 end
3078 return result
3079 end
3080 self.getPackageDependants = function(self, packages, resolved, unresolved)
3081 if resolved == nil then
3082 resolved = { }
3083 end
3084 if unresolved == nil then
3085 unresolved = { }
3086 end
3087 for _index_0 = 1, #packages do
3088 local name = packages[_index_0]
3089 local isResolved = false
3090 for _index_1 = 1, #resolved do
3091 local pkg = resolved[_index_1]
3092 if pkg.name == name then
3093 isResolved = true
3094 break
3095 end
3096 end
3097 if not (isResolved) then
3098 insert(unresolved, {
3099 name = name
3100 })
3101 local manifest = loadManifest(name, nil, "oppm")
3102 if manifest then
3103 insert(resolved, {
3104 name = name,
3105 manifest = manifest
3106 })
3107 local list = try(listFiles(concat(distPath, "oppm")))
3108 for file in list do
3109 manifest = try(loadManifest(file, nil, "oppm"))
3110 local _list_0 = manifest.dependencies
3111 for _index_1 = 1, #_list_0 do
3112 local dep = _list_0[_index_1]
3113 if dep.name == name then
3114 isResolved = false
3115 for _index_2 = 1, #resolved do
3116 local pkg = resolved[_index_2]
3117 if pkg.name == file then
3118 isResolved = true
3119 break
3120 end
3121 end
3122 if not isResolved then
3123 for _index_2 = 1, #unresolved do
3124 local pkg = unresolved[_index_2]
3125 if pkg.name == file then
3126 log.fatal("Circular dependencies detected: " .. tostring(file))
3127 end
3128 end
3129 self:getPackageDependants({
3130 file
3131 }, resolved, unresolved)
3132 end
3133 end
3134 end
3135 end
3136 else
3137 log.fatal("Package " .. tostring(name) .. " is referenced as a dependant of another package, however, this package isn't installed.")
3138 end
3139 unresolved[#unresolved] = nil
3140 end
3141 end
3142 return resolved
3143 end
3144 self.whatDependsOn = function(self, name)
3145 local manifest = try(loadManifest(name, nil, "oppm"))
3146 local result = { }
3147 local list = try(listFiles(concat(distPath, "oppm")))
3148 for file in list do
3149 manifest = try(loadManifest(file, nil, "oppm"))
3150 local _list_0 = manifest.dependencies
3151 for _index_0 = 1, #_list_0 do
3152 local dep = _list_0[_index_0]
3153 if dep.name == name then
3154 insert(result, file)
3155 end
3156 end
3157 end
3158 return result
3159 end
3160 self.install = public(function(self, ...)
3161 local cacheList = try(self:listCache())
3162 self:notifyCacheUpdate(cacheList.updated)
3163 local packages = {
3164 ...
3165 }
3166 local reinstall = options.r or options.reinstall
3167 local save = options.s or options.save
3168 local dependencyGraph = try(self:resolveDependencies(packages, reinstall))
3169 pkgPlan({
3170 install = (function()
3171 local _accum_0 = { }
3172 local _len_0 = 1
3173 for _index_0 = 1, #dependencyGraph do
3174 local node = dependencyGraph[_index_0]
3175 if not reinstall or not isin(node, packages) then
3176 _accum_0[_len_0] = node
3177 _len_0 = _len_0 + 1
3178 end
3179 end
3180 return _accum_0
3181 end)(),
3182 reinstall = reinstall and (function()
3183 local _accum_0 = { }
3184 local _len_0 = 1
3185 for _index_0 = 1, #dependencyGraph do
3186 local node = dependencyGraph[_index_0]
3187 if isin(node, packages) then
3188 _accum_0[_len_0] = node
3189 _len_0 = _len_0 + 1
3190 end
3191 end
3192 return _accum_0
3193 end)() or nil
3194 })
3195 local stats = {
3196 filesInstalled = 0,
3197 packagesInstalled = 0
3198 }
3199 if reinstall then
3200 local manifests
3201 do
3202 local _accum_0 = { }
3203 local _len_0 = 1
3204 for _index_0 = 1, #packages do
3205 local name = packages[_index_0]
3206 _accum_0[_len_0] = try(loadManifest(name, nil, "oppm"))
3207 _len_0 = _len_0 + 1
3208 end
3209 manifests = _accum_0
3210 end
3211 self:_remove(manifests, true, false)
3212 end
3213 for _index_0 = 1, #dependencyGraph do
3214 local node = dependencyGraph[_index_0]
3215 log.print("Installing '" .. tostring(node) .. "'...")
3216 local prefix
3217 if save then
3218 prefix = "./" .. tostring(node) .. "/"
3219 else
3220 prefix = "/"
3221 end
3222 local manifest, statsPart = self:rawInstall(node, prefix, isin(node, packages), save)
3223 stats.filesInstalled = stats.filesInstalled + statsPart.filesInstalled
3224 stats.packagesInstalled = stats.packagesInstalled + statsPart.packagesInstalled
3225 if stats.packagesInstalled ~= 0 then
3226 local success, reason = saveManifest(manifest, "oppm")
3227 if success then
3228 log.info("Saved the manifest of '" .. tostring(manifest.name) .. "'.")
3229 else
3230 log.fatal("Couldn't save the manifest of '" .. tostring(manifest.name) .. "': " .. tostring(reason) .. ".")
3231 end
3232 end
3233 end
3234 log.print("- " .. tostring(stats.packagesInstalled) .. " package" .. tostring(plural(stats.packagesInstalled)) .. " installed.")
3235 log.print("- " .. tostring(stats.filesInstalled) .. " file" .. tostring(plural(stats.filesInstalled)) .. " installed.")
3236 return log.print("Done.")
3237 end)
3238 self.remove = public(function(self, ...)
3239 local packages = {
3240 ...
3241 }
3242 local manifests = { }
3243 for _index_0 = 1, #packages do
3244 local pkg = packages[_index_0]
3245 local manifest = try(loadManifest(pkg, nil, "oppm"))
3246 insert(manifests, manifest)
3247 end
3248 self:_remove(manifests, false)
3249 return log.print("Done.")
3250 end)
3251 self._remove = function(self, manifests, noPlan, removeDeps)
3252 if noPlan == nil then
3253 noPlan = false
3254 end
3255 if removeDeps == nil then
3256 removeDeps = true
3257 end
3258 local deps
3259 if not config.get("oppm", { }, true).get("remove_dependants", true) or not removeDeps then
3260 do
3261 local _accum_0 = { }
3262 local _len_0 = 1
3263 for _index_0 = 1, #manifests do
3264 local manifest = manifests[_index_0]
3265 _accum_0[_len_0] = {
3266 name = manifest.name,
3267 manifest = manifest
3268 }
3269 _len_0 = _len_0 + 1
3270 end
3271 deps = _accum_0
3272 end
3273 else
3274 deps = self:getPackageDependants((function()
3275 local _accum_0 = { }
3276 local _len_0 = 1
3277 for _index_0 = 1, #manifests do
3278 local manifest = manifests[_index_0]
3279 _accum_0[_len_0] = manifest.name
3280 _len_0 = _len_0 + 1
3281 end
3282 return _accum_0
3283 end)())
3284 end
3285 if not (noPlan) then
3286 pkgPlan({
3287 remove = (function()
3288 local _accum_0 = { }
3289 local _len_0 = 1
3290 for _index_0 = 1, #deps do
3291 local node = deps[_index_0]
3292 _accum_0[_len_0] = tostring(node.name)
3293 _len_0 = _len_0 + 1
3294 end
3295 return _accum_0
3296 end)()
3297 })
3298 end
3299 for _index_0 = 1, #deps do
3300 local dep = deps[_index_0]
3301 log.print("Removing '" .. tostring(dep.manifest.name) .. "' ...")
3302 try(_class_0.__parent.remove(self, dep.manifest, "oppm"))
3303 end
3304 return true
3305 end
3306 self.cache = public(function(self, command, ...)
3307 local _exp_0 = command
3308 if "update" == _exp_0 then
3309 log.print("Updating OpenPrograms program cache ...")
3310 try(self:updateCache())
3311 return log.print("Done.")
3312 else
3313 log.error("Unknown command.")
3314 return log.print("Usage: hpm oppm:cache update")
3315 end
3316 end)
3317 self.autoremove = public(function(self)
3318 local toRemove = { }
3319 local sorted = { }
3320 local list = try(listFiles(concat(distPath, "oppm")))
3321 for file in list do
3322 local manifest = try(loadManifest(file, nil, "oppm"))
3323 if not (manifest.manual) then
3324 local deps = self:getPackageDependants(file)
3325 if #deps == 1 then
3326 insert(toRemove, file)
3327 insert(sorted, file)
3328 end
3329 end
3330 end
3331 while true do
3332 local changed = false
3333 list = try(listFiles(concat(distPath, "oppm")))
3334 for file in list do
3335 if not (isin(file, toRemove)) then
3336 local manifest = try(loadManifest(file, nil, "oppm"))
3337 if not (manifest.manual) then
3338 local deps = self:getPackageDependants(file)
3339 table.remove(deps, 1)
3340 if all((function()
3341 local _accum_0 = { }
3342 local _len_0 = 1
3343 for _index_0 = 1, #deps do
3344 local x = deps[_index_0]
3345 _accum_0[_len_0] = isin(x.name, toRemove)
3346 _len_0 = _len_0 + 1
3347 end
3348 return _accum_0
3349 end)()) then
3350 for _index_0 = 1, #deps do
3351 local dep = deps[_index_0]
3352 local _, k = isin(dep.name, sorted)
3353 if k then
3354 table.remove(sorted, k)
3355 end
3356 end
3357 insert(toRemove, file)
3358 insert(sorted, file)
3359 changed = true
3360 end
3361 end
3362 end
3363 end
3364 if not (changed) then
3365 break
3366 end
3367 end
3368 pkgPlan({
3369 remove = (function()
3370 if #toRemove > 0 then
3371 local _accum_0 = { }
3372 local _len_0 = 1
3373 for _index_0 = 1, #toRemove do
3374 local name = toRemove[_index_0]
3375 _accum_0[_len_0] = "oppm:" .. tostring(name)
3376 _len_0 = _len_0 + 1
3377 end
3378 return _accum_0
3379 else
3380 return nil
3381 end
3382 end)()
3383 })
3384 for _index_0 = 1, #sorted do
3385 local name = sorted[_index_0]
3386 self:_remove({
3387 try(loadManifest(name, nil, "oppm"))
3388 }, false)
3389 end
3390 log.print("Done.")
3391 return true
3392 end)
3393 self.search = public(function(self, ...)
3394 local cache = try(self:listCache())
3395 self:notifyCacheUpdate(cache.updated)
3396 local list = try(cache.cache)
3397 local found = { }
3398 if ... then
3399 for _index_0 = 1, #list do
3400 local _des_0 = list[_index_0]
3401 local data
3402 data = _des_0.data
3403 local pkg = data.data
3404 local left = {
3405 ...
3406 }
3407 for i = #left, 1, -1 do
3408 local phrase = left[i]
3409 if data.name:find(phrase) then
3410 table.remove(left, i)
3411 break
3412 end
3413 if pkg.name and pkg.name:find(phrase) then
3414 table.remove(left, i)
3415 break
3416 end
3417 if pkg.description and pkg.description:find(phrase) then
3418 table.remove(left, i)
3419 break
3420 end
3421 if pkg.note and pkg.note:find(phrase) then
3422 table.remove(left, i)
3423 break
3424 end
3425 end
3426 if #left == 0 then
3427 insert(found, data)
3428 end
3429 end
3430 else
3431 do
3432 local _accum_0 = { }
3433 local _len_0 = 1
3434 for _index_0 = 1, #list do
3435 local x = list[_index_0]
3436 _accum_0[_len_0] = x.data
3437 _len_0 = _len_0 + 1
3438 end
3439 found = _accum_0
3440 end
3441 end
3442 for _index_0 = 1, #found do
3443 local pkg = found[_index_0]
3444 log.print(tostring(pkg.name) .. " - " .. tostring(pkg.data.name or pkg.name) .. ": " .. tostring(pkg.data.description))
3445 end
3446 end)
3447 self.info = public(function(self, name)
3448 if empty(name) then
3449 log.fatal("Usage: hpm oppm:info <package name>")
3450 end
3451 local list = try(self:listCache())
3452 list = list.cache
3453 local package = nil
3454 for _index_0 = 1, #list do
3455 local pkg = list[_index_0]
3456 if pkg.pkg == name then
3457 package = pkg
3458 break
3459 end
3460 end
3461 if not (package) then
3462 log.fatal("No such package.")
3463 end
3464 log.print("- Package name: " .. tostring(package.pkg))
3465 if package.data.data.name then
3466 log.print(" " .. tostring(package.data.data.name))
3467 end
3468 if package.data.data.description then
3469 log.print("- Description:\n" .. tostring(package.data.data.description))
3470 end
3471 if package.data.data.authors then
3472 log.print("- Authors:\n" .. tostring(package.data.data.authors))
3473 end
3474 if package.data.data.files then
3475 log.print("- Files: " .. tostring(tableLen(package.data.data.files)))
3476 end
3477 if package.data.data.dependencies then
3478 log.print("- Depends: " .. tostring(table.concat((function()
3479 local _accum_0 = { }
3480 local _len_0 = 1
3481 for x in pairs(package.data.data.dependencies) do
3482 _accum_0[_len_0] = x
3483 _len_0 = _len_0 + 1
3484 end
3485 return _accum_0
3486 end)(), " ")))
3487 end
3488 if package.data.data.note then
3489 log.print("- Note:\n" .. tostring(package.data.data.note))
3490 end
3491 return log.print("- Repository: https://github.com/" .. tostring(package.repo))
3492 end)
3493 if _parent_0.__inherited then
3494 _parent_0.__inherited(_parent_0, _class_0)
3495 end
3496 modules.oppm = _class_0
3497end
3498local printPackageList
3499printPackageList = function()
3500 local modList = try(listFiles(distPath))
3501 empty = true
3502 for modDir in modList do
3503 local mod = fs.name(modDir)
3504 if isDirectory(concat(distPath, mod)) then
3505 local list = try(listFiles(concat(distPath, mod)))
3506 for file in list do
3507 if not (isDirectory(concat(distPath, mod, file))) then
3508 local manifest = try(loadManifest(file, nil, mod))
3509 log.print(mod .. ":" .. file .. (manifest.version and " @ " .. manifest.version or ""))
3510 empty = false
3511 end
3512 end
3513 end
3514 end
3515 if empty then
3516 return log.print("No packages installed.")
3517 end
3518end
3519local parseArguments
3520parseArguments = function(...)
3521 args, options = parse(...)
3522 if #args < 1 then
3523 return printUsage()
3524 end
3525end
3526local process
3527process = function()
3528 local _exp_0 = args[1]
3529 if "list" == _exp_0 then
3530 return printPackageList()
3531 elseif "help" == _exp_0 then
3532 return printUsage()
3533 else
3534 do
3535 local cmd = findCustomCommand(args[1])
3536 if cmd then
3537 return cmd(unpack((function()
3538 local _accum_0 = { }
3539 local _len_0 = 1
3540 for _index_0 = 2, #args do
3541 local x = args[_index_0]
3542 _accum_0[_len_0] = x
3543 _len_0 = _len_0 + 1
3544 end
3545 return _accum_0
3546 end)()))
3547 end
3548 end
3549 end
3550end
3551env.semver = {
3552 Version = semver.Version,
3553 Spec = semver.Spec,
3554 SpecItem = semver.SpecItem,
3555 compare = semver.compare,
3556 match = semver.match,
3557 validate = semver.validate
3558}
3559env.json = json
3560env.CONFIG_PATH = CONFIG_PATH
3561env.USAGE = USAGE
3562env.DEFAULT_CONFIG = DEFAULT_CONFIG
3563env.options = options
3564env.args = args
3565env.request = request
3566env.modules = modules
3567env.config = config
3568env.modulePath = modulePath
3569env.distPath = distPath
3570env.exitCode = exitCode
3571env.log = log
3572env.assert = assert
3573env.unimplemented = unimplemented
3574env.printUsage = printUsage
3575env.try = try
3576env.checkType = checkType
3577env.argNumber = argNumber
3578env.argString = argString
3579env.isin = isin
3580env.tableLen = tableLen
3581env.empty = empty
3582env.all = all
3583env.existsDir = existsDir
3584env.existsFile = existsFile
3585env.plural = plural
3586env.singular = singular
3587env.linkingVerb = linkingVerb
3588env.remove = remove
3589env.loadConfig = loadConfig
3590env.checkInternet = checkInternet
3591env.download = download
3592env.findCustomCommand = findCustomCommand
3593env.getModuleBy = getModuleBy
3594env.callModuleMethod = callModuleMethod
3595env.saveManifest = saveManifest
3596env.loadManifest = loadManifest
3597env.removeManifest = removeManifest
3598env.public = public
3599env.wrapResponse = wrapResponse
3600env.recv = recv
3601env.confirm = confirm
3602env.pkgPlan = pkgPlan
3603env.printPackageList = printPackageList
3604env.parseArguments = parseArguments
3605env.process = process
3606for k, v in pairs(_G) do
3607 env[k] = v
3608end
3609parseArguments(...)
3610try(loadConfig())
3611loadCustomModules()
3612process()
3613return exitCode