The hidden engine self-tests had accreted into a grab-bag of arbitrary
single-letter/-digit -# arms (-#0, -#A, -#W, ...) buried in the htscoremain.c
option switch, with no mnemonics and stale --help text. Collapse them into one
registry: -#test lists every test with a usage hint and one-line description,
and -#test=NAME [args] runs one.
The handlers and the two helpers they used (basic_selftests,
string_safety_selftests) move to a new htsselftest.c keyed by a
{name, args, desc, fn} table; htscoremain.c keeps only a small dispatch that
runs ahead of the no-URL usage gate, so a bare -#test (or an arg-less test like
copyopt/dns/cookies) no longer needs a dummy URL token to be reached. The
genuine debug knobs (-#L, -#C, -#R, -#h, ...) stay as letters in the switch;
only the unit self-tests, whose sole callers are tests/01_engine-*.test, are
renamed, so this is internal-only with no compatibility surface. Behavior is
preserved: each test prints the same result line and exit code, which the
existing assertions pin. Three now-unused includes (htscache_selftest.h,
htsdns_selftest.h, htsencoding.h) drop out of htscoremain.c.
Tests: the engine tests move to -#test=NAME; 01_engine-hashtable now asserts its
success line (not just exit code) so a misrouted registry row can't pass, and a
new 01_engine-selftest-dispatch covers the bare-list and unknown-name paths.
The --help/man "guru options" list now points at -#test instead of enumerating
a stale subset. The lone vestigial alias --debug-testfilters still resolves to
the removed -#0 (it was already non-functional: param1 supplies one argument,
-#0 required two); it is left untouched because editing that array forces
clang-format to reflow the whole untouched table.
Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
A failed write to the new.zip cache (zipOpenNewFileInZip / zipWriteInFileInZip /
zipCloseFileInZip / zipFlush returning non-Z_OK) was a fatal assertf() that
aborted the whole process and popped CRASH.TXT. The trigger is storage going
away mid-crawl: a disk filling up overnight (#174) or a network share holding
the mirror dropping (#219); WinHTTrack users commonly mirror to a NAS or mapped
drive.
The cache lives in the same output tree as the mirror, so a cache write failing
means the mirror files can no longer be written either. Continuing would only
produce a broken, incomplete mirror reported as success. So treat it the same
way the engine already treats a failed mirror-file write (htscore.c:1961,
htsback.c:2933): log the error and set opt->state.exit_xh = -1 to stop the
mirror cleanly and exit non-zero. No crash, no CRASH.TXT.
Route the cache_add() write sites through cache_zip_write_failed(), which logs
once (the standard "disk full or filesystem problems" message when
check_fatal_io_errno() confirms it) and flags the cache so sibling cache_add()
calls don't re-enter the broken stream before the loop notices exit_xh. The flag
is appended to the end of the engine-owned, non-installed struct cache_back, so
the ABI is unchanged.
Add an in-process self-test (httrack -#W) that drives cache_add() into a ZIP
whose disk-full backend fails its writes; 01_engine-cache-writefail.test asserts
httrack signals a fatal abort instead of crashing. Negative controls proven:
reverting the fix makes -#W abort (SIGABRT); dropping the exit_xh assignment
makes the test fail on the abort-signal check.
Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>