From 953183f16df12bbf348d0d9012fb146f6f983f5e Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 8 Aug 2021 02:47:42 +0200 Subject: [PATCH] add help sections and vt100 stripper --- copyparty/__main__.py | 127 +++++++++++++++++++++++++++++++++--------- copyparty/svchub.py | 7 +-- copyparty/util.py | 3 + 3 files changed, 106 insertions(+), 31 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 6c48e099..6c772ed7 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -23,7 +23,7 @@ from textwrap import dedent from .__init__ import E, WINDOWS, VT100, PY2, unicode from .__version__ import S_VERSION, S_BUILD_DT, CODENAME from .svchub import SvcHub -from .util import py_desc, align_tab, IMPLICATIONS +from .util import py_desc, align_tab, IMPLICATIONS, ansi_re from .authsrv import re_vol HAVE_SSL = True @@ -67,8 +67,12 @@ class Dodge11874(RiceFormatter): def lprint(*a, **ka): global printed - printed += " ".join(unicode(x) for x in a) + ka.get("end", "\n") - print(*a, **ka) + txt = " ".join(unicode(x) for x in a) + ka.get("end", "\n") + printed += txt + if not VT100: + txt = ansi_re.sub("", txt) + + print(txt, **ka) def warn(msg): @@ -197,8 +201,14 @@ def run_argparse(argv, formatter): formatter_class=formatter, prog="copyparty", description="http file sharing hub v{} ({})".format(S_VERSION, S_BUILD_DT), - epilog=dedent( - """ + ) + + sects = [ + [ + "accounts", + "accounts and volumes", + dedent( + """ -a takes username:password, -v takes src:dst:perm1:perm2:permN:cflag1:cflag2:cflagN:... where "perm" is "accesslevels,username1,username2,..." @@ -210,11 +220,7 @@ def run_argparse(argv, formatter): "m" (move): move files and folders; need "w" at destination "d" (delete): permanently delete files and folders - list of cflags: - "c,nodupe" rejects existing files (instead of symlinking them) - "c,e2d" sets -e2d (all -e2* args can be set using ce2* cflags) - "c,d2t" disables metadata collection, overrides -e2t* - "c,d2d" disables all database stuff, overrides -e2* + too many cflags to list here, see the other sections example:\033[35m -a ed:hunter2 -v .::r:rw,ed -v ../inc:dump:w:rw,ed:c,nodupe \033[36m @@ -231,29 +237,84 @@ def run_argparse(argv, formatter): consider the config file for more flexible account/volume management, including dynamic reload at runtime (and being more readable w) + """ + ), + ], + [ + "cflags", + "list of cflags", + dedent( + """ + cflags are appended to volume definitions, for example, + to create a write-only volume with the \033[33mnodupe\033[0m and \033[32mnosub\033[0m flags: + \033[35m-v /mnt/inc:/inc:w\033[33m:c,nodupe\033[32m:c,nosub + \033[0muploads, general: + \033[36mnodupe\033[35m rejects existing files (instead of symlinking them) + \033[36mnosub\033[35m forces all uploads into the top folder of the vfs + \033[36mgz\033[35m allows server-side gzip of uploads with ?gz (also c,xz) + \033[36mpk\033[35m forces server-side compression, optional arg: xz,9 + + \033[0mupload rules: + \033[36mmaxn=250,600\033[35m max 250 uploads over 15min + \033[36mmaxb=1g,300\033[35m max 1 GiB over 5min (suffixes: b, k, m, g) + \033[36msz=1k-3m\033[35m allow filesizes between 1 KiB and 3MiB + + \033[0mupload rotation: + (moves all uploads into the specified folder structure) + \033[36mrotn=100,3\033[35m 3 levels of subfolders with 100 entries in each + \033[36mrotf=%Y-%m/%d-%H\033[35m date-formatted organizing + + \033[0mdatabase, general: + \033[36me2d\033[35m sets -e2d (all -e2* args can be set using ce2* cflags) + \033[36md2t\033[35m disables metadata collection, overrides -e2t* + \033[36md2d\033[35m disables all database stuff, overrides -e2* + \033[36mdhash\033[35m disables file hashing on initial scans, also ehash + \033[36mhist=/tmp/cdb\033[35m puts thumbnails and indexes at that location + + \033[0mdatabase, audio tags: + "mte", "mth", "mtp", "mtm" all work the same as -mte, -mth, ... + \033[36mmtp=.bpm=f,audio-bpm.py\033[35m uses the "audio-bpm.py" program to + generate ".bpm" tags from uploads (f = overwrite tags) + \033[36mmtp=ahash,vhash=media-hash.py\033[35m collects two tags at once + \033[0m""" + ), + ], + [ + "urlform", + "", + dedent( + """ values for --urlform: - "stash" dumps the data to file and returns length + checksum - "save,get" dumps to file and returns the page like a GET - "print,get" prints the data in the log and returns GET + \033[36mstash\033[35m dumps the data to file and returns length + checksum + \033[36msave,get\033[35m dumps to file and returns the page like a GET + \033[36mprint,get\033[35m prints the data in the log and returns GET (leave out the ",get" to return an error instead) - - values for --ls: - "USR" is a user to browse as; * is anonymous, ** is all users - "VOL" is a single volume to scan, default is * (all vols) - "FLAG" is flags; - "v" in addition to realpaths, print usernames and vpaths - "ln" only prints symlinks leaving the volume mountpoint - "p" exits 1 if any such symlinks are found - "r" resumes startup after the listing + """ + ), + ], + [ + "ls", + "volume inspection", + dedent( + """ + \033[35m--ls USR,VOL,FLAGS + \033[36mUSR\033[0m is a user to browse as; * is anonymous, ** is all users + \033[36mVOL\033[0m is a single volume to scan, default is * (all vols) + \033[36mFLAG\033[0m is flags; + \033[36mv\033[0m in addition to realpaths, print usernames and vpaths + \033[36mln\033[0m only prints symlinks leaving the volume mountpoint + \033[36mp\033[0m exits 1 if any such symlinks are found + \033[36mr\033[0m resumes startup after the listing examples: --ls '**' # list all files which are possible to read --ls '**,*,ln' # check for dangerous symlinks --ls '**,*,ln,p,r' # check, then start normally if safe - \033[0m """ - ), - ) + ), + ], + ] + # fmt: off u = unicode ap2 = ap.add_argument_group('general options') @@ -357,10 +418,22 @@ def run_argparse(argv, formatter): ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead") ap2.add_argument("--stackmon", metavar="P,S", type=u, help="write stacktrace to Path every S second") ap2.add_argument("--log-thrs", metavar="SEC", type=float, help="list active threads every SEC") - - return ap.parse_args(args=argv[1:]) # fmt: on + ap2 = ap.add_argument_group("help sections") + for k, h, _ in sects: + ap2.add_argument("--help-" + k, action="store_true", help=h) + + ret = ap.parse_args(args=argv[1:]) + for k, h, t in sects: + k2 = "help_" + k.replace("-", "_") + if vars(ret)[k2]: + lprint("# {} help page".format(k)) + lprint(t + "\033[0m") + sys.exit(0) + + return ret + def main(argv=None): time.strptime("19970815", "%Y%m%d") # python#7980 diff --git a/copyparty/svchub.py b/copyparty/svchub.py index 3f05e5b0..b03991df 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -14,7 +14,7 @@ from datetime import datetime, timedelta import calendar from .__init__ import E, PY2, WINDOWS, ANYWIN, MACOS, VT100, unicode -from .util import mp, start_log_thrs, start_stackmon, min_ex +from .util import mp, start_log_thrs, start_stackmon, min_ex, ansi_re from .authsrv import AuthSrv from .tcpsrv import TcpSrv from .up2k import Up2k @@ -41,7 +41,6 @@ class SvcHub(object): self.stop_cond = threading.Condition() self.httpsrv_up = 0 - self.ansi_re = re.compile("\033\\[[^m]*m") self.log_mutex = threading.Lock() self.next_day = 0 @@ -279,9 +278,9 @@ class SvcHub(object): if not VT100: fmt = "{} {:21} {}\n" if "\033" in msg: - msg = self.ansi_re.sub("", msg) + msg = ansi_re.sub("", msg) if "\033" in src: - src = self.ansi_re.sub("", src) + src = ansi_re.sub("", src) elif c: if isinstance(c, int): msg = "\033[3{}m{}".format(c, msg) diff --git a/copyparty/util.py b/copyparty/util.py index 3450d000..016b6e96 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -58,6 +58,9 @@ except: return struct.unpack(f.decode("ascii"), *a, **ka) +ansi_re = re.compile("\033\\[[^m]*m") + + surrogateescape.register_surrogateescape() FS_ENCODING = sys.getfilesystemencoding() if WINDOWS and PY2: