Compare commits

..

21 Commits

Author SHA1 Message Date
Xavier Roche
5d7fee672b sitemap: translate the new GUI strings into the remaining 28 locales (#738)
The feature PR added the four LANG_SITEMAP* entries to lang.def with English
and Francais only; every other locale fell back to English in the WebHTTrack
form. Each file is written in its own declared charset.

Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 07:46:29 +02:00
Xavier Roche
123fa7c3d3 sitemap: realign the htsopt.h comments after the single-file merge
Master's longer LLint declarator moved the block's comment column.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-27 07:30:15 +02:00
Xavier Roche
ae5860eb9e Merge remote-tracking branch 'origin/master' into feat/sitemap
Signed-off-by: Xavier Roche <roche@httrack.com>

# Conflicts:
#	fuzz/Makefile.am
#	lang.def
#	lang/English.txt
#	lang/Francais.txt
#	src/Makefile.am
#	src/htscore.c
#	src/htscoremain.c
#	src/htsopt.h
#	src/htsselftest.c
#	tests/Makefile.am
#	tests/webhttrack-smoke.sh
2026-07-27 07:28:02 +02:00
Xavier Roche
b4def900aa Merge remote-tracking branch 'origin/master' into feat/sitemap
Signed-off-by: Xavier Roche <roche@httrack.com>

# Conflicts:
#	lang.def
#	lang/English.txt
#	lang/Francais.txt
#	src/Makefile.am
#	src/htsalias.c
#	src/htscore.c
#	src/htsopt.h
#	src/htsselftest.c
#	src/libhttrack.vcxproj
#	tests/Makefile.am
#	tests/webhttrack-smoke.sh
2026-07-27 07:12:21 +02:00
Xavier Roche
4fe770a265 tests: renumber the sitemap crawl test to 95
Master took 88 through 93 and #720 claims 94.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-27 07:07:48 +02:00
Xavier Roche
30a4d37b0e sitemap: keep the ingestion state out of htsoptstate
htsoptstate is embedded by value as httrackp.state, so a field at its tail
shifts every httrackp member declared after it: an offsetof probe put
warc_file at 141752 on master and 141760 on the branch. Move the pointer to
httrackp's own tail, where every existing offset holds and copy_htsopt still
ignores it.

Also renumber the crawl test to 89, master having taken 87 and 90, and give
the new option8 checkbox the hidden companion that 90_webhttrack-checkbox-clear
requires, plus its row in that test's table.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 22:18:40 +02:00
Xavier Roche
97f9a04d31 Merge remote-tracking branch 'origin/master' into feat/sitemap
Signed-off-by: Xavier Roche <roche@httrack.com>

# Conflicts:
#	src/htsselftest.c
#	tests/Makefile.am
2026-07-26 22:10:56 +02:00
Xavier Roche
1f944c9ef7 sitemap: date the new files 2026
The headers were copied from an existing file and kept its 1998 year.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 21:21:54 +02:00
Xavier Roche
d22dae4895 Merge remote-tracking branch 'origin/master' into feat/sitemap
Signed-off-by: Xavier Roche <roche@httrack.com>

# Conflicts:
#	tests/Makefile.am
2026-07-26 20:25:38 +02:00
Xavier Roche
ec96d5f24a selftest: bound the sitemap document builders' snprintf accumulation
snprintf returns the length it wanted to write, so accumulating it blind
lets the next offset and size argument walk past the buffer. Guard each
step the way the argv builder above already does, and give the per-URL
loop a real remaining-space bound instead of a fixed 33.

Signed-off-by: Xavier Roche <xroche@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 20:21:11 +02:00
Xavier Roche
4f15490186 fuzz: keep only the four sitemap seed inputs
A libFuzzer run writes its finds into the first corpus directory, and 191 of
them were committed with the harness.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 19:22:42 +02:00
Xavier Roche
028ac8b5ad sitemap: add the fuzz harness the parser was missing, and drop truncated Sitemap: lines
The file header called the scanner fuzzable while fuzz/ registered ten
harnesses and none for it. fuzz-sitemap feeds it raw XML, gzip-framed bodies
and truncated streams off a heap copy of exactly the input size, so an overread
is an ASan report rather than a quiet pass, with a four-file seed corpus.
60000 runs clean under ASan+UBSan.

robots_parse now drops a Sitemap: line that filled its scratch buffer instead
of handing on the half URL it was truncated to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 19:21:06 +02:00
Xavier Roche
3745d8321b sitemap: gate each fetch by who asked for it, and anchor travel on the start URL
A nine-agent review found a scope escape: the sitemap document was its own
`premier`, so the wizard measured travel from wherever the site chose to put
its sitemap. A root /sitemap.xml therefore widened a /deep/dir/ crawl to the
whole host. The ingester now points the wizard at the crawl's own start link
and lets each seeded URL become its own anchor, which is what a command-line
seed gets.

Robots handling was both mistimed and undifferentiated. The Sitemap: lines are
now collected by robots_parse, on the same body in the same fetch, and acted on
after the parsed rules are installed rather than before; and the decision comes
from a new hts_robots_forbids extracted out of the wizard, so the sitemap path
inherits the -s1 filters-win override instead of a stricter hand-rolled check.
The four fetches are no longer treated alike: a sitemap the user names is user
intent, one the site declares invites the fetch, only the guessed /sitemap.xml
obeys a Disallow, and the URLs listed inside stay fully gated.

Also: hts_unescapeEntities replaces the private entity decoder, whose guard
tests and fuzz corpus it silently forfeited; hts_codec_head replaces hts_zhead,
which is only defined under HTS_USEZLIB; the composed URL buffer now fits two
maximal components plus a scheme, which a 2046-byte --sitemap-url reached; the
bounded search is promoted to htstools as hts_memstr; and the live state moves
from httrackp into htsoptstate, leaving two installed fields rather than three.

Tests gain the scope escape, the three robots cases, a cap-boundary control,
the handler invocation count and a compression-bomb decode. Every one was
checked against a deliberately broken build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 19:10:01 +02:00
Xavier Roche
eea8ec5b29 Merge remote-tracking branch 'origin/master' into feat/sitemap
# Conflicts:
#	tests/Makefile.am
2026-07-26 18:55:22 +02:00
Xavier Roche
0ce7da1973 sitemap: a child sitemap is a fetch, so filters and robots.txt must gate it
An adversarial review found that a <sitemapindex> <loc>, and a robots.txt
Sitemap: line, went straight to hts_record_link: the request went out even
when a -* rule or robots.txt Disallow covered it. Only the <urlset> half ran
through the wizard. Gate the document itself on the filters and on
robots.txt, which is all that can apply: the wizard proper wants a referring
link, and its up/down travel rules would judge a child sitemap against the
parent sitemap's own directory. The robots.txt probe is exempt, being the
request that fetches the rules.

A 301 also used to end ingestion silently, since the engine re-queues the
target as a fresh link that carried no sitemap marking. That hit any site
redirecting http to https. The marking now follows the redirect.

The "N URL(s) added" counter reported what the scanner emitted rather than
what was taken, which hid both of the above; it now reads "N of M". The
fallback to /sitemap.xml keys on the same corrected count, so a robots.txt
whose only Sitemap: line is off-host or filtered still falls back. Root
classification skips a UTF-8 BOM and an XML namespace prefix, and the doc
list is cleared when a mirror starts rather than only when it ends.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 18:14:41 +02:00
Xavier Roche
dcfc4acef8 sitemap: state what the decompression cap actually binds on
deflate tops out near 1032:1, so hts_codec_maxout never binds before the
64 MiB cap; the old comment implied a ratio guard that cannot fire.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 18:00:57 +02:00
Xavier Roche
1d96350564 sitemap: fix an out-of-bounds read, tighten the parser and the tests
lienrelatif() walked back from the last character of its current-path
argument without checking the path was non-empty, reading one byte before
the stack buffer. htsAddLink is the first caller to pass an empty savename,
which sitemap documents have because they are ingested rather than mirrored,
so ASan caught it on the new crawl test.

The parser drops a value whose numeric character reference decodes outside
printable ASCII, rather than leaving the reference verbatim and seeding a URL
the site never published, and classifies a document by its real root element,
so a comment naming the other one no longer flips urlset and sitemapindex.
The robots.txt line reader is bounded by the body size instead of relying on
a NUL terminator.

The self-test moves to 01_zlib-sitemap.test: MSan runs 01_engine-* only,
because an uninstrumented libz floods it with false positives.

Tests gain the assertions the earlier ones were missing: which of the
robots.txt route and the /sitemap.xml fallback was taken, that the sitemap
documents stay out of the mirror, that the off-host child sitemap is refused,
the sitemapindex nesting cap, the per-document URL cap at its production
value, and copy_htsopt coverage for the two new fields.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 17:59:19 +02:00
Xavier Roche
f2abda8c0e Merge remote-tracking branch 'origin/master' into feat/sitemap 2026-07-26 17:50:13 +02:00
Xavier Roche
bb3d8db103 sitemap: distinguish the sitemapindex log line from a urlset one
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 17:50:13 +02:00
Xavier Roche
47fe9558da Merge remote-tracking branch 'origin/master' into feat/sitemap
# Conflicts:
#	tests/Makefile.am
2026-07-26 17:40:46 +02:00
Xavier Roche
9cc9a36fa6 Read sitemap files so URLs nothing links to are found
HTTrack finds URLs only by parsing links, so anything a site publishes solely
in its sitemap stayed invisible: robots.txt was already parsed, but its
Sitemap: lines were ignored and nothing else in the tree touched sitemaps.

Adds opt-in --sitemap (-%m), which probes the start host's robots.txt and
falls back to /sitemap.xml, and --sitemap-url (-%mu) for an explicit document.
Handles <urlset> and nested <sitemapindex>, plain or gzipped. Discovered URLs
enter with the full depth budget but still go through the wizard, so filters
and scope rules decide; a sitemap is not a filter bypass.

The parser reads attacker-controlled XML off the network, so it is capped on
URL count, index nesting, decompressed size and decompression ratio, and child
sitemaps must stay on the host that named them.

Closes #712

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-26 17:38:02 +02:00
9 changed files with 108 additions and 107 deletions

View File

@@ -46,32 +46,10 @@ jobs:
- name: Configure
run: |
set -euo pipefail
# Regenerate: configure and the Makefile.in's are not tracked.
# Regenerate from configure.ac/Makefile.am to validate them; the
# committed generated files already let a plain checkout build.
autoreconf -fi
# Disabling zlib must fail here rather than at link with a pile of
# undefined minizip references (#735). Both spellings, so a rewrite
# cannot keep one and lose the other. Probed out-of-tree to leave
# nothing behind.
nozlib="$RUNNER_TEMP/nozlib"
for arg in --without-zlib --with-zlib=no; do
rm -rf "$nozlib" && mkdir -p "$nozlib"
if (cd "$nozlib" && "$GITHUB_WORKSPACE/configure" "$arg" >out.log 2>&1); then
echo "::error::configure $arg succeeded; it must be rejected"
exit 1
fi
# ... and for the stated reason, not an unrelated configure failure.
grep -q "zlib cannot be disabled" "$nozlib/out.log" \
|| { cat "$nozlib/out.log"; exit 1; }
done
./configure
# Same dead end from the compile side. The bare compile is the
# control: without it a broken probe would pass vacuously.
hdr='#include "htsglobal.h"'
echo "$hdr" | $CC -I. -Isrc -fsyntax-only -xc -
if echo "$hdr" | $CC -DHTS_USEZLIB=0 -I. -Isrc -fsyntax-only -xc - 2>/dev/null; then
echo "::error::-DHTS_USEZLIB=0 compiled; htsglobal.h must reject it"
exit 1
fi
# a missing decoder would silently drop the coding from Accept-Encoding
grep -q "define HTS_USEBROTLI 1" config.h
grep -q "define HTS_USEZSTD 1" config.h

View File

@@ -175,7 +175,7 @@ AX_CHECK_ALIGNED_ACCESS_REQUIRED
# check for various headers
AC_CHECK_HEADERS([execinfo.h sys/ioctl.h])
### zlib (mandatory)
### zlib
CHECK_ZLIB()
### brotli and zstd content codings (optional)

View File

@@ -1,33 +1,82 @@
dnl @synopsis CHECK_ZLIB()
dnl
dnl Look for zlib. It is a hard requirement, not an option: the cache and the
dnl WARC output are zip/gzip containers, and the bundled minizip calls zlib
dnl directly. --with-zlib=DIR points at a non-standard prefix.
dnl This macro searches for an installed zlib library. If nothing
dnl was specified when calling configure, it searches first in /usr/local
dnl and then in /usr. If the --with-zlib=DIR is specified, it will try
dnl to find it in DIR/include/zlib.h and DIR/lib/libz.a. If --without-zlib
dnl is specified, the library is not searched at all.
dnl
dnl If either the header file (zlib.h) or the library (libz) is not
dnl found, the configuration exits on error, asking for a valid
dnl zlib installation directory or --without-zlib.
dnl
dnl The macro defines the symbol HAVE_LIBZ if the library is found. You should
dnl use autoheader to include a definition for this symbol in a config.h
dnl file. Sample usage in a C/C++ source is as follows:
dnl
dnl #ifdef HAVE_LIBZ
dnl #include <zlib.h>
dnl #endif /* HAVE_LIBZ */
dnl
dnl @version $Id$
dnl @author Loic Dachary <loic@senga.org>
dnl
dnl Adds -lz to LIBS and defines HAVE_LIBZ.
AC_DEFUN([CHECK_ZLIB], [
AC_ARG_WITH([zlib],
[AS_HELP_STRING([--with-zlib=DIR],[root directory of the zlib installation])],
[zlib_want=$withval], [zlib_want=yes])
if test "$zlib_want" = "no"; then
AC_MSG_ERROR([zlib cannot be disabled: the cache and the WARC output are zip/gzip containers, and the bundled minizip calls zlib directly])
AC_DEFUN([CHECK_ZLIB],
#
# Handle user hints
#
[AC_MSG_CHECKING(if zlib is wanted)
AC_ARG_WITH(zlib,
[ --with-zlib=DIR root directory path of zlib installation [defaults to
/usr/local or /usr if not found in /usr/local]
--without-zlib to disable zlib usage completely],
[if test "$withval" != no ; then
AC_MSG_RESULT(yes)
ZLIB_HOME="$withval"
else
AC_MSG_RESULT(no)
fi], [
AC_MSG_RESULT(yes)
ZLIB_HOME=/usr/local
if test ! -f "${ZLIB_HOME}/include/zlib.h"
then
ZLIB_HOME=/usr
fi
if test "$zlib_want" != "yes"; then
# An explicit prefix is authoritative: if the header is not under it,
# error rather than silently pick a system copy.
if test ! -f "$zlib_want/include/zlib.h"; then
AC_MSG_ERROR([zlib requested at $zlib_want but $zlib_want/include/zlib.h is missing])
fi
CPPFLAGS="$CPPFLAGS -I$zlib_want/include"
LDFLAGS="$LDFLAGS -L$zlib_want/lib"
elif test -f /usr/local/include/zlib.h; then
# Where the BSD ports tree lands zlib, and not always searched by default.
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
fi
AC_CHECK_HEADER([zlib.h], [],
[AC_MSG_ERROR([zlib.h not found; install the zlib development files or pass --with-zlib=DIR])])
AC_CHECK_LIB([z], [inflateEnd], [],
[AC_MSG_ERROR([libz not found; install the zlib development files or pass --with-zlib=DIR])])
])
#
# Locate zlib, if wanted
#
if test -n "${ZLIB_HOME}"
then
ZLIB_OLD_LDFLAGS=$LDFLAGS
ZLIB_OLD_CPPFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib"
CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include"
AC_LANG_SAVE
AC_LANG_C
AC_CHECK_LIB(z, inflateEnd, [zlib_cv_libz=yes], [zlib_cv_libz=no])
AC_CHECK_HEADER(zlib.h, [zlib_cv_zlib_h=yes], [zlib_cv_zlib_h=no])
AC_LANG_RESTORE
if test "$zlib_cv_libz" = "yes" -a "$zlib_cv_zlib_h" = "yes"
then
#
# If both library and header were found, use them
#
AC_CHECK_LIB(z, inflateEnd)
AC_MSG_CHECKING(zlib in ${ZLIB_HOME})
AC_MSG_RESULT(ok)
else
#
# If either header or library was not found, revert and bomb
#
AC_MSG_CHECKING(zlib in ${ZLIB_HOME})
LDFLAGS="$ZLIB_OLD_LDFLAGS"
CPPFLAGS="$ZLIB_OLD_CPPFLAGS"
AC_MSG_RESULT(failed)
AC_MSG_ERROR(either specify a valid zlib installation with --with-zlib=DIR or disable zlib usage with --without-zlib)
fi
fi
])

View File

@@ -48,6 +48,11 @@ Please visit our Website: http://www.httrack.com
#include "htsftp.h"
#include "htscodec.h"
#include "htsproxy.h"
#if HTS_USEZLIB
#include "htszlib.h"
#else
#error HTS_USEZLIB not defined
#endif
#ifdef _WIN32
#ifndef __cplusplus

View File

@@ -138,11 +138,10 @@ Please visit our Website: http://www.httrack.com
#define HTS_DOSNAME 0
#endif
// zlib is mandatory: the cache is a zip and minizip calls it regardless
// utiliser zlib?
#ifndef HTS_USEZLIB
// autoload
#define HTS_USEZLIB 1
#elif !HTS_USEZLIB
#error HTS_USEZLIB=0 is not a supported configuration
#endif
// brotli and zstd content codings; off unless the build opted in (configure,

View File

@@ -44,18 +44,9 @@ Please visit our Website: http://www.httrack.com
#endif
#endif
/* Outstanding threads, counted at spawn rather than by the child at entry, so
that a caller which spawns and immediately waits still joins them (#747). */
static int process_chain = 0;
static htsmutex process_chain_mutex = HTSMUTEX_INIT;
static void process_chain_add(int delta) {
hts_mutexlock(&process_chain_mutex);
process_chain += delta;
assertf(process_chain >= 0);
hts_mutexrelease(&process_chain_mutex);
}
HTSEXT_API void htsthread_wait(void) {
htsthread_wait_n(0);
}
@@ -108,12 +99,20 @@ static void *hts_entry_point(void *tharg)
void *const arg = s_args->arg;
void (*fun) (void *arg) = s_args->fun;
freet(tharg);
free(tharg);
hts_mutexlock(&process_chain_mutex);
process_chain++;
assertf(process_chain > 0);
hts_mutexrelease(&process_chain_mutex);
/* run */
fun(arg);
process_chain_add(-1);
hts_mutexlock(&process_chain_mutex);
process_chain--;
assertf(process_chain >= 0);
hts_mutexrelease(&process_chain_mutex);
#ifdef _WIN32
return 0;
#else
@@ -123,20 +122,18 @@ static void *hts_entry_point(void *tharg)
/* create a thread */
HTSEXT_API int hts_newthread(void (*fun) (void *arg), void *arg) {
hts_thread_s *s_args = malloct(sizeof(hts_thread_s));
hts_thread_s *s_args = malloc(sizeof(hts_thread_s));
assertf(s_args != NULL);
s_args->arg = arg;
s_args->fun = fun;
process_chain_add(1);
#ifdef _WIN32
{
unsigned int idt;
HANDLE handle =
(HANDLE) _beginthreadex(NULL, 0, hts_entry_point, s_args, 0, &idt);
if (handle == 0) {
process_chain_add(-1);
freet(s_args);
free(s_args);
return -1;
} else {
/* detach the thread from the main process so that is can be independent */
@@ -154,8 +151,7 @@ HTSEXT_API int hts_newthread(void (*fun) (void *arg), void *arg) {
|| pthread_attr_setstacksize(&attr, stackSize) != 0
|| (retcode =
pthread_create(&handle, &attr, hts_entry_point, s_args)) != 0) {
process_chain_add(-1);
freet(s_args);
free(s_args);
return -1;
} else {
/* detach the thread from the main process so that is can be independent */

View File

@@ -101,8 +101,8 @@ static void htsweb_sig_brpipe(int code) {
/* ignore */
}
/* Threads that never return; no wait may count on them draining. */
static int nonjoinable_threads = 0;
/* Number of background threads */
static int background_threads = 0;
/* Server/client ping handling */
static htsmutex pingMutex = HTSMUTEX_INIT;
@@ -299,19 +299,15 @@ int main(int argc, char *argv[]) {
/* pinger */
if (parentPid > 0) {
if (hts_newthread(client_ping, (void *) (uintptr_t) parentPid) == 0) {
#ifndef _WIN32
nonjoinable_threads++; /* client_ping() only ever leaves through exit() */
#endif
}
hts_newthread(client_ping, (void *) (uintptr_t) parentPid);
background_threads++; /* Do not wait for this thread! */
smallserver_setpinghandler(pingHandler, NULL);
}
/* launch */
ret = help_server(argv[1], defaultPort, bindAddr);
/* Drain everything a mirror may still have in flight, the pinger aside. */
htsthread_wait_n(nonjoinable_threads);
htsthread_wait_n(background_threads - 1);
hts_uninit();
#ifdef _WIN32
@@ -386,6 +382,7 @@ void webhttrack_main(char *cmd) {
commandRunning = 1;
DEBUG(fprintf(stderr, "commandRunning=1\n"));
hts_newthread(back_launch_cmd, (void *) strdup(cmd));
background_threads++; /* Do not wait for this thread! */
}
void webhttrack_lock(void) {
@@ -426,8 +423,8 @@ static int webhttrack_runmain(httrackp * opt, int argc, char **argv) {
/* Rock'in! */
ret = hts_main2(argc, argv, opt);
/* Wait for pending threads to finish; the pinger and this thread stay. */
htsthread_wait_n(nonjoinable_threads + 1);
/* Wait for pending threads to finish */
htsthread_wait_n(background_threads);
return ret;
}

View File

@@ -306,8 +306,8 @@ int main(int argc, char **argv) {
fprintf(stderr, "* %s\n", hts_errmsg(opt));
}
global_opt = NULL;
htsthread_wait(); /* pending threads still read opt */
hts_free_opt(opt);
htsthread_wait(); /* wait for pending threads */
hts_uninit();
#ifdef _WIN32

View File

@@ -79,29 +79,6 @@ done
# 65535 is a valid port the old "< 65535" bound refused
web_accepted 65535
# --- htsserver returns from main() instead of blocking at exit -------------
# The exit wait must exclude exactly the threads that never return (#753).
# $tmp has no lang.def, so the server fails right after announcing and main()
# reaches the wait on its own; rc, not EXITED, carries the verdict.
web_exits() {
local rc=0
: >"$tmp/exit.log"
run_with_timeout 30 htsserver "$tmp" "$@" >"$tmp/exit.log" 2>&1 || rc=$?
grep -q "^EXITED" "$tmp/exit.log" ||
! echo "FAIL: #753: htsserver ${*:-(no options)} never finished serving" || exit 1
# exactly 1, not merely "not 124": a crash or an assertf abort also escapes
# the wait, and would otherwise read as a pass
test "$rc" -eq 1 ||
! echo "FAIL: #753: htsserver ${*:-(no options)} exited $rc, want 1" || exit 1
}
# no pinger: the excluded count went negative
web_exits
# a pinger, which never returns and so must stay excluded
web_exits --ppid $$
# --- proxytrack <proxy-addr:port> <ICP-addr:port> --------------------------
# A bad argument falls through to the usage screen; it had no range check at
# all, so 65616 quietly listened on port 80. A valid one binds and blocks.