From 34c856e83843275f2249d84750b7a07b34c553ef Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 2 Jul 2026 20:34:48 +0000 Subject: [PATCH] md-edit: md_nhist (max num backups) --- copyparty/__main__.py | 3 ++- copyparty/authsrv.py | 2 +- copyparty/cfg.py | 4 +++- copyparty/httpcli.py | 10 ++++++++++ tests/util.py | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 435bfcc2..c2daf91c 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1904,7 +1904,8 @@ def add_txt(ap): ap2 = ap.add_argument_group("textfile options") ap2.add_argument("--rw-edit", metavar="T,T", type=u, default="md", help="comma-sep. list of file-extensions to allow editing with permissions read+write; all others require read+write+delete (volflag=rw_edit)") ap2.add_argument("--md-no-br", action="store_true", help="markdown: disable newline-is-newline; will only render a newline into the html given two trailing spaces or a double-newline (volflag=md_no_br)") - ap2.add_argument("--md-hist", metavar="TXT", type=u, default="s", help="where to store old version of markdown files; [\033[32ms\033[0m]=subfolder, [\033[32mv\033[0m]=volume-histpath, [\033[32mn\033[0m]=nope/disabled (volflag=md_hist)") + ap2.add_argument("--md-hist", metavar="TXT", type=u, default="s", help="where to store old version of textfiles; [\033[32ms\033[0m]=subfolder, [\033[32mv\033[0m]=volume-histpath, [\033[32mn\033[0m]=nope/disabled (volflag=md_hist)") + ap2.add_argument("--md-nhist", metavar="TXT", type=int, default=99, help="how many old versions of textfiles to keep; 0=infinite (volflag=md_nhist)") ap2.add_argument("--show-hist", action="store_true", help="allow browsing the .hist folder; enable to view old markdown files (volflag=show_hist)") ap2.add_argument("--txt-eol", metavar="TYPE", type=u, default="", help="enable EOL conversion when writing documents; supported: CRLF, LF (volflag=txt_eol)") ap2.add_argument("-mcr", metavar="SEC", type=int, default=60, help="the textfile editor will check for serverside changes every \033[33mSEC\033[0m seconds") diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 92132578..3319435f 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -2464,7 +2464,7 @@ class AuthSrv(object): if vf not in vol.flags: vol.flags[vf] = getattr(self.args, ga) - zs = "forget_ip gid nrand tail_who th_qv th_qvx th_spec_p u2abort u2ow uid unp_who ups_who zip_who" + zs = "forget_ip gid md_nhist nrand tail_who th_qv th_qvx th_spec_p u2abort u2ow uid unp_who ups_who zip_who" for k in zs.split(): if k in vol.flags: vol.flags[k] = int(vol.flags[k]) diff --git a/copyparty/cfg.py b/copyparty/cfg.py index c4cb5f0b..29e254d9 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -123,6 +123,7 @@ def vf_vmap() -> dict[str, str]: "lg_sba", "md_sba", "md_hist", + "md_nhist", "nrand", "u2ow", "og_desc", @@ -407,7 +408,8 @@ flagcats = { "textfiles": { "rw_edit=md,txt": "only require read+write to edit .md and .txt", "md_no_br": "newline only on double-newline or two tailing spaces", - "md_hist": "where to put markdown backups; s=subfolder, v=volHist, n=nope", + "md_hist": "where to put text-editor backups; s=subfolder, v=volHist, n=nope", + "md_nhist=99": "how many text-editor backups to keep", "show_hist": "allow browsing the .hist folder (old textfiles)", "exp": "enable textfile expansion; see --help-exp", "exp_md": "placeholders to expand in markdown files; see --help", diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 711cf8dc..8b1f3fbe 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -4172,6 +4172,16 @@ class HttpCli(object): pass if dp: atomic_move(self.log, fp, os.path.join(dp, mfile2), vfs.flags) + nmax = dbv.flags["md_nhist"] + if nmax: + zs = "%s\.[0-9]+\.[0-9]{3}\.%s" + ptn = re.compile(zs % (re.escape(fname), re.escape(fext))) + zsl = [x for x in os.listdir(dp) if ptn.match(x)] + zsl.sort(reverse=True) + while len(zsl) > nmax: + zs = os.path.join(dp, zsl.pop()) + self.log("rm %r" % (zs,)) + wunlink(self.log, zs, vfs.flags) assert self.parser.gen # !rm p_field, _, p_data = next(self.parser.gen) diff --git a/tests/util.py b/tests/util.py index d50ab383..746e08fb 100644 --- a/tests/util.py +++ b/tests/util.py @@ -163,7 +163,7 @@ class Cfg(Namespace): ex = "ac_convt au_vol dl_list du_iwho mcr mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt th_qv th_qvx ups_who ver_iwho zip_who" ka.update(**{k: 9 for k in ex.split()}) - ex = "ctl_re db_act forget_ip gauto idp_cookie idp_store k304 loris no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs" + ex = "ctl_re db_act forget_ip gauto idp_cookie idp_store k304 loris md_nhist no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs" ka.update(**{k: 0 for k in ex.split()}) ex = "ah_alg bname chdir chmod_f chpw_db csp_dl csp_ui db_xattr doctitle df epilogues exit favico fika ipa ipar html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_date log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts preadmes prologues readmes shr shr1 shr_site site smsg tcolor textfiles th_pregen txt_eol ufavico ufavico_h unlist up_site vc_url vname xff_src zipmaxt R RS SR"