Skip to content

Instantly share code, notes, and snippets.

@martandrMC
Created June 1, 2024 00:06
Show Gist options
  • Save martandrMC/11e8cafa79a072be071b48957bc8de7b to your computer and use it in GitHub Desktop.
Save martandrMC/11e8cafa79a072be071b48957bc8de7b to your computer and use it in GitHub Desktop.
Computercraft Virtual Filesystem Library (Written with Google Drive in mind though other services could work)
local export = {}
----------------
local link_pref, cache_path
local root_dir = {}
local curr_dir = {}
local curr_path = {}
----------------
-- Private Functions
----------------
local function getDirFile(link)
local handle, err, flag
local local_path = cache_path .. link .. ".dir"
-- If the needed directory has been cached
if fs.exists(local_path) then
-- Open cached directory list from FS
handle, err = fs.open(local_path, "r")
flag = false
else
-- Open handle to download directory list
handle, err = http.get(link_pref .. link)
flag = true
end
if handle == nil then error(err, 0) end
-- Read each line and add it to array
local lines = {}
local line = handle.readLine()
while line ~= nil do
table.insert(lines, line)
line = handle.readLine()
end
handle.close()
-- If the directory was downloaded, cache it
if flag then
handle, err = fs.open(local_path, "w")
if handle == nil then error(err, 0) end
for i=1, #lines do handle.writeLine(lines[i]) end
handle.close()
end
-- Parse the directory list's lines
local dir_table = {}
for i=1, #lines do
-- Line format: <type> <name> <link>
-- or $ <link>, used for the parent directory
local f = lines[i]:gmatch("%S+")
local type = f()
if type == "$" then
dir_table.parent = f()
else
local entry = {}
entry.type = type
entry.name = f()
entry.link = f()
table.insert(dir_table, entry)
end
end
return dir_table
end
local function lookup(dir, name)
-- Search the directory for an entry named xx
for i=1, #dir do
if dir[i].name == name then
-- Return the index of that entry
return i
end
end
-- Zero used as a not-found signal
-- since zero is an invalid index
return 0
end
----------------
-- Public Functions
----------------
function export.getPath()
-- Append every entry in the curr_path array
-- separated with slashes
local path = "/"
for i=1, #curr_path do
path = path .. curr_path[i] .. "/"
end
return path
end
function export.getDir()
-- Create a new directory table
-- without the sub-dir links and return that
local dir = {}
for i=1, #curr_dir do
local entry = {}
entry.name = curr_dir[i].name
entry.type = curr_dir[i].type
if entry.type ~= "dir" then
entry.link = curr_dir[i].link
end
table.insert(dir, entry)
end
dir.isRoot = (curr_dir.parent == nil)
return dir
end
function export.getFile(filename, binary)
-- Find a non-directory entry in curr_dir
local index = lookup(curr_dir, filename)
if index == 0 then return false end
if curr_dir[index].type == "dir" then return false end
-- Open a http handle and return it
local link = link_pref .. curr_dir[index].link
handle, err = http.get({url=link, binary=binary})
if handle == nil then error(err, 0) end
return true, handle
end
----------------
function export.find(filename)
-- lookup() but only at the current directory
return lookup(curr_dir, filename)
end
function export.changeDir(new_path)
local tmp_dir, tmp_path
-- If the path is just /, go to root
if new_path == "/" then
curr_dir = root_dir
curr_path = {}
return true
-- If the path starts with /, begin at root
elseif string.sub(new_path, 1, 1) == "/" then
tmp_dir = root_dir
tmp_path = {}
-- If it doesn't start with /, begin at current dir
else
tmp_dir = curr_dir
tmp_path = curr_path
end
-- Run loop once for every substring within slashes
local f = new_path:gmatch("[^/]+")
local part = f()
while part ~= nil do
-- If the substring is .. follow the parent link
if part == ".." then
if tmp_dir.parent == nil then return false end
table.remove(tmp_path)
tmp_dir = getDirFile(tmp_dir.parent)
-- Else lookup current dir for that name and follow that
else
local index = lookup(tmp_dir, part)
if index == 0 then return false end
table.insert(tmp_path, tmp_dir[index].name)
tmp_dir = getDirFile(tmp_dir[index].link)
end
part = f()
end
curr_dir = tmp_dir
curr_path = tmp_path
return true
end
function export.clearCache()
-- Go over all files in the cache directory
-- and call fs.delete on them
local files = fs.list(cache_path)
for i=1, #files do fs.delete(cache_path .. files[i]) end
end
function export.init(pref, root, cache, clear)
-- Setup link prefix
link_pref = pref
-- Setup directory caching location
cache_path = cache .. ".webfscache/"
fs.makeDir(cache_path)
if clear then export.clearCache() end
-- Fetch the root directory
root_dir = getDirFile(root)
curr_dir = root_dir
end
----------------
return export
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment