Debian Package Tracker
Register | Log in
Subscribe

httrack

Copy websites to your computer (Offline browser)

Choose email to subscribe with

general
  • source: httrack (main)
  • version: 3.50.1-1
  • maintainer: Xavier Roche (DMD)
  • arch: all any
  • std-ver: 4.7.4
  • VCS: Git (Browse, QA)
versions [more versions can be listed by madison] [old versions available from snapshot.debian.org]
[pool directory]
  • o-o-stable: 3.49.2-1.1
  • oldstable: 3.49.4-1
  • stable: 3.49.6-1
  • testing: 3.49.23-1
  • unstable: 3.50.1-1
versioned links
  • 3.49.2-1.1: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
  • 3.49.4-1: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
  • 3.49.6-1: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
  • 3.49.23-1: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
  • 3.50.1-1: [.dsc, use dget on this link to retrieve source package] [changelog] [copyright] [rules] [control]
binaries
  • httrack
  • httrack-doc
  • libhttrack-dev
  • libhttrack3
  • proxytrack
  • webhttrack
  • webhttrack-common
action needed
36 new commits since last upload, is it time to release? normal
vcswatch reports that this package seems to have new commits in its VCS but has not yet updated debian/changelog. You should consider updating the Debian changelog and uploading this new version into the archive.

Here are the relevant commit logs:
commit ecf31b2c8047a5340138b70ca7ccfb2bb5a3a187
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 22:47:11 2026 +0200

    An .arc record's declared length can reach into the record stored after it (#1526)
    
    An `.arc` record's declared length is read out of the archive. A record
    that inflates it is served bytes belonging to the record stored after
    it. That page's URL line, its `Content-Type`, its `Location`, its `Etag`
    and its body all go out under the first record's URL. `proxytrack
    --convert` copies them into the archive it writes, and a client GET
    receives them. Whoever hands ProxyTrack an `.arc` to serve picks that
    length.
    
    `PT_LoadCache__Arc` now refuses a record whose declared data holds
    another record's line, counted by where that line starts. A line ending
    past the declared end still has its front served, so where it ends
    decides nothing.
    
    It hit the same trap three times, because the loader and the scan
    disagreed on what a record line is. The scan's was narrower. A record
    the loader indexes and serves under its own URL was then invisible to
    the scan, and a neighbour could swallow it. Three fields rather than
    five was one way through. So was a length `sscanf` reads but a digit run
    does not. So was one byte `linput` drops, sitting in front of an
    ordinary line.
    
    The scan now asks the question the loader asks, `getArcLength` plus a
    scheme it can serve. On top of that it takes the arc shape, for the
    records the loader never serves. Heritrix writes one `dns:` record per
    host, and those bound their neighbour all the same.
    
    The scan also builds each line the way the loader's own `linput` does.
    That is the one in `proxytrack.h`, not the `htslib.c` one proxytrack
    never links. It drops NUL along with TAB, CR and form feed. A record
    line carrying a NUL is therefore indexed and served, and a scan keeping
    the NUL sees a C string that ends at it.
    
    Widening the scan rather than narrowing the loader is deliberate.
    Narrowing the loader would drop records httrack reads today out of
    third-party archives, and the shapes involved are ones real writers
    emit. The cost of widening is named below instead.
    
    Refusing a record no longer costs the archive the records after it. The
    walk resumes at the declared end and judges what it finds there as it
    judges every other record. Nothing about the refusal decides whether the
    walk goes on. A fixture whose next record line carries a trailing space
    loaded nothing at all under the previous attempt. It now loads
    everything except the greedy record itself.
    
    The scan reads the record's data forward rather than seeking back over
    it. Over an archive of 10000 small records, 658969 bytes, it reads
    672676 bytes, exactly what the current release reads, against 1990127
    for the previous attempt. That parity holds only where the release
    already reads the data. A record large enough for the release to seek
    past its body is read in full instead. One 64MB record costs 67115953
    bytes against the release's 10985.
    
    A page holding a line the loader would itself take for a record loses
    its place in the archive. Over an archive built from every
    documentation, man and HTML file on one machine, 2 records of 6827 are
    refused. The check does not cover a record the walk parses but never
    indexes, such as an undated `gopher:` line. A greedy neighbour can still
    swallow that. The boundary the check draws is narrower than the release
    behaves today.
    
    Test 414 crafts an honest archive and the lying ones beside it, then
    runs both consumers over each. It covers a length aligned to a later
    record's separator, and one ending inside the next record's line. It
    covers dropped bytes in front of a record line, three-field and
    ten-field lines, and lengths a digit run rejects. It covers a line
    straddling the scan's buffer or closing the file, and archives where the
    record after a refusal must survive. Both the current release and the
    previous attempt fail it. Of 500 generated mutants, 305 build and 171 of
    those die. Sixty of the survivors sit in the reader's header parsing,
    which this change does not touch. The rest mostly widen the check, which
    costs pages rather than leaking them. One mutant makes the scan loop
    forever, and only the suite's wall-clock guard reports it. That guard is
    the mechanism for non-termination, so the reader needs no assertion of
    its own.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 8e3aefd72fce55f70ade29226a658384bb8dc182
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 20:06:22 2026 +0200

    The emulated leg times out where it used to pace itself, and reads argv[0] off a child (#1538)
    
    The `emulated suite (s390x)` leg has been red on master since #1534. It
    is the only place the suite runs big-endian. It is also not in the
    required set, so a red one loses that coverage without blocking
    anything.
    
    Two tests fail, for unrelated reasons.
    
    `397_configure-auto-features` hits its 900s guard. It already paced
    itself with `skip_if_out_of_budget`, but only after its last three
    steps, and #1534 added a debian/rules section ahead of them. A configure
    run costs about thirty times more under qemu-user than it does natively.
    The first paced step stopped fitting, so the guard fired before any
    pacing ran. It now paces after every step, and projects against the
    costliest step so far rather than the one just run. These steps are
    ordered by what they mean rather than by what they cost, so a cheap
    neighbour under-projects the slow step after it. On master at a 25s
    budget the test runs 34s before skipping, which the guard would kill.
    With this it skips at 16s.
    
    `257_testlib-asserts` pinned the relay's image name to `sh`. binfmt_misc
    leaves the interpreter in argv[0] and shifts the program one place
    right, which is the shape `tools/emulated-suite.sh` asserts this leg
    runs on. So under qemu the relay's cmdline names the emulator. The pid
    half is what the case is for, and it was never in doubt. The test now
    asserts the pid always, and the image only where a child's own cmdline
    names the program it runs. A mutant giving that child a binfmt-shaped
    argv[0] takes the other branch.
    
    Nothing native changes: the suite still runs all fourteen steps of 397.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 823d98b0ed6fb7ab2e6e5a0b5a1de9fd14452f7e
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 17:32:32 2026 +0200

    Twenty tests ask the shell what platform it is, and under WSL2 the shell is not the target (#1536)
    
    Under the wsl2 backend the shell answers `Linux` while the binary under
    test is still a native httrack.exe. Around twenty tests were keyed on
    that shell answer, so they would have picked the POSIX expectation and
    passed against a Windows binary. A test that passes while asserting the
    wrong thing is worse than one that fails. The skip ratchet cannot see it
    either, because those tests never skip.
    
    Two kinds, and they want opposite predicates. Eight files chose a
    Windows-or-POSIX expected value from `HTS_OS`, and now ask
    `target_is_windows`, tested before the `Linux)` arm a wsl2 shell would
    otherwise match. Nine guarded a Linux-only mechanism: ptrace, /proc, or
    an LD_PRELOAD interposer sitting in the engine's own process. Those ask
    the new `target_is_linux`, because a Linux shell is not enough when the
    target is an exe.
    
    Three sites were deliberately left alone, and the reasons matter more
    than the count. `19_local-connect-fallback.test` switches on GNU/Hurd
    having a single loopback address, which has nothing to do with Windows.
    `218_crash-nopie-frames.test` and `372_crash-signal-number.test` compile
    their own ELF probes with the local compiler and never invoke httrack,
    so they genuinely want the shell.
    
    The ratchet holds one skip list per backend now. An unknown backend is a
    loud error rather than an empty list. An empty expectation makes every
    skip look expected, so the instrument would stop working without saying
    so. The wsl2 list starts as a copy of the msys one, labelled in the file
    as a prediction. The first real wsl2 run is what corrects it.
    
    Nothing here changes behaviour on Linux or under msys, which is the
    point. `target_is_linux` reduces to the old `HTS_OS` test on both, and
    the msys skip list is byte-identical. The suite reads 433 total, 419
    pass, 14 skip, 0 fail, against 432 and 418 before, the difference being
    the new test.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 96986df3bc1fc77cb5bdd32451a317813aadc4ff
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 16:46:55 2026 +0200

    The Windows suite can only run under MSYS, whose fork emulation breaks first under load (#1535)
    
    MSYS's fork emulation is what breaks first when the Windows suite is
    under load (#1273). This adds a second harness against the same native
    httrack.exe: a Linux shell under WSL2. It spawns 500 processes in 1s
    where MSYS takes 7s on windows-2022 and 28s on windows-2025. WSL2 comes
    up in about 35 seconds with no reboot, because the three Windows
    features it needs are already enabled on the hosted images.
    
    Those numbers time spawns to completion, so they measure throughput and
    say nothing about a job that never finishes. #1531 is still what decides
    whether forks provoke the runner wedges. This is worth doing either way,
    because a harness that does not depend on fork emulation survives that
    question concluding anything.
    
    Both backends stay live behind `HTTRACK_SUITE_BACKEND`, so the msys path
    is untouched and a regression is a variable flip rather than a revert.
    Deleting the MSYS side is a later change.
    
    `is_windows()` answered two questions at once. A survey of all 135 call
    sites found 90 asking whether the binary under test is a Windows exe,
    which stays true under WSL2. Only 6 ask whether the shell is MSYS, which
    does not. It is deleted rather than redefined, so a site nobody
    converted is a command-not-found instead of a silently wrong answer.
    
    The one primitive WSL2 lacks is `/proc/<pid>/winpid`, because interop's
    Linux-side child is only a relay. A spike on a hosted runner proved two
    things. The Windows process can be found through a marker already on its
    command line, and the whole tree then dies. Run:
    https://github.com/xroche/httrack/actions/runs/33855598109. That marker
    travels to PowerShell in the environment rather than in the argument
    list. Passed as an argument it would sit on the querying process's own
    command line, and the query would match itself.
    
    Two bugs of the same shape turned up on the way. `proclib.sh`'s
    forensics kept a bare `tasklist`, which under WSL2 returns nothing and
    calls the host clean. `58_watchdog.test` ran a bare `ping`, where WSL's
    Linux one reads `-n` as numeric output rather than a count.
    
    The suite's skip ratchet measures the rest, because it diffs the skip
    set both ways. The new leg therefore reports exactly which tests changed
    status, rather than leaving it to be noticed. That leg does not gate the
    job yet, since a red read against no history tells nobody anything. Flip
    `continue-on-error` once it has a few days behind it.
    
    The cost is one more Windows job per run, and every added Windows job is
    one the runner can kill.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit efebf062b8bfac2dccc2218934b04973685300a9
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 15:32:40 2026 +0200

    Measure which workload provokes the Windows runner kill (#1531)
    
    #1228 ended on a hypothesis it called unsettleable from our side, that
    the kill tracks MSYS process creation over loopback socket churn. It is
    settleable as an A/B, and this is that experiment.
    
    Three arms spend the same event budget on a different kind of event. One
    makes MSYS forks alone. One makes loopback connections alone from
    PowerShell, so no MSYS process exists. One makes a process and a
    connection per iteration, which is the shape the suite has when it
    spawns httrack.exe. No arm builds the engine, so a probe job is dose and
    nothing else. Only one arm runs at a time. The job pool is shared and
    already queues, so three more Windows jobs an hour would cost the
    control its samples.
    
    The control is the live windows-build suite, and
    `tools/kill-probe-report.sh` prints it beside the arms, because a window
    whose control shows no kill measures nothing. At roughly 5% per
    uncensored job, hourly runs give about 40 exposures per arm in two days.
    That separates an arm that kills from one that does not, and it will not
    split two arms that both do.
    
    Writing the census turned up two things. GitHub drops a job's step
    records after about a week, and the wedge signature lives in them. An
    older kill used to come back as a clean job, so it is recognised by
    runtime instead. The lease reaps a lost job at around 45 minutes, where
    a real failure ends in fifteen. The rate has also moved. Over 28 August
    to 4 September the control reads 47 kills in 364 uncensored jobs,
    against the 5.1% #1228 measured in July.
    
    The probe stops itself on 2026-10-06 rather than leaving an hourly cron
    nobody remembers. A PR touching these files runs the arms at a smoke
    dose. The first such run caught two arms of three spending nothing at
    all.
    
    One correction to `windows-runner-death/MEASUREMENTS.md` in
    httrack-works: Defender is out of the picture, because the runner images
    put it in passive mode by design. What is left of that hypothesis is the
    Windows Filtering Platform and the MSYS fork path.
    
    ---------
    
    Signed-off-by: Xavier Roche <xroche@gmail.com>
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit c70871df11dd7f0e3080f89bdef09487a56284d5
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 14:59:40 2026 +0200

    Nothing checks that debian/rules still names every optional feature (#1534)
    
    Three follow-ups a review of #1509 left open.
    
    `debian/rules` passes `--disable-auto-features` and then names the four
    optional features by flag, which makes that list the whole optional set.
    Nothing checked the list, so deleting `--with-zstd` from it would have
    shipped a package that cannot decode zstd and stayed green. 397 reads
    the flags out of `debian/rules` now and requires every feature it knows
    to be named.
    
    Six review rounds went at this check and the first five each got through
    it. Three readers in a row lost to make and shell syntax, so the check
    no longer reads `debian/rules` at all. It runs the recipe with every
    `dh_*` stubbed and records what `dh_auto_configure` is handed. Make
    expands the variables and picks the recipe, the shell splits the words,
    and only a list of tokens is left to judge.
    
    What walked past one reader or another, all of it now pinned by a
    fixture:
    
    - `--with-zstd=no`, `--without-zstd` behind the positive flag, and a
    value behind a make variable
    - a `--with-zstd` commented out mid-continuation, and a backslash with a
    space after it that make does not join
    - a fallback invocation that never runs, and a second one that overrides
    the first
    - `--with-zstd=auto`, which hands the decision back to the buildd and is
    the lottery the flag list exists to end
    - a `dh_auto_configure` under `build-arch`, which is what
    `dpkg-buildpackage` runs for an arch:any package, or behind a `dh`
    sequencer's override target
    - a flag behind an `ifeq` on `DEB_HOST_ARCH_OS`
    - `--disable-https` and `--enable-https=no`, which no amount of checking
    the four auto features would see
    - a `dh_auto_configure` in an arch-only override or an `execute_after_`
    hook, and a `./configure` run that is not `dh_auto_configure` at all
    
    Three bounds are deliberate. The recorded flags are this host's
    expansion. A second pass under a foreign `DEB_HOST_ARCH_OS` is a sample
    rather than a proof. A flag gated on a file in the source tree escapes
    it. A feature dropped by removing its `Build-Depends` makes the build
    fail loudly, so it needs no catching here. Every recorded invocation has
    to carry the whole list, which would red a rules file that configures a
    second time for something else. A configure hidden in `binary` or
    `install` is out of reach, because those recipes move files no scratch
    tree has and stop before it.
    
    Thirty-two fixtures pin the reader, written as literal text. Built by
    expansion instead, the `--with-zstd="no"` case came out with an escaped
    quote under bash 3.2 and read as passed on the macOS leg alone.
    
    Four of them measure rather than assert. A broken recipe reaches make
    either as a failure or as a silently shorter flag list, and which one
    depends on the host. A comment inside a continuation reds on Linux and
    grades as a short list on macOS. Those four run their makefile through a
    plain make first, then require the reader to report what that run did.
    GNU make 3.81 built from source agrees with 4.4.1 here on all
    thirty-two, under `/bin/sh`, bash and dash alike. So the difference is
    neither the make version nor the shell, and I have not identified it.
    
    Two error arms configure has and 397 did not exercise. A bare
    `--with-iconv` has to fail the build where `iconv_open` does not link,
    rather than fall back to the codepage tables. The fixture is a header
    declaring the symbol under a name nothing provides. That is what a
    renaming libiconv header without its library looks like.
    `--enable-backtrace=bogus` has to be refused the way
    `--enable-auto-features=maybe` already is.
    
    `feature_public` returned an exit status where its five siblings echo a
    string. It echoes now, and it echoes nothing for a feature whose macro
    stays out of the installed header. That is the shape `feature_libs` and
    `feature_pc` already use for the same idea.
    
    ---------
    
    Signed-off-by: Xavier Roche <xroche@gmail.com>
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit c9840a479a2200b269ee26d354c9f0e473bfb5de
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 14:21:35 2026 +0200

    Three option defaults documented wrongly, and a FAQ that denies SOCKS exists (#1520)
    
    Six factual corrections to the option documentation. They come from
    cross-referencing every switch in `htscoremain.c` against the man page,
    the `html/` guide pages and both GUI front ends.
    
    `--help` starred the wrong line twice. `-R`'s default is 2 (`opt->retry
    = 2` in htslib.c), not the `*R1` shown. The default log mode is
    `httrack_logmode = 3`, which is what `-f2` selects, so that star
    belonged on `f2` rather than on `f`. Both were checked by running crawls
    rather than only by reading the initialiser: a default run leaves
    `hts-log.txt` alone, and `-f` also leaves `hts-err.txt`.
    
    `man/httrack.1` and `html/httrack.man.html` are generated from `--help`,
    so they carried both errors. Regenerating them is why the diff touches
    four files for three text changes.
    
    `html/fcguide.html` answered "Is there any SOCKS support?" with "Not
    yet!". SOCKS5 and HTTP CONNECT have shipped and `faq.html` was corrected
    for them, but fcguide was missed. The same page gives the maximum filter
    count as 500 against a default of 200, and spells the footer option
    `-&F`. That spelling works, because `-&` is an undocumented synonym for
    `-%`, but it appears nowhere else in the tree.
    
    Some findings were left alone on purpose. The `-C` default is genuinely
    ambiguous. C1 and C2 cannot be told apart on a first crawl, and the
    value from the second run on is what the help already says. Four
    alias-table rows point at the wrong option. `--extract-cache` reaches
    the cache lister rather than the extractor, and `--autotest` runs a
    mirror rather than the autocheck. Those change what a command line does,
    so they belong in a code PR. Most of fcguide's remaining wrong numbers
    sit inside a verbatim paste of the 2001 help text. Correcting one number
    there is an editorial decision about the page rather than a fact fix.
    
    One earlier finding did not survive checking. `verbosedisplay` is set to
    `HTS_VERBOSE_SIMPLE` on a terminal before any option is parsed, so the
    `* %v1` star is right.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit d04b597c3888311e2c100c6c75ca415374f4f441
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 12:03:29 2026 +0200

    A test that cannot run its subject reports PASS instead of a skip (#1533)
    
    `399_deb-build-deps.test` ends at `exit 0` when PyYAML is absent, so
    automake records a PASS for a test that asserted nothing.
    `113_engine-threadattr-leak.test` does the same off Linux and on a
    `--disable-shared` build, and `253_local-ftp-close-once.test` does it on
    a `--disable-shared` build. All six sites call `skip` now, which exits
    77.
    
    `278_install-headers-msvc.test` is a seventh, and a review found it
    rather than the scan below. Its staging leg needs a C compiler and its
    workflow audit needs PyYAML. With both absent, the only thing still
    running is two file-existence checks. Those are real assertions, which
    is why nothing mechanical can see it. It counts its legs now and skips
    when neither ran.
    
    The line is whether anything ran before the gate. A test that asserts,
    then bails out of a leg it cannot reach, has proved something, so PASS
    is honest there. About twenty do that and stay untouched. A test that
    bails before its first assertion has proved nothing.
    
    I enumerated every `exit 0` under `tests/`, then reproduced each missing
    prerequisite and read the verdict back:
    
    - a `PYTHONPATH` pointing at a `yaml.py` that raises `ImportError`
    - a `python3` shim that exits 1
    - a second tree configured `--disable-shared`
    - `HTS_OS=Darwin`
    - an `abs_top_srcdir` carrying neither `debian/` nor `.github/`
    
    Each of the six sites reported PASS before and reports SKIP after.
    
    The Windows job pins its skip set, which makes it a control the Linux
    runs cannot be. The sweep moved exactly one test into it.
    `113_engine-threadattr-leak.test` was greening there without running its
    subject. It counts a pthread_attr init/destroy imbalance through an
    LD_PRELOAD interposer, and MSYS has no equivalent. The job sets no
    `THREADATTRFAIL_LA` either, so the Linux gate is the only thing keeping
    the test off its own "was not built" arm.
    `253_local-ftp-close-once.test` interposes the same way and has been in
    that list since it was written.
    
    `418_test-vacuous-skip.test` is the guard. It reports an `exit 0` a test
    can reach before anything that could have failed that test. What runs
    inside the gate's own `if` counts as part of the gate, not as evidence.
    Over the 430 tests on master it names those three files and nothing
    else, and 278 is the case it cannot reach.
    
    Its controls earn their place. The first scan was itself vacuous,
    because awk carried its tally across files, so the first file's
    assertions vouched for every file behind it. An adversarial review then
    broke it eight more ways in both directions, `|| { exit 0; }` and a
    `case` arm and a trailing comment among them. Twelve fixtures pin it
    now, six that must be reported and six that must not, and gawk, mawk and
    busybox awk agree on all twelve.
    
    Two bounds remain. An assertion written on the gate's own line still
    counts as prior work, and the word `ok` or `fail` inside a string reads
    as one. A test can slip past on either. The remedy is the one a real
    finding gets: write `skip` instead of `exit 0`, or teach the scan the
    idiom.
    
    ---------
    
    Signed-off-by: Xavier Roche <xroche@gmail.com>
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 3fe462e75a0a94031ce9f8227853f138228e000a
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 09:30:36 2026 +0200

    A cached X-Size sizes the buffer ProxyTrack reads a disk-held body into (#1517)
    
    ProxyTrack reads a cache entry's `X-Size` out of the zip index and hands
    it to `malloc(r->size + 1)` for entries whose body sits on disk
    (`X-In-Cache: 0`). Nothing caps it between the header and the
    allocation, so the header decides the size. Where `size_t` is 32 bits,
    `X-Size: 4294967295` wraps that sum and `malloc(0)` returns a region
    with no usable bytes. `hts_fread_exact` then fills it with the file's
    contents, and `r->adr[r->size]` writes the terminator below the
    allocation.
    
    The engine already refuses this. `htscache.c` gates the same read at
    `r.size >= INT_MAX` before it opens the body file, because either way
    the body is read whole into memory. ProxyTrack reads the engine's own
    caches and had no such gate. #1389 validated the decode (negative, wider
    than `size_t`, in-cache above `INT_MAX`) and left the on-disk body path
    uncapped. The gate now sits where the engine puts it, after the
    save-name check and before the open. A declared body of 2 GB or more
    comes back as `Cache Read Error : Bad Size` instead of a multi-gigabyte
    allocation.
    
    `tests/402_local-proxytrack-cache-bodysize.test` forges four sizes.
    Three are the honest one, the `INT_MAX` boundary and `SIZE_MAX` on a
    32-bit build. The fourth is a size the reader can still hold that the
    file is far too short to satisfy. It matters because a fix that dropped
    every short-file entry would pass the other three.
    
    The wrap is 32-bit only and this box has no 32-bit toolchain, so it was
    pinned outside the suite. A probe runs the reader's arithmetic with
    `size_t` narrowed to 32 bits and allocates flush against a `PROT_NONE`
    page. At `X-Size: 4294967295` the computed size is 0 and the fill takes
    SIGSEGV on its first byte, while the honest size does not fault. ASan
    cannot stand in for that, because it does not police a write into a
    `malloc(0)` region.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Signed-off-by: Xavier Roche <xroche@gmail.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 6c392115b3fbba644ebc805aec5e20721d6efa42
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 08:34:47 2026 +0200

    Test 84 waits for a directory that the profile save already created (#1529)
    
    `84_webhttrack-mirror-verbatim.test` failed on master's `build (Ubuntu
    devel, uutils coreutils)` leg with "the crawl ended before the probe, so
    this proves nothing". The uutils coreutils angle is a red herring,
    because the assertion greps a file and none of the SIGPIPE differences
    apply.
    
    The test has to fetch `/website/` while a crawl runs. A finished crawl
    serves that path too, so the probe would pass on an unfixed server. The
    test waited for the crawl's project directory to appear, then read the
    pane once. But `command_do=start` saves the profile before it launches
    the engine, and the save on its own creates that directory. Sending
    `command_do=save` alone confirms it. The directory appears, and the pane
    correctly reports nothing in progress. The wait therefore never proved
    the engine was running, and a slow start on a loaded runner failed the
    single read.
    
    It now waits on the pane, which moves only once the engine runs, and
    dumps the server log if the crawl never shows up. Mutated to never start
    the engine, the guard still fires.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 1c950aa80bedc8a8ee1538a275391c2014e4cffa
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 08:32:56 2026 +0200

    hdiutil create loses a race on the new macOS runner image (#1530)
    
    Master's `macOS app bundle (arm64)` leg failed twice tonight at "Pack
    again under a channel label" with `hdiutil: create failed - Resource
    busy`. The 62 runs before those were green. The break lines up with the
    runner image moving to macos-15-arm64 20260829.0321.1. The last green
    run on the old image was 2026-09-03 18:25, and on the new image the leg
    has gone red, green, red.
    
    The job runs `tools/macos-release.sh` three times back to back, and the
    second pack is the one that dies. hdiutil attaches the image while it
    fills it, and that attach collides with the previous run's helper. The
    runner then reports an orphan `diskimages-help` process at cleanup.
    
    The same script builds the DMG the release attaches, so this costs a
    release build and not only a CI leg. It now retries up to four times,
    and a permanent failure still stops with hdiutil's own message. No
    Linux-runnable test covers a macOS packaging path, so I proved the loop
    against a stub hdiutil. It succeeds on the first try, survives three
    transient failures, and still exits 1 when hdiutil never succeeds.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 0340bdff563107a78931d9db10c3a7bfa3c1be21
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 07:45:10 2026 +0200

    htsweb: stale Winsock-init comment on an empty callback (#1511)
    
    `htsshow_init()` has an empty body. The comment above it, `/* Initialize
    the Winsock */`, describes work the function no longer does.
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit efc1514a1fa068e67bd253845fd1cd731351102f
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 06:07:52 2026 +0200

    Ten early exits from hts_main_internal free nothing (#1528)
    
    `hts_main_internal()` allocates a 64 KB `url` buffer and a token block
    for the rebuilt command line, and `htsmain_free()` releases both. Every
    error exit expands the macro. Ten exits that end the run without an
    error do not. Under an ASan build, `-#h` reports 98392 bytes leaked in 5
    allocations, against a clean `-h`. `-#C`, `-#E`, `-#R` and `-#t` report
    the same block. Seven of the ten return 0 and three return 1, so what
    they share is skipping the macro, not the status they exit with.
    
    The macro now expands `freet` instead of raw `free`. The tree's
    conventions ask for that anyway, and it makes a second expansion a
    no-op. `cmdl_free()` was already idempotent, and the tail of the
    function already called it twice.
    
    Four more leaks sit on those same paths, and the macro covers none of
    them, so they are fixed here. The `-#C` handler never releases the
    coucal hashtable it builds, nor `cache.zipInput`, the archive
    `cache_init()` opens, which costs another 352 bytes against a real
    cache. `hts_extract_meta()` drops the meta-data handle when the cache
    will not open, and the cache handle when the meta-data file cannot be
    created.
    
    One leak is left alone. The `-#C` loop calls `cache_read_ro()` per entry
    and never frees the `r.adr` its caller owns. That loop is gated on
    `hts-cache/new.ndx`, a legacy index no code in the tree writes any more.
    No fixture reaches it, and a fix there would ship unexercised.
    
    `tests/283_engine-cmdline-leak.test` was written for this class and
    could not see it, because both of its cases were error exits. It now
    drives all ten sites and all four extra leaks. Each case asserts a
    message only that handler prints, plus the exit status. The closing
    "Thanks for using HTTrack" line must be absent, which proves the run
    left early instead of reaching the exit that already frees. Deleting one
    release at a time reds the test for all fourteen.
    
    Two cases needed a fixture rather than a flag. The aborted-mirror notice
    is guarded by `!opt->quiet`, and a run whose stdout is not a terminal
    sets `quiet`. That case therefore goes through `script(1)`. Its
    util-linux and BSD spellings do not overlap. The test probes both with a
    command only a terminal passes, and skips the case when neither works.
    The `-#E` case that has to leave the cache open makes `hts-cache`
    read-only. It verifies the denial rather than assuming it, and skips
    when the write still succeeds, as it would for root.
    
    The `-#R`, `-#E` and `-#C` cases that need a cache write their zip with
    `printf`, which keeps python out of the test.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 5415fa2e62d4a1bedde72cba2f8437a92b526f49
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 05:36:52 2026 +0200

    --sizehack on an update decides from the cache index alone (#1527)
    
    `back_wait()` decides the same thing in four places: what we already
    hold is the size the server announces, so keep it and force a 304. Three
    of them measure the file on disk, and those same three refuse hypertext.
    The fourth, the one that runs on an update crawl, reads the size out of
    the cache index and guards nothing else.
    
    The cache index records a past fetch, not what the mirror holds now.
    Both consequences below reproduce against the local test server.
    
    An HTML page is kept whenever its length holds, so a server that always
    answers 200 hands the user last week's page. The same crawl without a
    cache entry re-fetches it, because that sibling block refuses hypertext.
    `--sizehack` still skips the transfer for every data file, and an honest
    server still answers the conditional request with a real 304. What the
    guard drops is the guess, and it drops it for the file type a reader
    actually reads.
    
    The second consequence loses the file. Say the mirrored copy no longer
    has the recorded size, because it was edited by hand or the mirror was
    rebuilt over an older cache. The forced 304 then sends `cache_read`
    looking for a file of that size. The entry is refused, and the socket is
    already closed. The file leaves the mirror and is never re-fetched. The
    log carries `warning: file size on disk (24) does not have the expected
    size (16))` and the run reports no errors.
    
    So the block now asks the two questions its siblings ask, and keeps its
    own cache read on top. One gap stays open, and master has it too.
    `cache_read` validates the save name the cache recorded, where this
    block measures the save name in force now. A crawl whose `-N` changed
    between runs therefore still compares two different files. The change
    narrows the class rather than closing it.
    
    `tests/417` pins three outcomes: the HTML page must be re-fetched, the
    drifted file must come back, and the data file must still take the
    shortcut. That last arm is the one that fails if the option is simply
    switched off. Crawling with `--footer ''` leaves the mirrored HTML
    byte-identical to its source. So the HTML arm needs the mime guard, not
    the change of size source. A second pass re-derived the mutants from the
    diff. It killed six of seven: each added condition dropped in turn, each
    inverted in turn, and the whole hunk reverted. No test can kill the
    seventh, which drops `strnotempty(url_sav)`, because `fsize_utf8`
    returns -1 on an empty name and the size comparison refuses on its own.
    
    One divergence I found and left alone. The non-update sizehack block
    omits the `/robots.txt` exclusion its two siblings carry. It sits on a
    path this change does not touch, and nothing measured it. Folding an
    unreproduced behavior change into this one would put two claims under
    one title.
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 36c4795740a1c888bd5ea2f8c4a76ee21bc433c3
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 03:36:45 2026 +0200

    httrack-library.h is not enough to use CHAIN_FUNCTION() (#1522)
    
    `CHAIN_FUNCTION()` is how html/plug.html tells a plugin author to
    install a callback, and html/library.html sends that author to
    `httrack-library.h` for the API. A translation unit that includes only
    that header does not compile. The macro dereferences `httrackp` and
    allocates a `t_hts_callbackarg`, and the header declared neither, so gcc
    reports `unknown type name 't_hts_callbackarg'`. A `requires:
    htsdefines.h` note sat above the macro, and nothing enforced it.
    
    The fix includes `htsdefines.h` and `htsopt.h`, both already in
    `DevIncludes_DATA`, so no new file is installed and no declaration
    moved. `nm -D --defined-only libhttrack.so` reports the same 172 symbols
    before and after.
    
    The doc's own worked example includes all three headers, which is
    probably why nobody hit this. Only a reader who followed the prose
    rather than the example would.
    
    `htsopt.h` pulls `htsnet.h` and therefore `winsock2.h`, and that broke
    the MSVC build. `htsthread.c` took `windows.h` first through
    `htsthread.h`, so `winsock.h` won and MSVC redefined `sockaddr`,
    `fd_set` and the socket calls. The defect is the header rather than one
    file's include order. Five other headers of ours had the same shape, and
    nothing had yet made their translation units reach `winsock2.h`. New
    `src/htswin32.h` establishes the order once, and six sites include it
    instead of `<windows.h>`. It is not installed and it defines no macro
    that hides a declaration. An include-graph walk over the Visual Studio
    project files counts 58 translation units. One reached `windows.h` first
    before the change, and none did after.
    
    Test 411 stages `DevIncludes_DATA` into a private directory and compiles
    `tests/chainplug.c` against it. No include path reaches the source tree,
    because a `-Isrc` makes the bug vanish. The test also checks that the
    same compile fails without the staged path, so a stray `-I` cannot make
    it vacuous. Reverting the header change turns it red on the compiler
    error above.
    
    That reach means a consumer also needs OpenSSL's own headers. Where
    OpenSSL ships a `.pc` file, `Requires.private` already carries the path.
    Where configure finds OpenSSL only through `CPPFLAGS`, as on a keg-only
    Homebrew prefix, nothing does, and `222_pkgconfig-consumer` caught it on
    macOS. `libhttrack.pc` now names that directory, but only when the
    compiler cannot find the header unaided. It takes an absolute path only,
    because a relative one in an installed `.pc` resolves against whatever
    directory the consumer builds in.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 0fbe7d6a4fb9f9eaf1cbd9759c886bef8b3a18d7
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 02:36:40 2026 +0200

    html/library.html documents no symbol and points at a file an integrator cannot build (#1523)
    
    `html/library.html` sent an integrator to `src/httrack.c` for an
    example. That file includes six headers the development package does not
    install: `htsbase.h`, `httrack.h`, `htslib.h`, `htscharset.h`,
    `htsbacktrace.h` and `htsthread.h`. So it cannot compile outside the
    source tree. The page named no symbol, no include path and no link flags
    either.
    
    It now lists the headers `DevIncludes_DATA` installs, gives the
    `pkg-config` line, and points at the `libtest/` examples. Those examples
    install to `$(datadir)/httrack/libtest` and include nothing outside that
    set.
    
    Both lists are copies of build-system facts, so they would rot the same
    way the old text did. `tests/412` parses them back out of
    `src/Makefile.am` and `src/httrack.c` and compares. A second agent
    re-measured the mutants on a separate checkout and killed 7 of 7. Each
    one edits a list the test compares.
    
    A full API reference is a separate question, and this PR does not
    address it.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 6a84bc6604b328587864e7dd0c5973198b4c3a23
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 02:34:21 2026 +0200

    ProxyTrack ships the uninitialised tail of a buffer its cache body was too short to fill (#1521)
    
    ProxyTrack sizes the body buffer from the cache entry's declared
    `X-Size`, then fills it from the file that entry names. When the file is
    shorter the read fails, but the buffer stays, and both consumers emit
    `element->size` bytes whatever the status code says.
    `proxytrack_process_HTTP` sends them to the client and the ARC writer
    puts them on disk. The tail of that buffer is whatever the heap last
    held.
    
    An attacker controls both halves, because the declared size and the body
    path are fields of the cache entry. Anyone who can hand ProxyTrack an
    archive to serve (a downloaded mirror, a shared cache) picks how many
    bytes leak. A 4096-byte `X-Size` over an 18-byte file leaked 4078 bytes,
    and nothing but memory bounds that number. That heap belongs to a
    process that has just parsed other clients' requests and other cache
    entries. The bytes can carry URLs, headers and cached page content.
    
    I reproduced it before fixing it, under `MALLOC_PERTURB_=165` so the
    tail reads 0x5a rather than the zeros a fresh page hands out. 4078
    sentinel bytes reached the client over the wire, and the same 4078
    landed in the ARC.
    
    The fix frees the body on a failed read, which is what the in-zip branch
    beside it already does. The pre-3.31 `.ndx` reader carries a second copy
    of the same allocate-then-fill and takes the same change. Two of the
    four sites the new helper replaces already dropped the body, so those
    hunks only remove the duplication. With no body to send, the reply still
    announced `Content-length: element->size`, and the log line still
    reported it. Both now ask for the bytes the element holds.
    
    Test 406 puts the zip reader through both consumers and the `.ndx`
    reader through the ARC writer. It requires the record to be present and
    no consumer to announce more bytes than the file held. An implementation
    that clips to the bytes it read therefore passes too, which is
    deliberate. Ten mutants, all killed. Three revert one fix each, and two
    refuse every disk-held body, one per reader. The rest zero-fill the
    tail, clip to a wrong length, drop the body only past a threshold,
    refuse the entry, or never serve one. The heap poison is a bonus rather
    than the gate, and the two real-bug mutants still fail where the
    allocator ignores `MALLOC_PERTURB_`.
    
    PR #1517 narrows this window but does not close it, and its own test
    deliberately leaves the holdable-but-short case out. The two branches
    merge cleanly and both tests pass on the merge.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 7db1ea5b04d067e81886e51731ecb40aa6d15400
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 02:15:50 2026 +0200

    Three alias rows expand to the option next to the one they name (#1524)
    
    Three rows of `hts_optalias[]` expanded to a short option that neither
    the alias name nor its own help string means.
    
    `--extract-cache` reached `-#C`, which lists the cache, so it printed
    "No cache entry found" and extracted nothing. Extraction is `-#E`.
    `--autotest` reached `-#T`, the transfer-ops log, so `--autotest URL`
    mirrored the site. The autocheck that prints `** AUTOCHECK OK` is `-#t`,
    which no alias reached. `--debug-testfilters` still named `-#0`. #427
    removed that arm with the other single-letter self-tests, so the option
    reported itself unrecognized, mirrored its two filter arguments as URLs
    and exited 0.
    
    The first two rows are repointed. The third is deleted because what
    replaced `-#0` is a self-test name (`-#test=filter`) and not a short
    option. `--debug-testfilters` now reports an unknown option instead of a
    wrong one. #427 already called the row vestigial. It survived only
    because editing the array made clang-format reflow the table, which the
    `/* clang-format off */` block has since fixed.
    
    I checked all 175 rows against the case labels htscoremain.c carries,
    and no other target is unreachable or misdirected. Two rows I left alone
    on purpose. `--urllist` names `-%S`, the scan-rules list, where `-%L` is
    the URL list. It is the first row for `-%S`, so the man page, the guide
    and the generated help print it as that option's long name. history.txt
    records a fix to it under that name too, so renaming or repointing it is
    a product call. `--fast-engine` and `--debug-overflows` name `-#X`,
    which still answers "warning: option has no effect".
    
    The reverse lookup gives `-#E` a long name it did not have, so
    `man/httrack.1` and its html render are regenerated.
    
    Test 410 pins both directions. The `optalias` self-test asserts what
    each name expands to, and what `-#E`, `-#C`, `-#t` and `-#T` resolve
    back to. The script then asserts at runtime what each corrected option
    does, against what its neighbour still does. It killed all seven
    single-row mutants I ran, including repointing a neighbour and keeping
    the right target under the stale value class.
    
    
    One spelling changes class of outcome. `--extract-cache` moves from the
    `level` class to `single`, because `-#E` takes no value.
    `--extract-cache=2` used to be accepted and to list the cache. It now
    exits 255 with `Option --extract-cache does not take the value 2`. The
    bare and detached spellings both extract, and `-#C2` is untouched.
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit d2aebf446faab33f557b714f70da8a3232006a68
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 02:14:20 2026 +0200

    A cache body whose CRC does not match is served without complaint (#1525)
    
    minizip verifies a member's CRC inside `unzCloseCurrentFile()`, once the
    whole member has been read. The cache read path threw that return away,
    so a body that inflated cleanly with a wrong CRC went into the mirror
    unremarked. That case now reports a cache read failure, which costs one
    refetch.
    
    The index load in `cache_init()` discards the same return, and that one
    stays. It reads no body, so the only member minizip can verify there is
    an empty one, true of every headers-only entry. That CRC covers no
    bytes, so checking it would drop a usable entry while saying nothing
    about the file on disk.
    
    The new case rides `tests/01_zlib-cache-corrupt.test`. It flips the
    victim's stored CRC in both the local and the central header. The two
    headers must agree, or the open fails first and the case would test the
    coherency check instead. Mutation testing killed three of the four
    mutants. The survivor keeps an earlier error message that no case in the
    file distinguishes. A second agent re-measured this on its own checkout
    and confirmed the corrupted member still inflates to full length before
    the CRC verdict.
    
    Two other items from the same sweep got a verdict and no change.
    `copy_htsopt()` has no caller in the tree, but it is `HTSEXT_API` in the
    installed `httrack-library.h`, so it counts as the front ends' API.
    `-#C` enumerates from `hts-cache/new.ndx`, which nothing has written
    since 3.31. A fresh mirror leaves only `new.zip`, so `httrack -#C '*'`
    prints "No cache entry found" over a cache holding two entries.
    Restoring that means enumerating the ZIP, and dropping the option
    removes a documented feature, so this is a ruling, not a cleanup.
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 2d269b26385aa6813a1f174d4f68049efe7119df
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 02:06:58 2026 +0200

    Three paths fill an in-memory body past the bound the receive guard enforces (#1515)
    
    `http_xfread1()` refuses an in-memory body at `INT32_MAX`, because
    `r->adr` is indexed with an int downstream and the allocations around it
    add a trailing NUL. Three other places fill `r->adr` without going
    through that guard.
    
    The chunk reader refused one value later, at `> INT32_MAX`. Cumulative
    chunk sizes of exactly `INT32_MAX` therefore bought a 2 GiB `realloct`
    out of `back_wait()`. Only the next `http_xfread1()` call tore the
    transfer down. On a 32-bit build that allocation fails outright.
    
    The decode path reaches the one size the guard rejects.
    `hts_codec_maxout()` caps a decoded body at `INT_MAX` inclusive, so
    `back_finalize()` can commit `r->size = INT32_MAX` and hand
    `readfile_utf8()`'s buffer to `htsparse.c`.
    
    `LOAD_IN_MEMORY_IF_NECESSARY()` reads a saved file back into memory to
    parse it, with no bound at all. `htsparse.c` casts that size to `int
    cSize` and passes `(int) r->size` to `check_html`. At line 3463 it
    divides by `(int) (r->size)`, under an `if (r->size)` test made in the
    wide type. At exactly 4 GiB the cast is zero and the guarded division
    divides by zero.
    
    That site is reached on an ordinary crawl. `back_finalize()` slurps a
    written real-media body back into memory only below 8192 bytes
    (htsback.c:1232). An `audio/x-mpegurl` response above that stays on
    disk, `r.adr` stays NULL, and the load runs. An 11108-byte playlist logs
    `File successfully loaded for parsing`. Nothing in the suite happened to
    serve one that large, which is why no existing test covers the route.
    
    One predicate, `hts_inmem_size_fits()`, names the bound for all four
    sites. The decode refusal takes the route the neighbouring decode
    failures take. It classes the slot `STATUSCODE_INVALID`, names it, and
    drops the still-coded body so nothing can commit it as the page.
    
    The local file is the one that cannot be bounded from outside.
    `readfile2()` stats the file itself, and sizes both its `malloct` and
    its `hts_fread_exact` from that stat. A caller that checks `fsize()`
    first therefore leaves a window. A first draft of this PR did exactly
    that. A 4-byte file that grew to 20 between the two calls came back as a
    20-byte buffer. `readfile2_inmem()` applies the bound on the single stat
    that sizes the read. `readfile2()` and `readfile()` keep the unbounded
    behaviour they had. The cache index read in `htscoremain.c` and the two
    in `htscache_selftest.c` are untouched. Whether they want a bound of
    their own is a separate question.
    
    `01_engine-xfread.test` pinned less than it looked like. It read `r.adr`
    inside the `printf` argument list that also called `http_xfread1()`. gcc
    sampled the pointer before the call, so the `adr=` column could never
    see an allocation the guard made. Every case also started from a memset
    `htsblk`, so `r.adr` was always NULL and a per-call guard was
    indistinguishable from a first-allocation-only one. That second gap
    hides a real memory bug, because the unknown-length arm would then reach
    `realloct(r->adr, (int) r->size + bufl + 1)` with `r->size` past 2^31.
    The test now reads `r.adr` and `r.statuscode` after the call in
    statements of their own. It enters one case holding a buffer, and keeps
    accept controls throughout, because a refusal-only assertion cannot tell
    a bound from a brick.
    
    Sixteen mutants, fourteen killed, with a semantic no-op green throughout
    as the harness control. Nine are of the receive guard, graded against
    the test each tree ships. That is 7 of 9 before and 9 of 9 after, and
    the two that used to survive are the two gaps above. The chunk bound put
    back to `>` dies in `130_local-chunked-truncated.test`, which now serves
    a `7fffffff` chunk-size line. Neutering the decode predicate dies in the
    self-test alone. The local-file bound dies on a sparse `INT32_MAX`-byte
    file made under the build directory. The fixed build refuses it in 0.01s
    without touching a page. A build without the bound reads all of it, in
    1.45s and 2.1 GB. Making that bound refuse every file instead dies on
    the small file beside it. The sparse case skips where the filesystem
    will not hand one out.
    
    Two mutants survive. Dropping the call to `back_set_decoded_size()`
    along with the size it commits dies in `50_local-contentcodings.test`,
    because the page is then written at its compressed length. Replacing
    that same call with the bare size assignment is the survivor. It keeps
    the page correct and drops only the bound, and nothing catches it.
    Reverting the local-file call site to plain `readfile2()` restores that
    whole bypass, and nothing catches it either. Telling either survivor
    apart needs a body at the bound. One costs a real 2 GiB decode, the
    other a 2 GiB file mirrored to disk. `local-crawl.sh` has no hook to
    grow a mirrored file between passes, so that one would cost harness
    plumbing for a single mutant.
    
    ---------
    
    Signed-off-by: Xavier Roche <xroche@gmail.com>
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit ae1de6e50acc808335f5faea59400446927d5f9f
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 00:58:32 2026 +0200

    abortf_ never returns but nothing tells the compiler (#1518)
    
    `abortf_` always ends in `abort()`, on every path including the
    `HTSSAFE_ABORT_FUNCTION` callback branch and the empty override the
    three ProxyTrack translation units install. Nothing in the source said
    so. This adds `HTS_NORETURN` beside `HTS_DEPRECATED` in `htsglobal.h`
    and applies it. The macro is spelled the way GCC, Clang and MSVC each
    want, and placed where all three accept it.
    
    The claim that this retires 21 static-analyzer findings did not survive
    measurement. scan-build and `gcc -fanalyzer` report the same counts and
    breakdowns before and after. Each already inlines the static `abortf_`
    and sees the `abort()` for itself. CodeQL is what CI runs, and I could
    not run it here.
    
    No new warning appears, and a `-Wall -Wextra` build gives the same count
    and breakdown either way. No test is added, because nothing observable
    changes.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Signed-off-by: Xavier Roche <xroche@gmail.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 0d3c871385cea1a44a1df15a0a5b628e99bf7c58
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 00:48:53 2026 +0200

    A reference on a document's first byte makes --single-file read in front of it (#1519)
    
    `--single-file` asks `singlefile_ref_class()` whether a reference
    htsparse found may be inlined. Inside a stylesheet or a script there is
    no enclosing tag, so the answer comes from `rech_tageq(attr, "import")`.
    That macro starts by reading the byte in front of the token, to decide
    whether the token starts a word. When the token is the document's first
    byte, the read lands one before the response body on the heap.
    
    A server picks both the mime type and the body it sends. A six-byte
    `text/css` reply of `url(x)` is enough, and the same body behind one
    space is clean. Under ASan the process dies. Without a sanitizer the
    stray byte decides whether a body opening with `import = ` counts as a
    stylesheet. The mirror's output then follows whatever the allocator left
    there. `--single-file` is off by default.
    
    The tree already owned the safe primitive. `html_prevc()` returns a
    space sentinel at a buffer start instead of reading `[-1]`. That is also
    the right answer here, because a token opening a document does sit on a
    word boundary. `singlefile_ref_class()` now receives the document start
    and uses it for all three of its boundary tests. No path through the
    function reads below the body any more.
    
    `tests/401_local-single-file-byte-zero-ref.test` crawls a stylesheet
    whose first byte opens a `url()` and checks the reference still inlines.
    It passes on the fix and fails on the reverted one under ASan, the leg
    that sees this class of bug.
    
    ---------
    
    Signed-off-by: Xavier Roche <xroche@gmail.com>
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit a65612a3c04dc4b47444406c1106830c30c23a78
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 00:44:16 2026 +0200

    A ZIP extra field picks its own copy length in an unreachable minizip helper (#1514)
    
    `zipRemoveExtraInfoBlock` in the vendored `src/minizip/zip.c` walked a
    ZIP extra field without ever validating the block size it read out of
    that field. Three defects follow from the one missing check. The loop
    guard `while(p < (pData + *dataLen))` admits a partial block, so the two
    `*(short*)` reads opening each iteration run past the buffer. The
    `memcpy(pTmp, p, dataSize + 4)` after them is never compared against
    `*dataLen`, so two bytes taken from the archive choose both a source
    offset and a copy length. A declared `dataSize` of 0x7ff0 writes 32756
    bytes into an allocation sized `*dataLen`, which is a controlled-offset,
    controlled-length heap write rather than an overread. `dataSize` is also
    a signed `short`, so a declared 0x8000 makes `dataSize + 4` negative and
    wraps the length. Those same two loads are misaligned, which faults on
    sh4, m68k, hppa, sparc, mips and alpha.
    
    Nothing reaches any of it today, so the fix is deletion rather than a
    patch. Bounding a function with no caller means inventing its contract,
    because every limit you pick is a guess about a caller that does not
    exist. Removing it retires the whole class.
    
    The reachability verdict comes from the linker rather than from grep.
    Across the 141 objects a full build produces, `nm --defined-only` finds
    the definition in `src/minizip/proxytrack-zip.o` and
    `src/minizip/libhttrack_la-zip.o` (plus libtool's PIC copy). `nm
    --undefined-only` finds no reference in any of those objects, nor in
    `httrack`, `htsserver`, `proxytrack`, `libhttrack.so` or the ten test
    plugins. `nm -D --defined-only libhttrack.so.3.0.20` lists 172 exported
    symbols and this is not one of them. That count is the control, because
    a probe reading an empty symbol table would report the same absence.
    `minizip/zip.h` is not in `DevIncludes_DATA`, so it never reaches an
    installed prefix. Upstream minizip does not call it either, since the
    `.orig` copies in the tree carry the same definition and prototype with
    no call site. The two apparent calls in `zip.h` sit inside the comment
    block that opens at line 359 and closes at line 374.
    
    `tests/405_minizip-no-extra-parser.test` scans the built library and
    every object under `src/`, and it requires `zipClose` in that same set
    before it trusts an absence. Without that control the test would pass on
    a stripped or empty table. It kills the mutant: a stub definition reds
    it and names all four products carrying the symbol.
    
    The tree keeps a `.orig` plus `.diff` record per vendored file, so
    `zip.c.diff` and `zip.h.diff` are regenerated. Both were checked by
    applying them to the `.orig` and comparing against the tree copy. The
    pre-existing hunks (the `next_in` const fix, `zipFlush`,
    `zipAbandonFileInZip`) survive unchanged.
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 54293d872a8e02593617235cb02ca2a40998c89e
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 00:39:38 2026 +0200

    A skipped crash-symbolize test names no suspect (#1513)
    
    The emulated s390x leg skips `80_engine-crash-symbolize.test` with
    `addr2line names no hidden symbol in this build`. That one message
    covers three failures that read identically in the log:
    
    - the trace parsed no frames,
    - no frame's module resolved to a file on disk,
    - addr2line ran and named something other than the hidden frames.
    
    The test now prints how many frames it parsed and how many it resolved,
    and it dumps the raw trace and the oracle before skipping. A negative
    control with a silent `addr2line` on the PATH confirms the dump fires.
    
    That diagnostic then ran on this branch and named the cause. The s390x
    trace stops at `linux-vdso64.so.1(__kernel_sigreturn)` after two frames,
    and addr2line resolved both to a file and a line. The engine frames sit
    below the signal frame and never reach the trace, so the old message
    blamed the wrong component. The second commit skips with that reason
    when the trace carries a sigreturn entry. It stays a skip: crossing a
    signal frame under qemu-user is not something a test can fix.
    `218_crash-nopie-frames.test` skips on that leg for what looks like the
    same reason.
    
    The overnight architecture sweep had blamed a missing `-g`, on the
    theory that `tools/emulated-suite.sh` runs a bare `configure` and
    addr2line therefore resolves nothing. Both halves are wrong.
    `configure.ac` never assigns `CFLAGS`, so `AC_PROG_CC` supplies gcc's
    default of `-g -O2`, and the emulated log carries `checking whether gcc
    accepts -g... yes`. I also built this tree with `CFLAGS=-O2`, where the
    test passes.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 4411fc73003180b3e08551d1a19f25a0c4980a28
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 00:38:50 2026 +0200

    Two is_space definitions in src/, with different character sets (#1516)
    
    `src/htscharset.c` defines a static `is_space()` over SPACE, TAB, CR and
    LF. `src/htslib.h` defines a function-like macro of the same name over
    eight characters: that set plus VT, FF and both quote characters.
    `src/htsserver.h` carried a verbatim copy of the macro. Nothing in
    `htscharset.c` reaches either header today, so the local definition wins
    and the sniffer behaves as intended.
    
    I checked what a colliding include would actually do, because that
    decides how urgent this is. A macro that arrives before the definition
    turns `static int is_space(char c)` into a syntax error, so a
    top-of-file include fails loudly. A macro that arrives after it compiles
    clean, and every call site below silently switches to the
    eight-character set. `htscharset.c` already has includes at lines 495 to
    504, so either ordering is reachable. Renaming the predicate to
    `is_html_space` removes the trap in both cases.
    
    Behaviour does not move. The macro keeps its eight characters and every
    caller keeps the set it had. Only the sniffer's three static helpers
    change name. The new cases in `tests/01_engine-charset.test` pin both
    halves of the sniffer's set. Six cover the characters the two sets
    disagree on, and each flips under a mutant whose `is_html_space` body is
    the eight-character set. Three more feed TAB, CR and LF, which kill a
    mutant matching a bare space. Those three need the batch path, because
    argv rewrites them to a space before the self-test sees them.
    
    `htsserver.h` loses only `is_space`, so one definition of the
    eight-character set survives. Its other four macros stay, because
    `htsweb.c` reads `is_realspace` from that header without including
    `htslib.h`, and because they are byte-identical to `htslib.h`'s.
    `htsserver.c` includes both headers, so a reworded copy would draw a
    redefinition warning.
    
    The macro's `/* Spaces: CR,LF,TAB,FF */` comment named neither the
    quotes nor VT, so it is gone. Correcting it in place was not possible:
    clang-format 19 rewrites about 35 lines of that hand-formatted region
    for any line added there.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit b5c8db9ee5bf4206852dff1408a05e92ecdaa9ef
Author: Xavier Roche <roche@httrack.com>
Date:   Fri Sep 4 00:18:04 2026 +0200

    -N's digit encoding is a parity test, not a threshold (#1512)
    
    `src/htsname.c` twice carried `// >1000 signifie "pas de www/"` over
    `((opt->savename_type / 1000) % 2) == 0`. The code tests that digit's
    parity rather than a threshold. So `-N2000` keeps the host directory the
    comment says it drops, and `-N3001` drops the `web/` directory a
    threshold reading would keep. No shipped preset is affected, because
    every one the man page documents stays below 2000.
    
    Behaviour is unchanged. Both comments now say what the test does, and
    the encoding itself moves to the field in `htsopt.h`, which recorded
    none of it. `% 100` picks the tree, an odd hundreds digit names the top
    directory after the host, and an odd thousands digit drops that
    directory. Site structure is the exception the man page's `-N100`
    already hints at, because it reads no hundreds digit beyond that literal
    value.
    
    The new `400_engine-savename-type-digits.test` pins each of those claims
    through `-#test=savename`. I ran it against both threshold mutants, one
    per digit, and each one reds it.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 77a97c81342efb6be2f8ea23d87973d113c9e3e6
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 23:55:06 2026 +0200

    Optional dependencies are decided by the build machine, not by the recipe (#1509)
    
    Four optional dependencies default to auto, so this source ships the
    `br` and `zstd` codings on Gentoo and drops them in a FreeBSD jail.
    Turning iconv off means the built-in codepage tables, which read no
    multi-byte charset, so `debian/rules` asks for iconv back. Those tables
    could never find their own `cp1252` entry from the `windows-1252` label
    every HTTP header uses, which this fixes.
    
    Building without iconv also exposed a crash in the same tables. A
    leading NUL stopped the decode loop before it had allocated anything, so
    the function stored the terminator through a null pointer.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Signed-off-by: Xavier Roche <xroche@gmail.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit e5f372ab7c9944c40a04c3cfd4b7db7e21f0a76a
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 20:23:59 2026 +0200

    HTS_FIND_LIBDIR never looks under a multiarch triplet (#1510)
    
    On Debian and Ubuntu `--with-brotli=/usr` and `--with-zlib=/usr` both
    hard-error on master, because the shared libraries live under
    `/usr/lib/<triplet>`. Clang rejects `-print-multiarch` and several CI
    legs are clang, so detection falls back to the directory the compiler
    resolves libc in. The triplet is guessed after `lib` and `lib64`, so no
    prefix that resolves today changes its answer.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit c8a61f44c1f48b78a28e8a0b17d283ddb3e14947
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 19:01:26 2026 +0200

    The WebHTTrack UI installs under docdir, which packagers strip (#1508)
    
    htsserver serves the WebHTTrack interface at runtime, and every
    packaging system treats `$(docdir)` as documentation it may strip. The
    interface now installs under `$(datadir)/httrack/html/server`, and a
    relative `doc` symlink beside it reaches `$(docdir)` so the Help links
    still work. The documentation does not move, so #1477's layout holds,
    and a `symlink_to_dir` maintscript carries Debian past the symlink dpkg
    will not replace.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit e67a8661e17c0e9d8764e75725f17921b42f2198
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 14:48:34 2026 +0200

    hts_self_path() has no BSD arm, so FreeBSD falls back to argv[0] (#1507)
    
    FreeBSD, NetBSD and DragonFly mount no /proc, so `hts_self_path()`
    returned NULL and the caller fell back to the argv[0] it exists to
    avoid. Each mib stays symbolic and comes from that BSD's own headers,
    because 9 is the pathname on DragonFly and `KERN_PROC_SV_NAME` on
    FreeBSD. The self-test sweep then caught the Windows arm leaving a
    terminated clipped path behind, so all four arms now share one refusal
    exit. No CI leg here executes a line of the sysctl arm, so the FreeBSD
    canary merged as #1505 is its only real verification.
    
    Closes #1506
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 6676a1f13b9983d3ebaaabc5d28f840f40257c57
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 10:55:45 2026 +0200

    Nothing rejects two tests claiming the same number (#1504)
    
    The exemptions are listed by family name rather than by a magnitude
    threshold, because the tree's sub-100 duplicates were accidents rather
    than a convention. So `02_update-cache` and `13_crawl_proxy_https` move
    to 394 and 395 instead of being grandfathered. This catches a duplicate
    that lands and stays, but not two open PRs picking 389, since the
    ruleset lets a PR merge behind master.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 004b88452870497b88f9a54b4e8a193ca6318a82
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 10:10:55 2026 +0200

    Nothing in our CI compiles this tree against a BSD libc (#1505)
    
    Nothing in our CI compiles against a BSD libc, and macOS is not close
    enough to stand in for one. The first runs turned up eight FreeBSD
    failures, so the job pins those names and reds on a ninth or one that
    stops failing. It skips the FreeBSD port on purpose, because that port
    does not build against 3.50.1 and `tools/downstream-drift.sh` already
    watches it.
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit d5a9a707101bdbebb43191cf9f8792c7d793404a
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 09:44:37 2026 +0200

    Only Fedora and Termux build master the way a packager does (#1500)
    
    Two defects in 3.50.0 (#1487, #1488) were visible only from a downstream
    build, and reading a recipe statically cannot find that class. These
    three canaries build master weekly through each packager's own recipe,
    and they block nothing.
    
    The openSUSE leg packages three files their `%files` predates, so rpm's
    unpackaged-file check stays armed for a fourth. Each acknowledgement
    reds once they package that file themselves. The tarball step moved into
    a shared action, which is why the merged Termux canary is in the diff.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 4a50a42af4cbafca5259a79d5628ed61ff177b34
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 09:41:42 2026 +0200

    A crafted ready-table size header writes outside its allocation, plus two mistyped-pointer writes (#1503)
    
    A `SIZE_MAX` size header in a backlog ready-table file wrapped
    `malloct(*size + 1)` to zero, so the guard byte wrote outside the
    allocation. The same helper let a `void *` out-parameter travel through
    a `char **` and a `lien_back **`, the tree's only `-Wstrict-aliasing=2`
    site. Five enum option fields scanned through an `(int *)` cast are
    written out of the object under `-fshort-enums`, and new tests cover all
    three.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit bad1278e99390454148f926d0d9e7b807a94948b
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 09:28:14 2026 +0200

    --with-brotli=DIR and --with-zstd=DIR look for the library in DIR/lib and nowhere else (#1502)
    
    `--with-brotli=DIR` and `--with-zstd=DIR` appended `DIR/lib`
    unconditionally, so a lib64 or multiarch prefix left configure reporting
    the coding as enabled against a system copy. #1472 fixed the same shape
    next door in `CHECK_ZLIB`, and the subdirectory search now lives in one
    macro both call. PACKAGING.md gains the flags that pin the two codings,
    which default to `auto` and have been following whatever the builder
    happened to hold.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

commit 9250384b605b6f161bfe57298052ca7602b16b71
Author: Xavier Roche <roche@httrack.com>
Date:   Thu Sep 3 07:14:44 2026 +0200

    Nothing in our CI builds the way a Termux packager does (#1499)
    
    Two defects in 3.50.0 (#1487, #1488) were visible only from a
    Termux-style build. This runs master through their own recipe weekly, in
    their builder image. It applies their patches verbatim and blocks
    nothing, so it stays red until termux/termux-packages#31457 removes the
    five that no longer fit master.
    
    ---------
    
    Signed-off-by: Xavier Roche <roche@httrack.com>
    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Created: 2026-09-04 Last update: 2026-09-05 01:33
testing migrations
  • This package will soon be part of the auto-openssl transition. You might want to ensure that your package is ready for it. You can probably find supplementary information in the debian-release archives or in the corresponding release.debian.org bug.
  • excuses:
    • Migration status for httrack (3.49.23-1 to 3.50.1-1): Waiting for test results or another package, or too young (no action required now - check later)
    • Issues preventing migration:
    • ∙ ∙ Too young, only 3 of 5 days old
    • Additional info (not blocking):
    • ∙ ∙ Piuparts tested OK - https://piuparts.debian.org/sid/source/h/httrack.html
    • ∙ ∙ Reproduced on amd64 - info
    • ∙ ∙ Reproduced on arm64 - info
    • ∙ ∙ Reproduced on armhf - info
    • ∙ ∙ Reproduced on i386 - info
    • Not considered
news
[rss feed]
  • [2026-09-02] Accepted httrack 3.50.1-1 (source) into unstable (Xavier Roche)
  • [2026-09-01] Accepted httrack 3.50.0-2 (source) into unstable (Xavier Roche)
  • [2026-09-01] Accepted httrack 3.50.0-1 (source) into unstable (Xavier Roche)
  • [2026-08-28] Accepted httrack 3.49.25-1 (source) into unstable (Xavier Roche)
  • [2026-08-26] Accepted httrack 3.49.24-1 (source) into unstable (Xavier Roche)
  • [2026-08-26] httrack 3.49.23-1 MIGRATED to testing (Debian testing watch)
  • [2026-08-21] Accepted httrack 3.49.23-1 (source) into unstable (Xavier Roche)
  • [2026-08-21] httrack 3.49.22-1 MIGRATED to testing (Debian testing watch)
  • [2026-08-16] Accepted httrack 3.49.22-1 (source) into unstable (Xavier Roche)
  • [2026-08-13] Accepted httrack 3.49.21-2 (source) into unstable (Xavier Roche)
  • [2026-08-12] Accepted httrack 3.49.21-1 (source) into unstable (Xavier Roche)
  • [2026-08-10] Accepted httrack 3.49.20-1 (source) into unstable (Xavier Roche)
  • [2026-08-08] Accepted httrack 3.49.19-1 (source) into unstable (Xavier Roche)
  • [2026-08-05] Accepted httrack 3.49.18-1 (source) into unstable (Xavier Roche)
  • [2026-08-05] Accepted httrack 3.49.17-1 (source) into unstable (Xavier Roche)
  • [2026-08-04] Accepted httrack 3.49.16-1 (source) into unstable (Xavier Roche)
  • [2026-07-30] Accepted httrack 3.49.15-1 (source) into unstable (Xavier Roche)
  • [2026-07-29] httrack 3.49.14-1 MIGRATED to testing (Debian testing watch)
  • [2026-07-24] Accepted httrack 3.49.14-1 (source) into unstable (Xavier Roche)
  • [2026-07-23] httrack 3.49.13-1 MIGRATED to testing (Debian testing watch)
  • [2026-07-18] Accepted httrack 3.49.13-1 (source) into unstable (Xavier Roche)
  • [2026-07-16] httrack 3.49.12-1 MIGRATED to testing (Debian testing watch)
  • [2026-07-10] Accepted httrack 3.49.12-1 (source) into unstable (Xavier Roche)
  • [2026-07-10] httrack 3.49.11-1 MIGRATED to testing (Debian testing watch)
  • [2026-07-04] Accepted httrack 3.49.11-1 (source) into unstable (Xavier Roche)
  • [2026-07-04] httrack 3.49.10-2 MIGRATED to testing (Debian testing watch)
  • [2026-06-28] Accepted httrack 3.49.10-2 (source) into unstable (Xavier Roche)
  • [2026-06-28] Accepted httrack 3.49.10-1 (source) into unstable (Xavier Roche)
  • [2026-06-27] httrack 3.49.9-1 MIGRATED to testing (Debian testing watch)
  • [2026-06-22] Accepted httrack 3.49.9-1 (source amd64 all) into unstable (Debian FTP Masters) (signed by: Xavier Roche)
  • 1
  • 2
bugs [bug history graph]
  • all: 0
links
  • homepage
  • lintian
  • buildd: logs, reproducibility, cross
  • popcon
  • browse source code
  • other distros
  • security tracker
  • screenshots
ubuntu Ubuntu logo [Information about Ubuntu for Debian Developers]
  • version: 3.50.0-2

Debian Package Tracker — Copyright 2013-2025 The Distro Tracker Developers
Report problems to the tracker.debian.org pseudo-package in the Debian BTS.
Documentation — Bugs — Git Repository — Contributing