Advertisement
Guest User

df

a guest
Dec 11th, 2019
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.32 KB | None | 0 0
  1.  
  2. local paths = require("Paths")
  3. local event = require("Event")
  4.  
  5. --------------------------------------------------------------------------------
  6.  
  7. local filesystem = {
  8.     SORTING_NAME = 1,
  9.     SORTING_TYPE = 2,
  10.     SORTING_DATE = 3,
  11. }
  12.  
  13. local BUFFER_SIZE = 1024
  14. local BOOT_PROXY
  15.  
  16. local mountedProxies = {}
  17.  
  18. --------------------------------------- String-related path processing -----------------------------------------
  19.  
  20. function filesystem.path(path)
  21.     return path:match("^(.+%/).") or ""
  22. end
  23.  
  24. function filesystem.name(path)
  25.     return path:match("%/?([^%/]+%/?)$")
  26. end
  27.  
  28. function filesystem.extension(path, lower)
  29.     return path:match("[^%/]+(%.[^%/]+)%/?$")
  30. end
  31.  
  32. function filesystem.hideExtension(path)
  33.     return path:match("(.+)%..+") or path
  34. end
  35.  
  36. function filesystem.isHidden(path)
  37.     if path:sub(1, 1) == "." then
  38.         return true
  39.     end
  40.  
  41.     return false
  42. end
  43.  
  44. function filesystem.removeSlashes(path)
  45.     return path:gsub("/+", "/")
  46. end
  47.  
  48. --------------------------------------- Mounted filesystem support -----------------------------------------
  49.  
  50. function filesystem.mount(cyka, path)  
  51.     if type(cyka) == "table" then
  52.         for i = 1, #mountedProxies do
  53.             if mountedProxies[i].path == path then
  54.                 return false, "mount path has been taken by other mounted filesystem"
  55.             elseif mountedProxies[i].proxy == cyka then
  56.                 return false, "proxy is already mounted"
  57.             end
  58.         end
  59.  
  60.         table.insert(mountedProxies, {
  61.             path = path,
  62.             proxy = cyka
  63.         })
  64.  
  65.         return true
  66.     else
  67.         error("bad argument #1 (filesystem proxy expected, got " .. tostring(cyka) .. ")")
  68.     end
  69. end
  70.  
  71. function filesystem.unmount(cyka)
  72.     if type(cyka) == "table" then
  73.         for i = 1, #mountedProxies do
  74.             if mountedProxies[i].proxy == cyka then
  75.                 table.remove(mountedProxies, i)
  76.                 return true
  77.             end
  78.         end
  79.  
  80.         return false, "specified proxy is not mounted"
  81.     elseif type(cyka) == "string" then
  82.         for i = 1, #mountedProxies do
  83.             if mountedProxies[i].proxy.address == cyka then
  84.                 table.remove(mountedProxies, i)
  85.                 return true
  86.             end
  87.         end
  88.        
  89.         return false, "specified proxy address is not mounted"
  90.     else
  91.         error("bad argument #1 (filesystem proxy or mounted path expected, got " .. tostring(cyka) .. ")")
  92.     end
  93. end
  94.  
  95. function filesystem.get(path)
  96.     checkArg(1, path, "string")
  97.  
  98.     for i = 1, #mountedProxies do
  99.         if path:sub(1, unicode.len(mountedProxies[i].path)) == mountedProxies[i].path then
  100.             return mountedProxies[i].proxy, unicode.sub(path, mountedProxies[i].path:len() + 1, -1)
  101.         end
  102.     end
  103.  
  104.     return BOOT_PROXY, path
  105. end
  106.  
  107. function filesystem.mounts()
  108.     local key, value
  109.     return function()
  110.         key, value = next(mountedProxies, key)
  111.         if value then
  112.             return value.proxy, value.path
  113.         end
  114.     end
  115. end
  116.  
  117. --------------------------------------- I/O methods -----------------------------------------
  118.  
  119. local function readString(self, count)
  120.     -- If current buffer content is a "part" of "count of data" we need to read
  121.     if count > #self.buffer then
  122.         local data, chunk = self.buffer
  123.  
  124.         while #data < count do
  125.             chunk = self.proxy.read(self.stream, BUFFER_SIZE)
  126.  
  127.             if chunk then
  128.                 data = data .. chunk
  129.             else
  130.                 self.position = self:seek("end", 0)
  131.  
  132.                 -- EOF at start
  133.                 if data == "" then
  134.                     return nil
  135.                 -- EOF after read
  136.                 else
  137.                     return data
  138.                 end
  139.             end
  140.         end
  141.  
  142.         self.buffer = data:sub(count + 1, -1)
  143.         chunk = data:sub(1, count)
  144.         self.position = self.position + #chunk
  145.  
  146.         return chunk
  147.     else
  148.         local data = self.buffer:sub(1, count)
  149.         self.buffer = self.buffer:sub(count + 1, -1)
  150.         self.position = self.position + count
  151.  
  152.         return data
  153.     end
  154. end
  155.  
  156. local function readLine(self)
  157.     local data = ""
  158.     while true do
  159.         if #self.buffer > 0 then
  160.             local starting, ending = self.buffer:find("\n")
  161.             if starting then
  162.                 local chunk = self.buffer:sub(1, starting - 1)
  163.                 self.buffer = self.buffer:sub(ending + 1, -1)
  164.                 self.position = self.position + #chunk
  165.  
  166.                 return data .. chunk
  167.             else
  168.                 data = data .. self.buffer
  169.             end
  170.         end
  171.  
  172.         local chunk = self.proxy.read(self.stream, BUFFER_SIZE)
  173.         if chunk then
  174.             self.buffer = chunk
  175.             self.position = self.position + #chunk
  176.         -- EOF
  177.         else
  178.             local data = self.buffer
  179.             self.position = self:seek("end", 0)
  180.  
  181.             return #data > 0 and data or nil
  182.         end
  183.     end
  184. end
  185.  
  186. local function lines(self)
  187.     return function()
  188.         local line = readLine(self)
  189.         if line then
  190.             return line
  191.         else
  192.             self:close()
  193.         end
  194.     end
  195. end
  196.  
  197. local function readAll(self)
  198.     local data, chunk = ""
  199.     while true do
  200.         chunk = self.proxy.read(self.stream, 4096)
  201.         if chunk then
  202.             data = data .. chunk
  203.         -- EOF
  204.         else
  205.             self.position = self:seek("end", 0)
  206.             return data
  207.         end
  208.     end
  209. end
  210.  
  211. local function readBytes(self, count, littleEndian)
  212.     if count == 1 then
  213.         local data = readString(self, 1)
  214.         if data then
  215.             return string.byte(data)
  216.         end
  217.  
  218.         return nil
  219.     else
  220.         local bytes, result = {string.byte(readString(self, count) or "\x00", 1, 8)}, 0
  221.  
  222.         if littleEndian then
  223.             for i = #bytes, 1, -1 do
  224.                 result = bit32.bor(bit32.lshift(result, 8), bytes[i])
  225.             end
  226.         else
  227.             for i = 1, #bytes do
  228.                 result = bit32.bor(bit32.lshift(result, 8), bytes[i])
  229.             end
  230.         end
  231.  
  232.         return result
  233.     end
  234. end
  235.  
  236. local function readUnicodeChar(self)
  237.     local byteArray = {string.byte(readString(self, 1))}
  238.  
  239.     local nullBitPosition = 0
  240.     for i = 1, 7 do
  241.         if bit32.band(bit32.rshift(byteArray[1], 8 - i), 0x1) == 0x0 then
  242.             nullBitPosition = i
  243.             break
  244.         end
  245.     end
  246.  
  247.     for i = 1, nullBitPosition - 2 do
  248.         table.insert(byteArray, string.byte(readString(self, 1)))
  249.     end
  250.  
  251.     return string.char(table.unpack(byteArray))
  252. end
  253.  
  254. local function read(self, format, ...)
  255.     local formatType = type(format)
  256.     if formatType == "number" then 
  257.         return readString(self, format)
  258.     elseif formatType == "string" then
  259.         format = format:gsub("^%*", "")
  260.  
  261.         if format == "a" then
  262.             return readAll(self)
  263.         elseif format == "l" then
  264.             return readLine(self)
  265.         elseif format == "b" then
  266.             return readBytes(self, 1)
  267.         elseif format == "bs" then
  268.             return readBytes(self, ...)
  269.         elseif format == "u" then
  270.             return readUnicodeChar(self)
  271.         else
  272.             error("bad argument #2 ('a' (whole file), 'l' (line), 'u' (unicode char), 'b' (byte as number) or 'bs' (sequence of n bytes as number) expected, got " .. format .. ")")
  273.         end
  274.     else
  275.         error("bad argument #1 (number or string expected, got " .. formatType ..")")
  276.     end
  277. end
  278.  
  279. local function seek(self, pizda, cyka)
  280.     if pizda == "set" then
  281.         local result, reason = self.proxy.seek(self.stream, "set", cyka)
  282.         if result then
  283.             self.position = result
  284.             self.buffer = ""
  285.         end
  286.  
  287.         return result, reason
  288.     elseif pizda == "cur" then
  289.         local result, reason = self.proxy.seek(self.stream, "set", self.position + cyka)
  290.         if result then
  291.             self.position = result
  292.             self.buffer = ""
  293.         end
  294.  
  295.         return result, reason
  296.     elseif pizda == "end" then
  297.         local result, reason = self.proxy.seek(self.stream, "end", cyka)
  298.         if result then
  299.             self.position = result
  300.             self.buffer = ""
  301.         end
  302.  
  303.         return result, reason
  304.     else
  305.         error("bad argument #2 ('set', 'cur' or 'end' expected, got " .. tostring(whence) .. ")")
  306.     end
  307. end
  308.  
  309. local function write(self, ...)
  310.     local data = {...}
  311.     for i = 1, #data do
  312.         data[i] = tostring(data[i])
  313.     end
  314.     data = table.concat(data)
  315.  
  316.     -- Data is small enough to fit buffer
  317.     if #data < (BUFFER_SIZE - #self.buffer) then
  318.         self.buffer = self.buffer .. data
  319.  
  320.         return true
  321.     else
  322.         -- Write current buffer content
  323.         local success, reason = self.proxy.write(self.stream, self.buffer)
  324.         if success then
  325.             -- If data will not fit buffer, use iterative writing with data partitioning
  326.             if #data > BUFFER_SIZE then
  327.                 for i = 1, #data, BUFFER_SIZE do
  328.                     success, reason = self.proxy.write(self.stream, data:sub(i, i + BUFFER_SIZE - 1))
  329.                    
  330.                     if not success then
  331.                         break
  332.                     end
  333.                 end
  334.  
  335.                 self.buffer = ""
  336.  
  337.                 return success, reason
  338.             -- Data will perfectly fit in empty buffer
  339.             else
  340.                 self.buffer = data
  341.  
  342.                 return true
  343.             end
  344.         else
  345.             return false, reason
  346.         end
  347.     end
  348. end
  349.  
  350. local function writeBytes(self, ...)
  351.     return write(self, string.char(...))
  352. end
  353.  
  354. local function close(self)
  355.     if self.write and #self.buffer > 0 then
  356.         self.proxy.write(self.stream, self.buffer)
  357.     end
  358.  
  359.     return self.proxy.close(self.stream)
  360. end
  361.  
  362. function filesystem.open(path, mode)
  363.     local proxy, proxyPath = filesystem.get(path)
  364.     local result, reason = proxy.open(proxyPath, mode)
  365.     if result then
  366.         local handle = {
  367.             proxy = proxy,
  368.             stream = result,
  369.             position = 0,
  370.             buffer = "",
  371.             close = close,
  372.             seek = seek,
  373.         }
  374.  
  375.         if mode == "r" or mode == "rb" then
  376.             handle.readString = readString
  377.             handle.readUnicodeChar = readUnicodeChar
  378.             handle.readBytes = readBytes
  379.             handle.readLine = readLine
  380.             handle.lines = lines
  381.             handle.readAll = readAll
  382.             handle.read = read
  383.  
  384.             return handle
  385.         elseif mode == "w" or mode == "wb" or mode == "a" or mode == "ab" then
  386.             handle.write = write
  387.             handle.writeBytes = writeBytes
  388.  
  389.             return handle
  390.         else
  391.             error("bad argument #2 ('r', 'rb', 'w', 'wb' or 'a' expected, got )" .. tostring(mode) .. ")")
  392.         end
  393.     else
  394.         return nil, reason
  395.     end
  396. end
  397.  
  398. --------------------------------------- Rest proxy methods -----------------------------------------
  399.  
  400. function filesystem.exists(path)
  401.     local proxy, proxyPath = filesystem.get(path)
  402.     return proxy.exists(proxyPath)
  403. end
  404.  
  405. function filesystem.size(path)
  406.     local proxy, proxyPath = filesystem.get(path)
  407.     return proxy.size(proxyPath)
  408. end
  409.  
  410. function filesystem.isDirectory(path)
  411.     local proxy, proxyPath = filesystem.get(path)
  412.     return proxy.isDirectory(proxyPath)
  413. end
  414.  
  415. function filesystem.makeDirectory(path)
  416.     local proxy, proxyPath = filesystem.get(path)
  417.     return proxy.makeDirectory(proxyPath)
  418. end
  419.  
  420. function filesystem.lastModified(path)
  421.     local proxy, proxyPath = filesystem.get(path)
  422.     return proxy.lastModified(proxyPath)
  423. end
  424.  
  425. function filesystem.remove(path)
  426.     local proxy, proxyPath = filesystem.get(path)
  427.     return proxy.remove(proxyPath)
  428. end
  429.  
  430. function filesystem.list(path, sortingMethod)
  431.     local proxy, proxyPath = filesystem.get(path)
  432.    
  433.     local list, reason = proxy.list(proxyPath) 
  434.     if list then
  435.         -- Fullfill list with mounted paths if needed
  436.         for i = 1, #mountedProxies do
  437.             if path == filesystem.path(mountedProxies[i].path) then
  438.                 table.insert(list, filesystem.name(mountedProxies[i].path))
  439.             end
  440.         end
  441.  
  442.         -- Applying sorting methods
  443.         if not sortingMethod or sortingMethod == filesystem.SORTING_NAME then
  444.             table.sort(list, function(a, b)
  445.                 return unicode.lower(a) < unicode.lower(b)
  446.             end)
  447.  
  448.             return list
  449.         elseif sortingMethod == filesystem.SORTING_DATE then
  450.             table.sort(list, function(a, b)
  451.                 return filesystem.lastModified(path .. a) > filesystem.lastModified(path .. b)
  452.             end)
  453.  
  454.             return list
  455.         elseif sortingMethod == filesystem.SORTING_TYPE then
  456.             -- Creating a map with "extension" = {file1, file2, ...} structure
  457.             local map, extension = {}
  458.             for i = 1, #list do
  459.                 extension = filesystem.extension(list[i]) or "Z"
  460.                
  461.                 -- If it's a directory without extension
  462.                 if extension:sub(1, 1) ~= "." and filesystem.isDirectory(path .. list[i]) then
  463.                     extension = "."
  464.                 end
  465.  
  466.                 map[extension] = map[extension] or {}
  467.                 table.insert(map[extension], list[i])
  468.             end
  469.  
  470.             -- Sorting lists for each extension
  471.             local extensions = {}
  472.             for key, value in pairs(map) do
  473.                 table.sort(value, function(a, b)
  474.                     return unicode.lower(a) < unicode.lower(b)
  475.                 end)
  476.  
  477.                 table.insert(extensions, key)
  478.             end
  479.  
  480.             -- Sorting extensions
  481.             table.sort(extensions, function(a, b)
  482.                 return unicode.lower(a) < unicode.lower(b)
  483.             end)
  484.  
  485.             -- Fullfilling final list
  486.             list = {}
  487.             for i = 1, #extensions do
  488.                 for j = 1, #map[extensions[i]] do
  489.                     table.insert(list, map[extensions[i]][j])
  490.                 end
  491.             end
  492.  
  493.             return list
  494.         end
  495.     end
  496.  
  497.     return list, reason
  498. end
  499.  
  500. function filesystem.rename(fromPath, toPath)
  501.     local fromProxy, fromProxyPath = filesystem.get(fromPath)
  502.     local toProxy, toProxyPath = filesystem.get(toPath)
  503.  
  504.     -- If it's the same filesystem component
  505.     if fromProxy.address == toProxy.address then
  506.         return fromProxy.rename(fromProxyPath, toProxyPath)
  507.     else
  508.         -- Copy files to destination
  509.         filesystem.copy(fromPath, toPath)
  510.         -- Remove original files
  511.         filesystem.remove(fromPath)
  512.     end
  513. end
  514.  
  515. --------------------------------------- Advanced methods -----------------------------------------
  516.  
  517. function filesystem.copy(fromPath, toPath)
  518.     local function copyRecursively(fromPath, toPath)
  519.         if filesystem.isDirectory(fromPath) then
  520.             filesystem.makeDirectory(toPath)
  521.  
  522.             local list = filesystem.list(fromPath)
  523.             for i = 1, #list do
  524.                 copyRecursively(fromPath .. "/" .. list[i], toPath .. "/" .. list[i])
  525.             end
  526.         else
  527.             local fromHandle = filesystem.open(fromPath, "rb")
  528.             if fromHandle then
  529.                 local toHandle = filesystem.open(toPath, "wb")
  530.                 if toHandle then
  531.                     while true do
  532.                         local chunk = readString(fromHandle, BUFFER_SIZE)
  533.                         if chunk then
  534.                             if not write(toHandle, chunk) then
  535.                                 break
  536.                             end
  537.                         else
  538.                             toHandle:close()
  539.                             fromHandle:close()
  540.  
  541.                             break
  542.                         end
  543.                     end
  544.                 end
  545.             end
  546.         end
  547.     end
  548.  
  549.     copyRecursively(fromPath, toPath)
  550. end
  551.  
  552. function filesystem.read(path)
  553.     local handle, reason = filesystem.open(path, "rb")
  554.     if handle then
  555.         local data = readAll(handle)
  556.         handle:close()
  557.  
  558.         return data
  559.     end
  560.  
  561.     return false, reason
  562. end
  563.  
  564. function filesystem.lines(path)
  565.     local handle, reason = filesystem.open(path, "rb")
  566.     if handle then
  567.         return handle:lines()
  568.     else
  569.         error(reason)
  570.     end
  571. end
  572.  
  573. function filesystem.readLines(path)
  574.     local handle, reason = filesystem.open(path, "rb")
  575.     if handle then
  576.         local lines, index, line = {}, 1
  577.  
  578.         repeat
  579.             line = readLine(handle)
  580.             lines[index] = line
  581.             index = index + 1
  582.         until not line
  583.  
  584.         handle:close()
  585.  
  586.         return lines
  587.     end
  588.  
  589.     return false, reason
  590. end
  591.  
  592. local function writeOrAppend(append, path, ...)
  593.     filesystem.makeDirectory(filesystem.path(path))
  594.    
  595.     local handle, reason = filesystem.open(path, append and "ab" or "wb")
  596.     if handle then
  597.         local result, reason = write(handle, ...)
  598.         handle:close()
  599.  
  600.         return result, reason
  601.     end
  602.  
  603.     return false, reason
  604. end
  605.  
  606. function filesystem.write(path, ...)
  607.     return writeOrAppend(false, path,...)
  608. end
  609.  
  610. function filesystem.append(path, ...)
  611.     return writeOrAppend(true, path, ...)
  612. end
  613.  
  614. function filesystem.writeTable(path, ...)
  615.     return filesystem.write(path, require("Text").serialize(...))
  616. end
  617.  
  618. function filesystem.readTable(path)
  619.     local result, reason = filesystem.read(path)
  620.     if result then
  621.         return require("Text").deserialize(result)
  622.     end
  623.  
  624.     return result, reason
  625. end
  626.  
  627. function filesystem.setProxy(proxy)
  628.     BOOT_PROXY = proxy
  629. end
  630.  
  631. function filesystem.getProxy()
  632.     return BOOT_PROXY
  633. end
  634.  
  635. --------------------------------------- loadfile() and dofile() implementation -----------------------------------------
  636.  
  637. function loadfile(path)
  638.     local data, reason = filesystem.read(path)
  639.     if data then
  640.         return load(data, "=" .. path)
  641.     end
  642.  
  643.     return nil, reason
  644. end
  645.  
  646. function dofile(path, ...)
  647.     local result, reason = loadfile(path)
  648.     if result then
  649.         local data = {xpcall(result, debug.traceback, ...)}
  650.         if data[1] then
  651.             return table.unpack(data, 2)
  652.         else
  653.             error(data[2])
  654.         end
  655.     else
  656.         error(reason)
  657.     end
  658. end
  659.  
  660. --------------------------------------------------------------------------------
  661.  
  662. -- Mount all existing filesystem components
  663. for address in component.list("filesystem") do
  664.     filesystem.mount(component.proxy(address), paths.system.mounts .. address .. "/")
  665. end
  666.  
  667. -- Automatically mount/unmount filesystem components
  668. event.addHandler(function(signal, address, type)
  669.     if signal == "component_added" and type == "filesystem" then
  670.         filesystem.mount(component.proxy(address), paths.system.mounts .. address .. "/")
  671.     elseif signal == "component_removed" and type == "filesystem" then
  672.         filesystem.unmount(address)
  673.     end
  674. end)
  675.  
  676. --------------------------------------------------------------------------------
  677.  
  678. return filesystem
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement