Files
cell/cellfs.cm
2026-02-25 17:43:01 -06:00

590 lines
14 KiB
Plaintext

var cellfs = {}
var fd = use('fd')
var miniz = use('miniz')
var qop = use('internal/qop')
var wildstar = use('internal/wildstar')
var blib = use('blob')
var mounts = []
var write_mount = null
function normalize_path(path) {
if (!path) return ""
return replace(path, /^\/+|\/+$/, "")
}
function mount_exists(mount, path) {
var result = false
var full_path = null
var st = null
var _check = null
if (mount.type == 'zip') {
_check = function() {
mount.handle.mod(path)
result = true
} disruption {}
_check()
} else if (mount.type == 'qop') {
_check = function() {
result = mount.handle.stat(path) != null
} disruption {}
_check()
} else if (mount.type == 'fs') {
full_path = fd.join_paths(mount.source, path)
_check = function() {
st = fd.stat(full_path)
result = st.isFile || st.isDirectory
} disruption {}
_check()
}
return result
}
function is_directory(path) {
var res = resolve(path)
var mount = res.mount
var result = false
var full_path = null
var st = null
var _check = null
if (mount.type == 'zip') {
_check = function() {
result = mount.handle.is_directory(path)
} disruption {}
_check()
} else if (mount.type == 'qop') {
_check = function() {
result = mount.handle.is_directory(path)
} disruption {}
_check()
} else {
full_path = fd.join_paths(mount.source, path)
_check = function() {
st = fd.stat(full_path)
result = st.isDirectory
} disruption {}
_check()
}
return result
}
function resolve(path, must_exist) {
var idx = null
var mount_name = ""
var rel_path = ""
var mount = null
var found_mount = null
var npath = normalize_path(path)
if (starts_with(npath, "@")) {
idx = search(npath, "/")
if (idx == null) {
mount_name = text(npath, 1)
rel_path = ""
} else {
mount_name = text(npath, 1, idx)
rel_path = text(npath, idx + 1)
}
arrfor(mounts, function(m) {
if (m.name == mount_name) {
mount = m
return true
}
}, false, true)
if (!mount) {
log.error("Unknown mount point: @" + mount_name); disrupt
}
return { mount: mount, path: rel_path }
}
arrfor(mounts, function(m) {
if (mount_exists(m, npath)) {
found_mount = { mount: m, path: npath }
return true
}
}, false, true)
if (found_mount) {
return found_mount
}
if (must_exist) {
log.error("File not found in any mount: " + npath); disrupt
}
}
function mount(source, name) {
var st = null
var blob = null
var qop_archive = null
var zip = null
var _try_qop = null
var http = null
var mount_info = {
source: source,
name: name || null,
type: 'fs',
handle: null,
zip_blob: null
}
if (starts_with(source, 'http://') || starts_with(source, 'https://')) {
http = use('http')
mount_info.type = 'http'
mount_info.handle = {
base_url: source,
get: function(path, callback) {
var url = source + '/' + path
$clock(function(_t) {
var resp = http.request('GET', url, null, null)
if (resp && resp.status == 200) {
callback(resp.body)
} else {
callback(null, "HTTP " + text(resp ? resp.status : 0) + ": " + url)
}
})
}
}
push(mounts, mount_info)
return
}
st = fd.stat(source)
if (st.isDirectory) {
mount_info.type = 'fs'
} else if (st.isFile) {
blob = fd.slurp(source)
qop_archive = null
_try_qop = function() {
qop_archive = qop.open(blob)
} disruption {}
_try_qop()
if (qop_archive) {
mount_info.type = 'qop'
mount_info.handle = qop_archive
mount_info.zip_blob = blob
} else {
zip = miniz.read(blob)
if (!is_object(zip) || !is_function(zip.count)) {
log.error("Invalid archive file (not zip or qop): " + source); disrupt
}
mount_info.type = 'zip'
mount_info.handle = zip
mount_info.zip_blob = blob
}
} else {
log.error("Unsupported mount source type: " + source); disrupt
}
push(mounts, mount_info)
}
function unmount(name_or_source) {
mounts = filter(mounts, function(m) {
return m.name != name_or_source && m.source != name_or_source
})
}
function slurp(path) {
var res = resolve(path, true)
var data = null
var full_path = null
if (!res) { log.error("File not found: " + path); disrupt }
if (res.mount.type == 'zip') {
return res.mount.handle.slurp(res.path)
} else if (res.mount.type == 'qop') {
data = res.mount.handle.read(res.path)
if (!data) { log.error("File not found in qop: " + path); disrupt }
return data
} else {
full_path = fd.join_paths(res.mount.source, res.path)
return fd.slurp(full_path)
}
}
function slurpwrite(path, data) {
var full_path = null
if (write_mount) {
full_path = fd.join_paths(write_mount.source, path)
} else {
full_path = fd.join_paths(".", path)
}
fd.slurpwrite(full_path, data)
}
function exists(path) {
var res = resolve(path, false)
if (starts_with(path, "@")) {
return mount_exists(res.mount, res.path)
}
return res != null
}
function stat(path) {
var res = resolve(path, true)
var mod = null
var s = null
var full_path = null
if (!res) { log.error("File not found: " + path); disrupt }
if (res.mount.type == 'zip') {
mod = res.mount.handle.mod(res.path)
return {
filesize: 0,
modtime: mod * 1000,
isDirectory: false
}
} else if (res.mount.type == 'qop') {
s = res.mount.handle.stat(res.path)
if (!s) { log.error("File not found in qop: " + path); disrupt }
return {
filesize: s.size,
modtime: s.modtime,
isDirectory: s.isDirectory
}
} else {
full_path = fd.join_paths(res.mount.source, res.path)
s = fd.stat(full_path)
return {
filesize: s.size,
modtime: s.mtime,
isDirectory: s.isDirectory
}
}
}
function searchpath() {
return array(mounts)
}
function mount_package(name) {
if (name == null) {
mount('.', null)
return
}
var shop = use('internal/shop')
var dir = shop.get_package_dir(name)
if (!dir) {
log.error("Package not found: " + name); disrupt
}
mount(dir, name)
}
function match(str, pattern) {
return wildstar.match(pattern, str, wildstar.WM_PATHNAME | wildstar.WM_PERIOD | wildstar.WM_WILDSTAR)
}
function rm(path) {
var res = resolve(path, true)
var full_path = null
var st = null
if (res.mount.type != 'fs') { log.error("Cannot delete from non-fs mount"); disrupt }
full_path = fd.join_paths(res.mount.source, res.path)
st = fd.stat(full_path)
if (st.isDirectory) fd.rmdir(full_path)
else fd.unlink(full_path)
}
function mkdir(path) {
var full = null
if (write_mount) {
full = fd.join_paths(write_mount.source, path)
} else {
full = fd.join_paths(".", path)
}
fd.mkdir(full)
}
function set_writepath(mount_name) {
var found = null
if (mount_name == null) { write_mount = null; return }
arrfor(mounts, function(m) {
if (m.name == mount_name) { found = m; return true }
}, false, true)
if (!found || found.type != 'fs') {
log.error("writepath: must be an fs mount"); disrupt
}
write_mount = found
}
function basedir() {
return fd.getcwd()
}
function prefdir(org, app) {
return "./"
}
function realdir(path) {
var res = resolve(path, false)
if (!res) return null
return fd.join_paths(res.mount.source, res.path)
}
function enumerate(_path, recurse) {
var path = _path == null ? "" : _path
var res = resolve(path, true)
var results = []
var full = null
var st = null
var all = null
var prefix = null
var prefix_len = null
var seen = null
function visit(curr_full, rel_prefix) {
var list = fd.readdir(curr_full)
if (!list) return
arrfor(list, function(item) {
var item_rel = rel_prefix ? rel_prefix + "/" + item : item
var child_st = null
push(results, item_rel)
if (recurse) {
child_st = fd.stat(fd.join_paths(curr_full, item))
if (child_st.isDirectory) {
visit(fd.join_paths(curr_full, item), item_rel)
}
}
})
}
if (res.mount.type == 'fs') {
full = fd.join_paths(res.mount.source, res.path)
st = fd.stat(full)
if (st && st.isDirectory) {
visit(full, "")
}
} else if (res.mount.type == 'qop') {
all = res.mount.handle.list()
prefix = res.path ? res.path + "/" : ""
prefix_len = length(prefix)
seen = {}
arrfor(all, function(p) {
var rel = null
var slash = null
if (starts_with(p, prefix)) {
rel = text(p, prefix_len)
if (length(rel) == 0) return
if (!recurse) {
slash = search(rel, '/')
if (slash != null) {
rel = text(rel, 0, slash)
}
}
if (!seen[rel]) {
seen[rel] = true
push(results, rel)
}
}
})
}
return results
}
function globfs(globs, _dir) {
var dir = _dir == null ? "" : _dir
var res = resolve(dir, true)
var results = []
var full = null
var st = null
var all = null
var prefix = null
var prefix_len = null
function check_neg(path) {
var result = false
arrfor(globs, function(g) {
if (starts_with(g, "!") && wildstar.match(text(g, 1), path, wildstar.WM_WILDSTAR)) {
result = true
return true
}
}, false, true)
return result
}
function check_pos(path) {
var result = false
arrfor(globs, function(g) {
if (!starts_with(g, "!") && wildstar.match(g, path, wildstar.WM_WILDSTAR)) {
result = true
return true
}
}, false, true)
return result
}
function visit(curr_full, rel_prefix) {
if (rel_prefix && check_neg(rel_prefix)) return
var list = fd.readdir(curr_full)
if (!list) return
arrfor(list, function(item) {
var item_rel = rel_prefix ? rel_prefix + "/" + item : item
var child_full = fd.join_paths(curr_full, item)
var child_st = fd.stat(child_full)
if (child_st.isDirectory) {
if (!check_neg(item_rel)) {
visit(child_full, item_rel)
}
} else {
if (!check_neg(item_rel) && check_pos(item_rel)) {
push(results, item_rel)
}
}
})
}
if (res.mount.type == 'fs') {
full = fd.join_paths(res.mount.source, res.path)
st = fd.stat(full)
if (st && st.isDirectory) {
visit(full, "")
}
} else if (res.mount.type == 'qop') {
all = res.mount.handle.list()
prefix = res.path ? res.path + "/" : ""
prefix_len = length(prefix)
arrfor(all, function(p) {
var rel = null
if (starts_with(p, prefix)) {
rel = text(p, prefix_len)
if (length(rel) == 0) return
if (!check_neg(rel) && check_pos(rel)) {
push(results, rel)
}
}
})
}
return results
}
// Requestor factory: returns a requestor for reading a file at path
function get(path) {
return function get_requestor(callback, value) {
var res = resolve(path, false)
var full = null
var f = null
var acc = null
var cancelled = false
var data = null
var _close = null
if (!res) { callback(null, "not found: " + path); return }
if (res.mount.type == 'zip') {
callback(res.mount.handle.slurp(res.path))
return
}
if (res.mount.type == 'qop') {
data = res.mount.handle.read(res.path)
if (data) {
callback(data)
} else {
callback(null, "not found in qop: " + path)
}
return
}
if (res.mount.type == 'http') {
res.mount.handle.get(res.path, callback)
return
}
full = fd.join_paths(res.mount.source, res.path)
f = fd.open(full, 'r')
acc = blob()
function next(_t) {
var chunk = null
if (cancelled) return
chunk = fd.read(f, 65536)
if (length(chunk) == 0) {
fd.close(f)
stone(acc)
callback(acc)
return
}
acc.write_blob(chunk)
$clock(next)
}
next()
return function cancel() {
cancelled = true
_close = function() { fd.close(f) } disruption {}
_close()
}
}
}
// Requestor factory: returns a requestor for writing data to path
function put(path, data) {
return function put_requestor(callback, value) {
var _data = data != null ? data : value
var full = null
var _do = null
if (!write_mount) { callback(null, "no write mount set"); return }
full = fd.join_paths(write_mount.source, path)
_do = function() {
fd.slurpwrite(full, _data)
callback(true)
} disruption {
callback(null, "write failed: " + path)
}
_do()
}
}
cellfs.mount = mount
cellfs.mount_package = mount_package
cellfs.unmount = unmount
cellfs.slurp = slurp
cellfs.slurpwrite = slurpwrite
cellfs.exists = exists
cellfs.is_directory = is_directory
cellfs.stat = stat
cellfs.searchpath = searchpath
cellfs.match = match
cellfs.enumerate = enumerate
cellfs.globfs = globfs
cellfs.rm = rm
cellfs.mkdir = mkdir
cellfs.writepath = set_writepath
cellfs.basedir = basedir
cellfs.prefdir = prefdir
cellfs.realdir = realdir
cellfs.get = get
cellfs.put = put
cellfs.resolve = resolve
return cellfs