Advertisement
pedrobneto3

git_clone

Jan 28th, 2023 (edited)
892
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 34.58 KB | Gaming | 0 0
  1. --- All credit goes to SquidDev
  2. -- Original post on cc forums: https://forums.computercraft.cc/index.php?topic=120.0
  3. -- Original code on his github: https://gist.github.com/SquidDev/e0f82765bfdefd48b0b15a5c06c0603b
  4.  
  5. -- As this was not currently on pastebin, I'm uploading and using for my own projects.
  6. -- Hope it's useful for everyone.
  7.  
  8. local preload = type(package) == "table" and type(package.preload) == "table" and package.preload or {}
  9.  
  10. local require = require
  11. if type(require) ~= "function" then
  12.     local loading = {}
  13.     local loaded = {}
  14.     require = function(name)
  15.         local result = loaded[name]
  16.  
  17.         if result ~= nil then
  18.             if result == loading then
  19.                 error("loop or previous error loading module '" .. name .. "'", 2)
  20.             end
  21.  
  22.             return result
  23.         end
  24.  
  25.         loaded[name] = loading
  26.         local contents = preload[name]
  27.         if contents then
  28.             result = contents(name)
  29.         else
  30.             error("cannot load '" .. name .. "'", 2)
  31.         end
  32.  
  33.         if result == nil then result = true end
  34.         loaded[name] = result
  35.         return result
  36.     end
  37. end
  38. preload["objects"] = function(...)
  39.     local inflate_zlib = require "deflate".inflate_zlib
  40.     local sha = require "metis.crypto.sha1"
  41.  
  42.     local band, bor, lshift, rshift = bit32.band, bit32.bor, bit32.lshift, bit32.rshift
  43.     local byte, format, sub = string.byte, string.format, string.sub
  44.  
  45.     local types = { [0] = "none", "commit", "tree", "blob", "tag", nil, "ofs_delta", "ref_delta", "any", "max" }
  46.  
  47.     --- Get the type of a specific object
  48.     -- @tparam Object x The object to get the type of
  49.     -- @treturn string The object's type.
  50.     local function get_type(x) return types[x.ty] or "?" end
  51.  
  52.     local event = ("luagit-%08x"):format(math.random(0, 2^24))
  53.     local function check_in()
  54.         os.queueEvent(event)
  55.         os.pullEvent(event)
  56.     end
  57.  
  58.     local sha_format = ("%02x"):rep(20)
  59.  
  60.     local function reader(str)
  61.         local expected_checksum = format(sha_format, byte(str, -20, -1))
  62.         local actual_checksum = sha(str:sub(1, -21));
  63.         if expected_checksum ~= actual_checksum then
  64.             error(("checksum mismatch: expected %s, got %s"):format(expected_checksum, actual_checksum))
  65.         end
  66.  
  67.         str = str:sub(1, -20)
  68.  
  69.         local pos = 1
  70.  
  71.         local function consume_read(len)
  72.             if len <= 0 then error("len < 0", 2) end
  73.             if pos > #str then error("end of stream") end
  74.  
  75.             local cur_pos = pos
  76.             pos = pos + len
  77.             local res = sub(str, cur_pos, pos - 1)
  78.             if #res ~= len then error("expected " .. len .. " bytes, got" .. #res) end
  79.             return res
  80.         end
  81.  
  82.         local function read8()
  83.             if pos > #str then error("end of stream") end
  84.             local cur_pos = pos
  85.             pos = pos + 1
  86.             return byte(str, cur_pos)
  87.         end
  88.  
  89.         return {
  90.             offset = function() return pos - 1 end,
  91.             read8 = read8,
  92.             read16 = function() return (read8() * (2^8)) + read8() end,
  93.             read32 = function() return (read8() * (2^24)) + (read8() * (2^16)) + (read8() * (2^8)) + read8() end,
  94.             read = consume_read,
  95.  
  96.             close = function()
  97.                 if pos ~= #str then error(("%d of %d bytes remaining"):format(#str - pos + 1, #str)) end
  98.             end,
  99.         }
  100.     end
  101.  
  102.     --- Consume a string from the given input buffer
  103.     --
  104.     -- @tparam Reader handle The handle to read from
  105.     -- @tparam number size The number of decompressed bytes to read
  106.     -- @treturn string The decompressed data
  107.     local function get_data(handle, size)
  108.         local tbl, n = {}, 1
  109.  
  110.         inflate_zlib {
  111.             input = handle.read8,
  112.             output = function(x) tbl[n], n = string.char(x), n + 1 end
  113.         }
  114.  
  115.         local res = table.concat(tbl)
  116.         if #res ~= size then error(("expected %d decompressed bytes, got %d"):format(size, #res)) end
  117.         return res
  118.     end
  119.  
  120.     --- Decode a binary delta file, applying it to the original
  121.     --
  122.     -- The format is described in more detail in [the Git documentation][git_pack]
  123.     --
  124.     -- [git_pack]: https://git-scm.com/docs/pack-format#_deltified_representation
  125.     --
  126.     -- @tparam string original The original string
  127.     -- @tparam string delta The binary delta
  128.     -- @treturn string The patched string
  129.     local function apply_delta(original, delta)
  130.         local delta_offset = 1
  131.         local function read_size()
  132.             local c = byte(delta, delta_offset)
  133.             delta_offset = delta_offset + 1
  134.  
  135.             local size = band(c, 0x7f)
  136.             local shift = 7
  137.             while band(c, 0x80) ~= 0 do
  138.                 c, delta_offset = byte(delta, delta_offset), delta_offset + 1
  139.                 size, shift = size + lshift(band(c, 0x7f), shift), shift + 7
  140.             end
  141.  
  142.             return size
  143.         end
  144.  
  145.         local original_length = read_size()
  146.         local patched_length = read_size()
  147.         if original_length ~= #original then
  148.             error(("expected original of size %d, got size %d"):format(original_length, #original))
  149.         end
  150.  
  151.         local parts, n = {}, 1
  152.         while delta_offset <= #delta do
  153.             local b = byte(delta, delta_offset)
  154.             delta_offset = delta_offset + 1
  155.  
  156.             if band(b, 0x80) ~= 0 then
  157.                 -- Copy from the original file. Each bit represents which optional length/offset
  158.                 -- bits are used.
  159.                 local offset, length = 0, 0
  160.  
  161.                 if band(b, 0x01) ~= 0 then
  162.                     offset, delta_offset = bor(offset, byte(delta, delta_offset)), delta_offset + 1
  163.                 end
  164.                 if band(b, 0x02) ~= 0 then
  165.                     offset, delta_offset = bor(offset, lshift(byte(delta, delta_offset), 8)), delta_offset + 1
  166.                 end
  167.                 if band(b, 0x04) ~= 0 then
  168.                     offset, delta_offset = bor(offset, lshift(byte(delta, delta_offset), 16)), delta_offset + 1
  169.                 end
  170.                 if band(b, 0x08) ~= 0 then
  171.                     offset, delta_offset = bor(offset, lshift(byte(delta, delta_offset), 24)), delta_offset + 1
  172.                 end
  173.  
  174.                 if band(b, 0x10) ~= 0 then
  175.                     length, delta_offset = bor(length, byte(delta, delta_offset)), delta_offset + 1
  176.                 end
  177.                 if band(b, 0x20) ~= 0 then
  178.                     length, delta_offset = bor(length, lshift(byte(delta, delta_offset), 8)), delta_offset + 1
  179.                 end
  180.                 if band(b, 0x40) ~= 0 then
  181.                     length, delta_offset = bor(length, lshift(byte(delta, delta_offset), 16)), delta_offset + 1
  182.                 end
  183.                 if length == 0 then length = 0x10000 end
  184.  
  185.                 parts[n], n = sub(original, offset + 1, offset + length), n + 1
  186.             elseif b > 0 then
  187.                 -- Copy from the delta. The opcode encodes the length
  188.                 parts[n], n = sub(delta, delta_offset, delta_offset + b - 1), n + 1
  189.                 delta_offset = delta_offset + b
  190.             else
  191.                 error(("unknown opcode '%02x'"):format(b))
  192.             end
  193.         end
  194.  
  195.         local patched = table.concat(parts)
  196.         if patched_length ~= #patched then
  197.             error(("expected patched of size %d, got size %d"):format(patched_length, #patched))
  198.         end
  199.  
  200.         return patched
  201.     end
  202.  
  203.     --- Unpack a single object, populating the output table
  204.     --
  205.     -- @tparam Reader handle The handle to read from
  206.     -- @tparam { [string] = Object } out The populated data
  207.     local function unpack_object(handle, out)
  208.         local c = handle.read8()
  209.         local ty = band(rshift(c, 4), 7)
  210.         local size = band(c, 15)
  211.         local shift = 4
  212.         while band(c, 0x80) ~= 0 do
  213.             c = handle.read8()
  214.             size = size + lshift(band(c, 0x7f), shift)
  215.             shift = shift + 7
  216.         end
  217.  
  218.         local data
  219.         if ty >= 1 and ty <= 4 then
  220.             -- commit/tree/blob/tag
  221.             data = get_data(handle, size)
  222.         elseif ty == 6 then
  223.             -- ofs_delta
  224.             data = get_data(handle, size)
  225.             error("ofs_delta not yet implemented")
  226.  
  227.         elseif ty == 7 then
  228.             -- ref_delta
  229.             local base_hash = sha_format:format(handle.read(20):byte(1, 20))
  230.             local delta = get_data(handle, size)
  231.  
  232.             local original = out[base_hash]
  233.             if not original then error(("cannot find object %d to apply diff"):format(base_hash)) return end
  234.             ty = original.ty
  235.             data = apply_delta(original.data, delta)
  236.         else
  237.             error(("unknown object of type '%d'"):format(ty))
  238.         end
  239.  
  240.         -- We've got to do these separately. Format doesn't like null bytes
  241.         local whole = ("%s %d\0"):format(types[ty], #data) .. data
  242.         local sha = sha(whole)
  243.         out[sha] = { ty = ty, data = data, sha = sha }
  244.     end
  245.  
  246.     local function unpack(handle, progress)
  247.         local header = handle.read(4)
  248.         if header ~= "PACK" then error("expected PACK, got " .. header, 0) end
  249.  
  250.         local version = handle.read32()
  251.         local entries = handle.read32()
  252.  
  253.         local out = {}
  254.         for i = 1, entries do
  255.             if progress then progress(i, entries) end
  256.             check_in()
  257.  
  258.             unpack_object(handle, out)
  259.         end
  260.  
  261.         return out
  262.     end
  263.  
  264.     local function build_tree(objects, object, prefix, out)
  265.         if not prefix then prefix = "" end
  266.         if not out then out = {} end
  267.  
  268.         local idx = 1
  269.  
  270.         while idx <= #object do
  271.             -- dddddd NAME\0<SHA>
  272.             local _, endidx, mode, name = object:find("^(%x+) ([^%z]+)%z", idx)
  273.             if not endidx then break end
  274.             name = prefix .. name
  275.  
  276.             local sha = object:sub(endidx + 1, endidx + 20):gsub(".", function(x) return ("%02x"):format(string.byte(x)) end)
  277.  
  278.             local entry = objects[sha]
  279.             if not entry then error(("cannot find %s %s (%s)"):format(mode, name, sha)) end
  280.  
  281.             if entry.ty == 3 then
  282.                 out[name] = entry.data
  283.             elseif entry.ty == 2 then
  284.                 build_tree(objects, entry.data, name .. "/", out)
  285.             else
  286.                 error("unknown type for " .. name .. " (" .. sha .. "): " .. get_type(entry))
  287.             end
  288.  
  289.             idx = endidx + 21
  290.         end
  291.  
  292.         return out
  293.     end
  294.  
  295.     local function build_commit(objects, sha)
  296.         local commit = objects[sha]
  297.         if not commit then error("cannot find commit " .. sha) end
  298.         if commit.ty ~= 1 then error("Expected commit, got " .. types[commit.ty]) end
  299.  
  300.         local tree_sha = commit.data:match("tree (%x+)\n")
  301.         if not tree_sha then error("Cannot find tree from commit") end
  302.  
  303.         local tree = objects[tree_sha]
  304.         if not tree then error("cannot find tree " .. tree_sha) end
  305.         if tree.ty ~= 2 then error("Expected tree, got " .. tree[tree.ty]) end
  306.  
  307.         return build_tree(objects, tree.data)
  308.     end
  309.  
  310.     return {
  311.         reader = reader,
  312.         unpack = unpack,
  313.         build_tree = build_tree,
  314.         build_commit = build_commit,
  315.         type = get_type,
  316.     }
  317. end
  318. preload["network"] = function(...)
  319.     local function pkt_line(msg)
  320.         return ("%04x%s\n"):format(5 + #msg, msg)
  321.     end
  322.  
  323.     local function pkt_linef(fmt, ...)
  324.         return pkt_line(fmt:format(...))
  325.     end
  326.  
  327.     local flush_line = "0000"
  328.  
  329.     local function read_pkt_line(handle)
  330.         local data = handle.read(4)
  331.         if data == nil or data == "" then return nil end
  332.  
  333.         local len = tonumber(data, 16)
  334.         if len == nil then
  335.             error(("read_pkt_line: cannot convert %q to a number"):format(data))
  336.         elseif len == 0 then
  337.             return false, data
  338.         else
  339.             return handle.read(len - 4), data
  340.         end
  341.     end
  342.  
  343.     local function fetch(url, lines, content_type)
  344.         if type(lines) == "table" then lines = table.concat(lines) end
  345.  
  346.         local ok, err = http.request(url, lines, {
  347.             ['User-Agent'] = 'CCGit/1.0',
  348.             ['Content-Type'] = content_type,
  349.         }, true)
  350.  
  351.         if ok then
  352.             while true do
  353.                 local event, event_url, param1, param2 = os.pullEvent()
  354.                 if event == "http_success" and event_url == url then
  355.                     return true, param1
  356.                 elseif event == "http_failure" and event_url == url then
  357.                     printError("Cannot fetch " .. url .. ": " .. param1)
  358.                     return false, param2
  359.                 end
  360.             end
  361.         else
  362.             printError("Cannot fetch " .. url .. ": " .. err)
  363.             return false, nil
  364.         end
  365.     end
  366.  
  367.     local function force_fetch(...)
  368.         local ok, handle, err_handle = fetch(...)
  369.         if not ok then
  370.             if err_handle then
  371.                 print(err_handle.getStatusCode())
  372.                 print(textutils.serialize(err_handle.getResponseHeaders()))
  373.                 print(err_handle.readAll())
  374.             end
  375.             error("Cannot fetch", 0)
  376.         end
  377.  
  378.         return handle
  379.     end
  380.  
  381.     local function receive(handle)
  382.         local out = {}
  383.         while true do
  384.             local line = read_pkt_line(handle)
  385.             if line == nil then break end
  386.             out[#out + 1] = line
  387.         end
  388.  
  389.         handle.close()
  390.         return out
  391.     end
  392.  
  393.     return {
  394.         read_pkt_line = read_pkt_line,
  395.         force_fetch = force_fetch,
  396.         receive = receive,
  397.  
  398.         pkt_linef = pkt_linef,
  399.         flush_line = flush_line,
  400.     }
  401. end
  402. preload["deflate"] = function(...)
  403.     --[[
  404.       (c) 2008-2011 David Manura.  Licensed under the same terms as Lua (MIT).
  405.  
  406.       Permission is hereby granted, free of charge, to any person obtaining a copy
  407.       of this software and associated documentation files (the "Software"), to deal
  408.       in the Software without restriction, including without limitation the rights
  409.       to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  410.       copies of the Software, and to permit persons to whom the Software is
  411.       furnished to do so, subject to the following conditions:
  412.  
  413.       The above copyright notice and this permission notice shall be included in
  414.       all copies or substantial portions of the Software.
  415.  
  416.       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  417.       IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  418.       FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  419.       AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  420.       LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  421.       OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  422.       THE SOFTWARE.
  423.       (end license)
  424.     --]]
  425.  
  426.     local assert, error, ipairs, pairs, tostring, type, setmetatable, io, math
  427.     = assert, error, ipairs, pairs, tostring, type, setmetatable, io, math
  428.     local table_sort, math_max, string_char = table.sort, math.max, string.char
  429.     local band, lshift, rshift = bit32.band, bit32.lshift, bit32.rshift
  430.  
  431.     local function make_outstate(outbs)
  432.         local outstate = {}
  433.         outstate.outbs = outbs
  434.         outstate.len = 0
  435.         outstate.window = {}
  436.         outstate.window_pos = 1
  437.         return outstate
  438.     end
  439.  
  440.  
  441.     local function output(outstate, byte)
  442.         local window_pos = outstate.window_pos
  443.         outstate.outbs(byte)
  444.         outstate.len = outstate.len + 1
  445.         outstate.window[window_pos] = byte
  446.         outstate.window_pos = window_pos % 32768 + 1  -- 32K
  447.     end
  448.  
  449.  
  450.     local function noeof(val)
  451.         return assert(val, 'unexpected end of file')
  452.     end
  453.  
  454.     local function memoize(f)
  455.         return setmetatable({}, {
  456.             __index = function(self, k)
  457.                 local v = f(k)
  458.                 self[k] = v
  459.                 return v
  460.             end
  461.         })
  462.     end
  463.  
  464.     -- small optimization (lookup table for powers of 2)
  465.     local pow2 = memoize(function(n) return 2^n end)
  466.  
  467.     local function bitstream_from_bytestream(bys)
  468.         local buf_byte = 0
  469.         local buf_nbit = 0
  470.         local o = { type = "bitstream" }
  471.  
  472.         function o:nbits_left_in_byte()
  473.             return buf_nbit
  474.         end
  475.  
  476.         function o:read(nbits)
  477.             nbits = nbits or 1
  478.             while buf_nbit < nbits do
  479.                 local byte = bys()
  480.                 if not byte then return end  -- note: more calls also return nil
  481.                 buf_byte = buf_byte + lshift(byte, buf_nbit)
  482.                 buf_nbit = buf_nbit + 8
  483.             end
  484.             local bits
  485.             if nbits == 0 then
  486.                 bits = 0
  487.             elseif nbits == 32 then
  488.                 bits = buf_byte
  489.                 buf_byte = 0
  490.             else
  491.                 bits = band(buf_byte, rshift(0xffffffff, 32 - nbits))
  492.                 buf_byte = rshift(buf_byte, nbits)
  493.             end
  494.             buf_nbit = buf_nbit - nbits
  495.             return bits
  496.         end
  497.  
  498.         return o
  499.     end
  500.  
  501.     local function get_bitstream(o)
  502.         if type(o) == "table" and o.type == "bitstream" then
  503.             return o
  504.         elseif io.type(o) == 'file' then
  505.             return bitstream_from_bytestream(function() local sb = o:read(1) if sb then return sb:byte() end end)
  506.         elseif type(o) == "function" then
  507.             return bitstream_from_bytestream(o)
  508.         else
  509.             error 'unrecognized type'
  510.         end
  511.     end
  512.  
  513.  
  514.     local function get_obytestream(o)
  515.         local bs
  516.         if io.type(o) == 'file' then
  517.             bs = function(sbyte) o:write(string_char(sbyte)) end
  518.         elseif type(o) == 'function' then
  519.             bs = o
  520.         else
  521.             error('unrecognized type: ' .. tostring(o))
  522.         end
  523.         return bs
  524.     end
  525.  
  526.  
  527.     local function HuffmanTable(init, is_full)
  528.         local t = {}
  529.         if is_full then
  530.             for val,nbits in pairs(init) do
  531.                 if nbits ~= 0 then
  532.                     t[#t+1] = {val=val, nbits=nbits}
  533.                 end
  534.             end
  535.         else
  536.             for i=1,#init-2,2 do
  537.                 local firstval, nbits, nextval = init[i], init[i+1], init[i+2]
  538.                 if nbits ~= 0 then
  539.                     for val=firstval,nextval-1 do
  540.                         t[#t+1] = {val=val, nbits=nbits}
  541.                     end
  542.                 end
  543.             end
  544.         end
  545.         table_sort(t, function(a,b)
  546.             return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits
  547.         end)
  548.  
  549.         -- assign codes
  550.         local code = 1  -- leading 1 marker
  551.         local nbits = 0
  552.         for _, s in ipairs(t) do
  553.             if s.nbits ~= nbits then
  554.                 code = code * pow2[s.nbits - nbits]
  555.                 nbits = s.nbits
  556.             end
  557.             s.code = code
  558.             code = code + 1
  559.         end
  560.  
  561.         local minbits = math.huge
  562.         local look = {}
  563.         for _, s in ipairs(t) do
  564.             minbits = math.min(minbits, s.nbits)
  565.             look[s.code] = s.val
  566.         end
  567.  
  568.         local msb = function(bits, nbits)
  569.             local res = 0
  570.             for _ = 1, nbits do
  571.                 res = lshift(res, 1) + band(bits, 1)
  572.                 bits = rshift(bits, 1)
  573.             end
  574.             return res
  575.         end
  576.  
  577.         local tfirstcode = memoize(
  578.                 function(bits) return pow2[minbits] + msb(bits, minbits) end)
  579.  
  580.         function t:read(bs)
  581.             local code = 1 -- leading 1 marker
  582.             local nbits = 0
  583.             while 1 do
  584.                 if nbits == 0 then  -- small optimization (optional)
  585.                     code = tfirstcode[noeof(bs:read(minbits))]
  586.                     nbits = nbits + minbits
  587.                 else
  588.                     local b = noeof(bs:read())
  589.                     nbits = nbits + 1
  590.                     code = code * 2 + b   -- MSB first
  591.                 end
  592.                 local val = look[code]
  593.                 if val then
  594.                     return val
  595.                 end
  596.             end
  597.         end
  598.  
  599.         return t
  600.     end
  601.  
  602.     local function parse_zlib_header(bs)
  603.         local cm = bs:read(4) -- Compression Method
  604.         local cinfo = bs:read(4) -- Compression info
  605.         local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG)
  606.         local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary)
  607.         local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level)
  608.         local cmf = cinfo * 16  + cm -- CMF (Compresion Method and flags)
  609.         local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs
  610.  
  611.         if cm ~= 8 then -- not "deflate"
  612.             error("unrecognized zlib compression method: " .. cm)
  613.         end
  614.         if cinfo > 7 then
  615.             error("invalid zlib window size: cinfo=" .. cinfo)
  616.         end
  617.         local window_size = 2^(cinfo + 8)
  618.  
  619.         if (cmf*256 + flg) %  31 ~= 0 then
  620.             error("invalid zlib header (bad fcheck sum)")
  621.         end
  622.  
  623.         if fdict == 1 then
  624.             error("FIX:TODO - FDICT not currently implemented")
  625.             local dictid_ = bs:read(32)
  626.         end
  627.  
  628.         return window_size
  629.     end
  630.  
  631.     local function parse_huffmantables(bs)
  632.         local hlit = bs:read(5)  -- # of literal/length codes - 257
  633.         local hdist = bs:read(5) -- # of distance codes - 1
  634.         local hclen = noeof(bs:read(4)) -- # of code length codes - 4
  635.  
  636.         local ncodelen_codes = hclen + 4
  637.         local codelen_init = {}
  638.         local codelen_vals = {
  639.             16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
  640.         for i=1,ncodelen_codes do
  641.             local nbits = bs:read(3)
  642.             local val = codelen_vals[i]
  643.             codelen_init[val] = nbits
  644.         end
  645.         local codelentable = HuffmanTable(codelen_init, true)
  646.  
  647.         local function decode(ncodes)
  648.             local init = {}
  649.             local nbits
  650.             local val = 0
  651.             while val < ncodes do
  652.                 local codelen = codelentable:read(bs)
  653.                 --FIX:check nil?
  654.                 local nrepeat
  655.                 if codelen <= 15 then
  656.                     nrepeat = 1
  657.                     nbits = codelen
  658.                 elseif codelen == 16 then
  659.                     nrepeat = 3 + noeof(bs:read(2))
  660.                     -- nbits unchanged
  661.                 elseif codelen == 17 then
  662.                     nrepeat = 3 + noeof(bs:read(3))
  663.                     nbits = 0
  664.                 elseif codelen == 18 then
  665.                     nrepeat = 11 + noeof(bs:read(7))
  666.                     nbits = 0
  667.                 else
  668.                     error 'ASSERT'
  669.                 end
  670.                 for _ = 1, nrepeat do
  671.                     init[val] = nbits
  672.                     val = val + 1
  673.                 end
  674.             end
  675.             local huffmantable = HuffmanTable(init, true)
  676.             return huffmantable
  677.         end
  678.  
  679.         local nlit_codes = hlit + 257
  680.         local ndist_codes = hdist + 1
  681.  
  682.         local littable = decode(nlit_codes)
  683.         local disttable = decode(ndist_codes)
  684.  
  685.         return littable, disttable
  686.     end
  687.  
  688.  
  689.     local tdecode_len_base
  690.     local tdecode_len_nextrabits
  691.     local tdecode_dist_base
  692.     local tdecode_dist_nextrabits
  693.     local function parse_compressed_item(bs, outstate, littable, disttable)
  694.         local val = littable:read(bs)
  695.         if val < 256 then -- literal
  696.             output(outstate, val)
  697.         elseif val == 256 then -- end of block
  698.             return true
  699.         else
  700.             if not tdecode_len_base then
  701.                 local t = {[257]=3}
  702.                 local skip = 1
  703.                 for i=258,285,4 do
  704.                     for j=i,i+3 do t[j] = t[j-1] + skip end
  705.                     if i ~= 258 then skip = skip * 2 end
  706.                 end
  707.                 t[285] = 258
  708.                 tdecode_len_base = t
  709.             end
  710.             if not tdecode_len_nextrabits then
  711.                 local t = {}
  712.                 for i=257,285 do
  713.                     local j = math_max(i - 261, 0)
  714.                     t[i] = rshift(j, 2)
  715.                 end
  716.                 t[285] = 0
  717.                 tdecode_len_nextrabits = t
  718.             end
  719.             local len_base = tdecode_len_base[val]
  720.             local nextrabits = tdecode_len_nextrabits[val]
  721.             local extrabits = bs:read(nextrabits)
  722.             local len = len_base + extrabits
  723.  
  724.             if not tdecode_dist_base then
  725.                 local t = {[0]=1}
  726.                 local skip = 1
  727.                 for i=1,29,2 do
  728.                     for j=i,i+1 do t[j] = t[j-1] + skip end
  729.                     if i ~= 1 then skip = skip * 2 end
  730.                 end
  731.                 tdecode_dist_base = t
  732.             end
  733.             if not tdecode_dist_nextrabits then
  734.                 local t = {}
  735.                 for i=0,29 do
  736.                     local j = math_max(i - 2, 0)
  737.                     t[i] = rshift(j, 1)
  738.                 end
  739.                 tdecode_dist_nextrabits = t
  740.             end
  741.             local dist_val = disttable:read(bs)
  742.             local dist_base = tdecode_dist_base[dist_val]
  743.             local dist_nextrabits = tdecode_dist_nextrabits[dist_val]
  744.             local dist_extrabits = bs:read(dist_nextrabits)
  745.             local dist = dist_base + dist_extrabits
  746.  
  747.             for _ = 1,len do
  748.                 local pos = (outstate.window_pos - 1 - dist) % 32768 + 1  -- 32K
  749.                 output(outstate, assert(outstate.window[pos], 'invalid distance'))
  750.             end
  751.         end
  752.         return false
  753.     end
  754.  
  755.  
  756.     local function parse_block(bs, outstate)
  757.         local bfinal = bs:read(1)
  758.         local btype = bs:read(2)
  759.  
  760.         local BTYPE_NO_COMPRESSION = 0
  761.         local BTYPE_FIXED_HUFFMAN = 1
  762.         local BTYPE_DYNAMIC_HUFFMAN = 2
  763.         local _BTYPE_RESERVED = 3
  764.  
  765.         if btype == BTYPE_NO_COMPRESSION then
  766.             bs:read(bs:nbits_left_in_byte())
  767.             local len = bs:read(16)
  768.             local _nlen = noeof(bs:read(16))
  769.  
  770.             for i=1,len do
  771.                 local by = noeof(bs:read(8))
  772.                 output(outstate, by)
  773.             end
  774.         elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then
  775.             local littable, disttable
  776.             if btype == BTYPE_DYNAMIC_HUFFMAN then
  777.                 littable, disttable = parse_huffmantables(bs)
  778.             else
  779.                 littable  = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil}
  780.                 disttable = HuffmanTable {0,5, 32,nil}
  781.             end
  782.  
  783.             repeat
  784.                 local is_done = parse_compressed_item(
  785.                         bs, outstate, littable, disttable)
  786.             until is_done
  787.         else
  788.             error('unrecognized compression type '..btype)
  789.         end
  790.  
  791.         return bfinal ~= 0
  792.     end
  793.  
  794.  
  795.     local function inflate(t)
  796.         local bs = get_bitstream(t.input)
  797.         local outbs = get_obytestream(t.output)
  798.         local outstate = make_outstate(outbs)
  799.  
  800.         repeat
  801.             local is_final = parse_block(bs, outstate)
  802.         until is_final
  803.     end
  804.  
  805.     local function adler32(byte, crc)
  806.         local s1 = crc % 65536
  807.         local s2 = (crc - s1) / 65536
  808.         s1 = (s1 + byte) % 65521
  809.         s2 = (s2 + s1) % 65521
  810.         return s2*65536 + s1
  811.     end -- 65521 is the largest prime smaller than 2^16
  812.  
  813.     local function inflate_zlib(t)
  814.         local bs = get_bitstream(t.input)
  815.         local outbs = get_obytestream(t.output)
  816.         local disable_crc = t.disable_crc
  817.         if disable_crc == nil then disable_crc = false end
  818.  
  819.         local _window_size = parse_zlib_header(bs)
  820.  
  821.         local data_adler32 = 1
  822.  
  823.         inflate {
  824.             input=bs,
  825.             output = disable_crc and outbs or function(byte)
  826.                 data_adler32 = adler32(byte, data_adler32)
  827.                 outbs(byte)
  828.             end,
  829.             len = t.len,
  830.         }
  831.  
  832.         bs:read(bs:nbits_left_in_byte())
  833.  
  834.         local b3 = bs:read(8)
  835.         local b2 = bs:read(8)
  836.         local b1 = bs:read(8)
  837.         local b0 = bs:read(8)
  838.         local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0
  839.  
  840.         if not disable_crc then
  841.             if data_adler32 ~= expected_adler32 then
  842.                 error('invalid compressed data--crc error')
  843.             end
  844.         end
  845.     end
  846.  
  847.     return {
  848.         inflate = inflate,
  849.         inflate_zlib = inflate_zlib,
  850.     }
  851. end
  852. preload["clone"] = function(...)
  853.     --- Git clone in Lua, from the bottom up
  854.     --
  855.     -- http://stefan.saasen.me/articles/git-clone-in-haskell-from-the-bottom-up/#the_clone_process
  856.     -- https://github.com/creationix/lua-git
  857.  
  858.     do -- metis loader
  859.         local modules = {
  860.             ["metis.argparse"] = "src/metis/argparse.lua",
  861.             ["metis.crypto.sha1"] = "src/metis/crypto/sha1.lua",
  862.             ["metis.timer"] = "src/metis/timer.lua",
  863.         }
  864.         package.loaders[#package.loaders + 1] = function(name)
  865.             local path = modules[name]
  866.             if not path then return nil, "not a metis module" end
  867.  
  868.             local local_path = "/.cache/metis/ae11085f261e5b506654162c80d21954c0d54e5e/" .. path
  869.             if not fs.exists(local_path) then
  870.                 local url = "https://raw.githubusercontent.com/SquidDev-CC/metis/ae11085f261e5b506654162c80d21954c0d54e5e/" .. path
  871.                 local request, err = http.get(url)
  872.                 if not request then return nil, "Cannot download " .. url .. ": " .. err end
  873.  
  874.                 local out = fs.open(local_path, "w")
  875.                 out.write(request.readAll())
  876.                 out.close()
  877.  
  878.                 request.close()
  879.             end
  880.  
  881.  
  882.             local fn, err = loadfile(local_path, nil, _ENV)
  883.             if fn then return fn, local_path else return nil, err end
  884.         end
  885.     end
  886.  
  887.     local network = require "network"
  888.     local objects = require "objects"
  889.  
  890.     local varargs = {...}
  891.     local url, name, branch = nil, nil, nil
  892.  
  893.     for index, value in ipairs(varargs) do
  894.         if string.match(value, "https://.*") then
  895.             url = value
  896.         elseif value == "-b" or value == "--branch" then
  897.             branch = varargs[index + 1]
  898.         elseif index > 0 and varargs[index - 1] ~= "-b" and varargs[index - 1] ~= "--branch" then
  899.             name = value
  900.         end
  901.     end
  902.  
  903.     if #varargs == 0 or varargs[0] == "-h" or varargs[0] == "--help" or not url then
  904.         error("Usages:\n    git_clone.lua URL\n    git_clone.lua URL [name]\n    git_clone.lua URL [name]\n    git_clone.lua URL -b [branch]\n    git_clone.lua URL [name] --branch [branch]", 0)
  905.     end
  906.  
  907.     if url:sub(-1) == "/" then url = url:sub(1, -2) end
  908.     name = name or fs.getName(url):gsub("%.git$", "")
  909.  
  910.     local destination = shell.resolve(name)
  911.     if fs.exists(destination) then
  912.         error(("%q already exists"):format(name), 0)
  913.     end
  914.  
  915.     local function report(msg)
  916.         local last = ""
  917.         for line in msg:gmatch("[^\n]+") do last = line end
  918.         term.setCursorPos(1, select(2, term.getCursorPos()))
  919.         term.clearLine()
  920.         term.write(last)
  921.     end
  922.  
  923.     local head
  924.     do -- Request a list of all refs
  925.         report("Cloning from " .. url)
  926.  
  927.         local handle = network.force_fetch(url .. "/info/refs?service=git-upload-pack")
  928.         local res = network.receive(handle)
  929.  
  930.         local sha_ptrn = ("%x"):rep(40)
  931.  
  932.         local caps = {}
  933.         local refs = {}
  934.         for i = 1, #res do
  935.             local line = res[i]
  936.             if line ~= false and line:sub(1, 1) ~= "#" then
  937.                 local sha, name = line:match("(" .. sha_ptrn .. ") ([^%z\n]+)")
  938.                 if sha and name then
  939.                     refs[name] = sha
  940.  
  941.                     local capData = line:match("%z([^\n]+)\n")
  942.                     if capData then
  943.                         for cap in (capData .. " "):gmatch("%S+") do
  944.                             local eq = cap:find("=")
  945.                             if eq then
  946.                                 caps[cap:sub(1, eq - 1)] = cap:sub(eq + 1)
  947.                             else
  948.                                 caps[cap] = true
  949.                             end
  950.                         end
  951.                     end
  952.                 else
  953.                     printError("Unexpected line: " .. line)
  954.                 end
  955.             end
  956.         end
  957.  
  958.         if branch then
  959.             head = refs['refs/heads/' .. branch] or error("Cannot find " .. branch, 0)
  960.         else
  961.             head = refs['HEAD'] or refs['refs/heads/master'] or refs['refs/heads/main'] or error("Cannot find " .. branch, 0)
  962.         end
  963.  
  964.         if not caps['shallow'] then error("Server does not support shallow fetching", 0) end
  965.  
  966.         -- TODO: Handle both. We don't even need the side-band really?
  967.         if not caps['side-band-64k'] then error("Server does not support side band", 0) end
  968.     end
  969.  
  970.     do -- Now actually perform the clone
  971.         local handle = network.force_fetch(url .. "/git-upload-pack", {
  972.             network.pkt_linef("want %s side-band-64k shallow", head),
  973.             network.pkt_linef("deepen 1"),
  974.             network.flush_line,
  975.             network.pkt_linef("done"),
  976.         }, "application/x-git-upload-pack-request")
  977.  
  978.         local pack, head = {}, nil
  979.         while true do
  980.             local line = network.read_pkt_line(handle)
  981.             if line == nil then break end
  982.  
  983.             if line == false or line == "NAK\n" then
  984.                 -- Skip
  985.             elseif line:byte(1) == 1 then
  986.                 table.insert(pack, line:sub(2))
  987.             elseif line:byte(1) == 2 or line:byte(1) == 3 then
  988.                 report(line:sub(2):gsub("\r", "\n"))
  989.             elseif line:find("^shallow ") then
  990.                 head = line:sub(#("shallow ") + 1)
  991.             else
  992.                 printError("Unknown line: " .. tostring(line))
  993.             end
  994.         end
  995.         handle.close()
  996.  
  997.         local stream = objects.reader(table.concat(pack))
  998.         local objs = objects.unpack(stream, function(x, n)
  999.             report(("Extracting %d/%d (%.2f%%)"):format(x, n, x/n*100))
  1000.         end)
  1001.         stream.close()
  1002.  
  1003.         if not head then error("Cannot find HEAD commit", 0) end
  1004.  
  1005.         for k, v in pairs(objects.build_commit(objs, head)) do
  1006.             local out = fs.open(fs.combine(destination, fs.combine(k, "")), "wb")
  1007.             out.write(v)
  1008.             out.close()
  1009.         end
  1010.     end
  1011.  
  1012.     report(("Cloned to %q"):format(name))
  1013.     print()
  1014. end
  1015. return preload["clone"](...)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement