Compare commits

...

5 Commits

Author SHA1 Message Date
ed
09557fbe83 v0.4.2 2020-05-15 01:02:18 +02:00
ed
1c0f44fa4e more 206 correctness 2020-05-15 00:52:57 +02:00
ed
fc4d59d2d7 improve autoindent 2020-05-15 00:39:36 +02:00
ed
12345fbacc fix editor cursor (especially in firefox) 2020-05-15 00:03:26 +02:00
ed
2e33c8d222 improve http206 and fuse-client 2020-05-15 00:00:49 +02:00
10 changed files with 168 additions and 77 deletions

48
bin/copyparty-fuse.py Executable file → Normal file
View File

@@ -22,7 +22,9 @@ from urllib.parse import quote_from_bytes as quote
try:
from fuse import FUSE, FuseOSError, Operations
except:
print("\n could not import fuse;\n pip install fusepy\n")
print(
"\n could not import fuse; these may help:\n python3 -m pip install --user fusepy\n apt install libfuse\n modprobe fuse"
)
raise
@@ -34,9 +36,7 @@ usage:
dependencies:
sudo apk add fuse-dev
python3 -m venv ~/pe/ve.fusepy
. ~/pe/ve.fusepy/bin/activate
pip install fusepy
python3 -m pip install --user fusepy
MB/s
@@ -60,20 +60,21 @@ def boring_log(msg):
def rice_tid():
tid = threading.current_thread().ident
c = struct.unpack(b"B" * 5, struct.pack(b">Q", tid)[-5:])
return "".join("\033[1;37;48;5;{}m{:02x}".format(x, x) for x in c)
return "".join("\033[1;37;48;5;{}m{:02x}".format(x, x) for x in c) + "\033[0m"
def fancy_log(msg):
print("{}\033[0m {}\n".format(rice_tid(), msg), end="")
print("{} {}\n".format(rice_tid(), msg), end="")
def null_log(msg):
pass
log = boring_log
info = fancy_log
log = fancy_log
log = threadless_log
dbg = fancy_log
log = null_log
dbg = null_log
@@ -118,7 +119,7 @@ class Gateway(object):
try:
return self.conns[tid]
except:
log("new conn [{}] [{}]".format(self.web_host, self.web_port))
info("new conn [{}] [{}]".format(self.web_host, self.web_port))
conn = http.client.HTTPConnection(self.web_host, self.web_port, timeout=260)
@@ -152,7 +153,7 @@ class Gateway(object):
if r.status != 200:
self.closeconn()
raise Exception(
"http error {} reading dir {} in {:x}".format(
"http error {} reading dir {} in {}".format(
r.status, web_path, rice_tid()
)
)
@@ -161,14 +162,14 @@ class Gateway(object):
def download_file_range(self, path, ofs1, ofs2):
web_path = "/" + "/".join([self.web_root, path])
hdr_range = "bytes={}-{}".format(ofs1, ofs2)
hdr_range = "bytes={}-{}".format(ofs1, ofs2 - 1)
log("downloading {}".format(hdr_range))
r = self.sendreq("GET", self.quotep(web_path), headers={"Range": hdr_range})
if r.status != http.client.PARTIAL_CONTENT:
self.closeconn()
raise Exception(
"http error {} reading file {} range {} in {:x}".format(
"http error {} reading file {} range {} in {}".format(
r.status, web_path, hdr_range, rice_tid()
)
)
@@ -246,14 +247,14 @@ class CPPF(Operations):
self.filecache = []
self.filecache_mtx = threading.Lock()
log("up")
info("up")
def clean_dircache(self):
"""not threadsafe"""
now = time.time()
cutoff = 0
for cn in self.dircache:
if cn.ts - now > 1:
if now - cn.ts > 1:
cutoff += 1
else:
break
@@ -398,7 +399,7 @@ class CPPF(Operations):
)
)
buf = self.gw.download_file_range(path, h_ofs, h_end - 1)
buf = self.gw.download_file_range(path, h_ofs, h_end)
ret = buf[-buf_ofs:] + cdr
elif car:
@@ -416,7 +417,7 @@ class CPPF(Operations):
)
)
buf = self.gw.download_file_range(path, h_ofs, h_end - 1)
buf = self.gw.download_file_range(path, h_ofs, h_end)
ret = car + buf[:buf_ofs]
else:
@@ -438,7 +439,7 @@ class CPPF(Operations):
)
)
buf = self.gw.download_file_range(path, h_ofs, h_end - 1)
buf = self.gw.download_file_range(path, h_ofs, h_end)
ret = buf[buf_ofs:buf_end]
cn = CacheNode([path, h_ofs], buf)
@@ -472,13 +473,16 @@ class CPPF(Operations):
log("read {} @ {} len {} end {}".format(path, offset, length, ofs2))
file_sz = self.getattr(path)["st_size"]
if ofs2 >= file_sz:
ofs2 = file_sz - 1
log("truncate to len {} end {}".format((ofs2 - offset) + 1, ofs2))
if ofs2 > file_sz:
ofs2 = file_sz
log("truncate to len {} end {}".format(ofs2 - offset, ofs2))
if file_sz == 0 or offset >= ofs2:
return b""
# toggle cache here i suppose
# return self.get_cached_file(path, offset, ofs2, file_sz)
return self.gw.download_file_range(path, offset, ofs2 - 1)
return self.gw.download_file_range(path, offset, ofs2)
def getattr(self, path, fh=None):
path = path.strip("/")
@@ -495,7 +499,7 @@ class CPPF(Operations):
cn = self.get_cached_dir(dirpath)
if cn:
# log('cache ok')
log("cache ok")
dents = cn.data
else:
log("cache miss")

View File

@@ -1,8 +1,8 @@
# coding: utf-8
VERSION = (0, 4, 1)
VERSION = (0, 4, 2)
CODENAME = "NIH"
BUILD_DT = (2020, 5, 14)
BUILD_DT = (2020, 5, 15)
S_VERSION = ".".join(map(str, VERSION))
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)

View File

@@ -769,11 +769,18 @@ class HttpCli(object):
else:
upper = file_sz
if lower < 0 or lower >= file_sz or upper < 0 or upper > file_sz:
if upper > file_sz:
upper = file_sz
if lower < 0 or lower >= upper:
raise Exception()
except:
raise Pebkac(400, "invalid range requested: " + hrange)
err = "invalid range ({}), size={}".format(hrange, file_sz)
self.loud_reply(err, status=416, headers={
"Content-Range": "bytes */{}".format(file_sz)
})
return True
status = 206
self.out_headers["Content-Range"] = "bytes {}-{}/{}".format(

View File

@@ -80,8 +80,9 @@ class HttpSrv(object):
"%s %s" % addr,
"shut_rdwr err:\n {}\n {}".format(repr(sck), ex),
)
if ex.errno not in [10038, 107, 57, 9]:
if ex.errno not in [10038, 10054, 107, 57, 9]:
# 10038 No longer considered a socket
# 10054 Foribly closed by remote
# 107 Transport endpoint not connected
# 57 Socket is not connected
# 9 Bad file descriptor

View File

@@ -49,6 +49,7 @@ HTTPCODE = {
404: "Not Found",
405: "Method Not Allowed",
413: "Payload Too Large",
416: "Requested Range Not Satisfiable",
422: "Unprocessable Entity",
500: "Internal Server Error",
501: "Not Implemented",
@@ -309,18 +310,7 @@ def get_boundary(headers):
def read_header(sr):
ret = b""
while True:
if ret.endswith(b"\r\n\r\n"):
break
elif ret.endswith(b"\r\n\r"):
n = 1
elif ret.endswith(b"\r\n"):
n = 2
elif ret.endswith(b"\r"):
n = 3
else:
n = 4
buf = sr.recv(n)
buf = sr.recv(1024)
if not buf:
if not ret:
return None
@@ -332,11 +322,15 @@ def read_header(sr):
)
ret += buf
ofs = ret.find(b"\r\n\r\n")
if ofs < 0:
if len(ret) > 1024 * 64:
raise Pebkac(400, "header 2big")
else:
continue
if len(ret) > 1024 * 64:
raise Pebkac(400, "header 2big")
return ret[:-4].decode("utf-8", "surrogateescape").split("\r\n")
sr.unrecv(ret[ofs + 4 :])
return ret[:ofs].decode("utf-8", "surrogateescape").split("\r\n")
def undot(path):

View File

@@ -30,6 +30,11 @@ function cls(dom, name, add) {
}
function static(obj) {
return JSON.parse(JSON.stringify(obj));
}
// add navbar
(function () {
var n = document.location + '';

View File

@@ -323,13 +323,26 @@ function save_chk() {
}
// firefox bug: initial selection offset isn't cleared properly through js
var ff_clearsel = (function () {
if (navigator.userAgent.indexOf(') Gecko/') === -1)
return function () { }
return function () {
var txt = dom_src.value;
var y = dom_src.scrollTop;
dom_src.value = '';
dom_src.value = txt;
dom_src.scrollTop = y;
};
})();
// returns car/cdr (selection bounds) and n1/n2 (grown to full lines)
function linebounds(just_car) {
function linebounds(just_car, greedy_growth) {
var car = dom_src.selectionStart,
cdr = dom_src.selectionEnd;
dbg(car, cdr);
if (just_car)
cdr = car;
@@ -337,11 +350,13 @@ function linebounds(just_car) {
n1 = Math.max(car, 0),
n2 = Math.min(cdr, md.length - 1);
if (n1 < n2 && md[n1] == '\n')
n1++;
if (greedy_growth !== true) {
if (n1 < n2 && md[n1] == '\n')
n1++;
if (n1 < n2 && md[n2 - 1] == '\n')
n2 -= 2;
if (n1 < n2 && md[n2 - 1] == '\n')
n2 -= 2;
}
n1 = md.lastIndexOf('\n', n1 - 1) + 1;
n2 = md.indexOf('\n', n2);
@@ -375,7 +390,7 @@ function setsel(s) {
s.cdr = s.pre.length + s.sel.length;
}
dom_src.value = [s.pre, s.sel, s.post].join('');
dom_src.setSelectionRange(s.car, s.cdr);
dom_src.setSelectionRange(s.car, s.cdr, dom_src.selectionDirection);
dom_src.oninput();
}
@@ -416,17 +431,32 @@ function md_header(dedent) {
// smart-home
function md_home(shift) {
var s = linebounds(!shift),
var s = linebounds(false, true),
ln = s.md.substring(s.n1, s.n2),
m = /^[ \t#>+-]*(\* )?([0-9]+\. +)?/.exec(ln),
home = s.n1 + m[0].length,
car = (s.car == home) ? s.n1 : home,
cdr = shift ? s.cdr : car;
dir = dom_src.selectionDirection,
rev = dir === 'backward',
p1 = rev ? s.car : s.cdr,
p2 = rev ? s.cdr : s.car,
home = 0,
lf = ln.lastIndexOf('\n') + 1,
re = /^[ \t#>+-]*(\* )?([0-9]+\. +)?/;
if (car > cdr)
car = [cdr, cdr = car][0];
if (rev)
home = s.n1 + re.exec(ln)[0].length;
else
home = s.n1 + lf + re.exec(ln.substring(lf))[0].length;
dom_src.setSelectionRange(car, cdr);
p1 = (p1 !== home) ? home : (rev ? s.n1 : s.n1 + lf);
if (!shift)
p2 = p1;
if (rev !== p1 < p2)
dir = rev ? 'forward' : 'backward';
if (!shift)
ff_clearsel();
dom_src.setSelectionRange(Math.min(p1, p2), Math.max(p1, p2), dir);
}
@@ -434,9 +464,14 @@ function md_home(shift) {
function md_newline() {
var s = linebounds(true),
ln = s.md.substring(s.n1, s.n2),
m = /^[ \t>+-]*(\* )?([0-9]+\. +)?/.exec(ln);
m1 = /^( *)([0-9]+)(\. +)/.exec(ln),
m2 = /^[ \t>+-]*(\* )?/.exec(ln);
s.pre = s.md.substring(0, s.car) + '\n' + m[0];
var pre = m2[0];
if (m1 !== null)
pre = m1[1] + (parseInt(m1[2]) + 1) + m1[3];
s.pre = s.md.substring(0, s.car) + '\n' + pre;
s.sel = '';
s.post = s.md.substring(s.car);
s.car = s.cdr = s.pre.length;
@@ -444,6 +479,25 @@ function md_newline() {
}
// backspace
function md_backspace() {
var s = linebounds(true),
ln = s.md.substring(s.n1, s.n2),
m = /^[ \t>+-]*(\* )?([0-9]+\. +)?/.exec(ln);
var v = m[0].replace(/[^ ]/g, " ");
if (v === m[0] || v.length !== ln.length)
return true;
s.pre = s.md.substring(0, s.n1) + v;
s.sel = '';
s.post = s.md.substring(s.car);
s.car = s.cdr = s.pre.length;
setsel(s);
return false;
}
// hotkeys / toolbar
(function () {
function keydown(ev) {
@@ -484,6 +538,9 @@ function md_newline() {
action_stack.redo();
return false;
}
if (!ctrl && !ev.shiftKey && kc == 8) {
return md_backspace();
}
}
}
document.onkeydown = keydown;
@@ -507,8 +564,10 @@ document.getElementById('help').onclick = function (e) {
// blame steen
action_stack = (function () {
var undos = [];
var redos = [];
var hist = {
un: [],
re: []
};
var sched_cpos = 0;
var sched_timer = null;
var ignore = false;
@@ -553,7 +612,7 @@ action_stack = (function () {
}
function apply(src, dst) {
dbg('undos(%d) redos(%d)', undos.length, redos.length);
dbg('undos(%d) redos(%d)', hist.un.length, hist.re.length);
if (src.length === 0)
return false;
@@ -581,36 +640,36 @@ action_stack = (function () {
ignore = false;
return;
}
redos = [];
hist.re = [];
clearTimeout(sched_timer);
sched_cpos = dom_src.selectionEnd;
sched_timer = setTimeout(push, 500);
}
function undo() {
if (redos.length == 0) {
if (hist.re.length == 0) {
clearTimeout(sched_timer);
push();
}
return apply(undos, redos);
return apply(hist.un, hist.re);
}
function redo() {
return apply(redos, undos);
return apply(hist.re, hist.un);
}
function push() {
var newtxt = dom_src.value;
var change = diff(ref, newtxt, sched_cpos);
if (change !== null)
undos.push(change);
hist.un.push(change);
ref = newtxt;
dbg('undos(%d) redos(%d)', undos.length, redos.length);
if (undos.length > 0)
dbg(undos.slice(-1)[0]);
if (redos.length > 0)
dbg(redos.slice(-1)[0]);
dbg('undos(%d) redos(%d)', hist.un.length, hist.re.length);
if (hist.un.length > 0)
dbg(static(hist.un.slice(-1)[0]));
if (hist.re.length > 0)
dbg(static(hist.re.slice(-1)[0]));
}
return {
@@ -618,8 +677,7 @@ action_stack = (function () {
undo: undo,
redo: redo,
push: schedule_push,
_undos: undos,
_redos: redos,
_hist: hist,
_ref: ref
}
})();

View File

@@ -80,3 +80,22 @@ for d in /usr /var; do find $d -type f -size +30M 2>/dev/null; done | while IFS=
# py2 on osx
brew install python@2
pip install virtualenv
##
## http 206
# az = abcdefghijklmnopqrstuvwxyz
printf '%s\r\n' 'GET /az HTTP/1.1' 'Host: ocv.me' 'Range: bytes=5-10' '' | ncat ocv.me 80
# Content-Range: bytes 5-10/26
# Content-Length: 6
# fghijk
Range: bytes=0-1 "ab" Content-Range: bytes 0-1/26
Range: bytes=24-24 "y" Content-Range: bytes 24-24/26
Range: bytes=24-25 "yz" Content-Range: bytes 24-25/26
Range: bytes=24- "yz" Content-Range: bytes 24-25/26
Range: bytes=25-29 "z" Content-Range: bytes 25-25/26
Range: bytes=26- Content-Range: bytes */26
HTTP/1.1 416 Requested Range Not Satisfiable

View File

@@ -180,7 +180,7 @@ diff --git a/src/Parser.js b/src/Parser.js
+ // similar to tables, writing contents before the <ul> tag
+ // so update the tag attribute as we go
+ // (assuming all list entries got tagged with a source-line, probably safe w)
+ body += this.renderer.tag_ln(item.tokens[0].ln).listitem(itemBody, task, checked);
+ body += this.renderer.tag_ln((item.tokens[0] || token).ln).listitem(itemBody, task, checked);
}
- out += this.renderer.list(body, ordered, start);

View File

@@ -166,3 +166,6 @@ chmod 755 $sfx_out.*
printf "done:\n"
printf " %s\n" "$(realpath $sfx_out)."{sh,py}
# rm -rf *
# tar -tvf ../sfx/tar | sed -r 's/(.* ....-..-.. ..:.. )(.*)/\2 `` \1/' | sort | sed -r 's/(.*) `` (.*)/\2 \1/'| less
# for n in {1..9}; do tar -tf tar | grep -vE '/$' | sed -r 's/(.*)\.(.*)/\2.\1/' | sort | sed -r 's/([^\.]+)\.(.*)/\2.\1/' | tar -cT- | bzip2 -c$n | wc -c; done