Compare commits

..

1 Commits

Author SHA1 Message Date
Xavier Roche
abbe806907 Windows CI: cache vcpkg binary archives across runs
vcpkg builds openssl/brotli/zlib/zstd from source on every Windows run.
Redirect its files binary cache into the workspace and persist it with
actions/cache, keyed on the manifest (which carries the builtin-baseline).

x-gha, which used to do this, was dropped from vcpkg-tool (#1662) after
GitHub changed the Actions cache API; actions/cache over the archive dir is
the endorsed replacement and stays within the GitHub-owned-actions policy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-25 09:56:43 +02:00
27 changed files with 117 additions and 699 deletions

View File

@@ -6,9 +6,3 @@ updates:
directory: /src
schedule:
interval: weekly
# Keep the workflow action pins current (they only rot manually otherwise).
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly

View File

@@ -31,7 +31,7 @@ jobs:
env:
CC: ${{ matrix.cc }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -75,7 +75,7 @@ jobs:
name: build (no python3, Debian buildd)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -119,7 +119,7 @@ jobs:
name: build (macOS arm64, clang)
runs-on: macos-14
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -166,7 +166,7 @@ jobs:
name: webhttrack smoke (macOS arm64)
runs-on: macos-14
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -199,7 +199,7 @@ jobs:
name: build (linux i386, gcc -m32)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -242,7 +242,7 @@ jobs:
name: sanitize (ASan+UBSan, gcc)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -296,7 +296,7 @@ jobs:
name: msan (MemorySanitizer, clang)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -344,7 +344,7 @@ jobs:
name: fuzz (libFuzzer corpus replay, clang)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -383,7 +383,7 @@ jobs:
name: build (no openssl, --disable-https)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -437,7 +437,7 @@ jobs:
zlib1g-dev libssl-dev libbrotli-dev libzstd-dev \
debhelper devscripts lintian fakeroot
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -468,7 +468,7 @@ jobs:
name: distcheck (release tarball)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -493,7 +493,7 @@ jobs:
if: github.event_name == 'pull_request'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -537,7 +537,7 @@ jobs:
tests/*.test
tools/mkdeb.sh
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
- name: Install linters
run: |
@@ -572,7 +572,7 @@ jobs:
if: github.event_name == 'pull_request'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -624,7 +624,7 @@ jobs:
if: github.event_name == 'pull_request'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: 0

View File

@@ -26,7 +26,7 @@ jobs:
# Upload findings to the repo's code-scanning dashboard.
security-events: write
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
submodules: recursive

View File

@@ -29,7 +29,7 @@ jobs:
env:
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}\vcpkg_cache
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v4
with:
submodules: recursive # coucal lives in src/coucal
@@ -60,7 +60,7 @@ jobs:
# carries the builtin-baseline, so a Dependabot bump busts it; restore-keys
# still seeds the unchanged ports' archives, so only the bumped one rebuilds.
- name: Cache vcpkg binary archives
uses: actions/cache@v6
uses: actions/cache@v4
with:
path: ${{ github.workspace }}\vcpkg_cache
key: vcpkg-${{ matrix.platform }}-${{ hashFiles('src/vcpkg.json') }}
@@ -233,7 +233,7 @@ jobs:
- name: Upload the test logs
if: always()
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: engine-tests-${{ matrix.platform }}-${{ matrix.configuration }}
path: tests/*.log
@@ -241,7 +241,7 @@ jobs:
- name: Upload MSBuild logs
if: always()
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v4
with:
name: msbuild-${{ matrix.platform }}-${{ matrix.configuration }}
path: msbuild-*.log

View File

@@ -66,11 +66,10 @@ AC_SUBST(LT_CV_OBJDIR,$lt_cv_objdir)
AC_SUBST(VERSION_INFO)
### Default CFLAGS
# No -Wdeclaration-after-statement: nothing sets -std=, so this builds as gnu17.
DEFAULT_CFLAGS="-Wall -Wformat -Wformat-security \
-Wmultichar -Wwrite-strings -Wcast-qual -Wcast-align \
-Wstrict-prototypes -Wmissing-prototypes \
-Wmissing-declarations \
-Wmissing-declarations -Wdeclaration-after-statement \
-Wpointer-arith -Wsequence-point -Wnested-externs \
-D_REENTRANT"
AC_SUBST(DEFAULT_CFLAGS)
@@ -78,28 +77,26 @@ DEFAULT_LDFLAGS=""
AC_SUBST(DEFAULT_LDFLAGS)
### Additional flags (if supported)
# -Werror on probes: exit-status-only checks let clang's warn-on-unknown-flag through.
AX_CHECK_COMPILE_FLAG([-Wparentheses], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wparentheses"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Winit-self], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Winit-self"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Wunused-but-set-parameter], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wunused-but-set-parameter"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Waddress], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Waddress"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Wuninitialized], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wuninitialized"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Wformat=2], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wformat=2"], [], [-Werror])
# -Wformat-nonliteral needs -Wformat in the probe or gcc rejects it as ignored.
AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wformat-nonliteral"], [], [-Werror -Wformat])
AX_CHECK_COMPILE_FLAG([-Wmissing-parameter-type], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wmissing-parameter-type"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Wold-style-definition], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wold-style-definition"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Wignored-qualifiers], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wignored-qualifiers"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Wparentheses], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wparentheses"])
AX_CHECK_COMPILE_FLAG([-Winit-self], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Winit-self"])
AX_CHECK_COMPILE_FLAG([-Wunused-but-set-parameter], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wunused-but-set-parameter"])
AX_CHECK_COMPILE_FLAG([-Waddress], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Waddress"])
AX_CHECK_COMPILE_FLAG([-Wuninitialized], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wuninitialized"])
AX_CHECK_COMPILE_FLAG([-Wformat=2], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wformat=2"])
AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wformat-nonliteral"])
AX_CHECK_COMPILE_FLAG([-Wmissing-parameter-type], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wmissing-parameter-type"])
AX_CHECK_COMPILE_FLAG([-Wold-style-definition], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wold-style-definition"])
AX_CHECK_COMPILE_FLAG([-Wignored-qualifiers], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Wignored-qualifiers"])
# Make htssafe.h's pointer-dest 'warning' attribute a hard error in our build
# (migration is at zero; a new char* dest is a regression). gcc/clang each take
# only their own spelling; downstream keeps the plain warning, not a build break.
AX_CHECK_COMPILE_FLAG([-Werror=attribute-warning], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Werror=attribute-warning"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Werror=user-defined-warnings], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Werror=user-defined-warnings"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-fstrict-aliasing -Wstrict-aliasing], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fstrict-aliasing -Wstrict-aliasing"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-Werror=attribute-warning], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Werror=attribute-warning"])
AX_CHECK_COMPILE_FLAG([-Werror=user-defined-warnings], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -Werror=user-defined-warnings"])
AX_CHECK_COMPILE_FLAG([-fstrict-aliasing -Wstrict-aliasing], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fstrict-aliasing -Wstrict-aliasing"])
AX_CHECK_COMPILE_FLAG([-fstack-protector-strong], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fstack-protector-strong"],
[AX_CHECK_COMPILE_FLAG([-fstack-protector], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fstack-protector"], [], [-Werror])], [-Werror])
AX_CHECK_COMPILE_FLAG([-fstack-clash-protection], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fstack-clash-protection"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-fcf-protection], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fcf-protection"], [], [-Werror])
[AX_CHECK_COMPILE_FLAG([-fstack-protector], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fstack-protector"])])
AX_CHECK_COMPILE_FLAG([-fstack-clash-protection], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fstack-clash-protection"])
AX_CHECK_COMPILE_FLAG([-fcf-protection], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -fcf-protection"])
AX_CHECK_LINK_FLAG([-Wl,--discard-all], [DEFAULT_LDFLAGS="$DEFAULT_LDFLAGS -Wl,--discard-all"])
AX_CHECK_LINK_FLAG([-Wl,--no-undefined], [DEFAULT_LDFLAGS="$DEFAULT_LDFLAGS -Wl,--no-undefined"])
AX_CHECK_LINK_FLAG([-Wl,-z,relro,-z,now], [DEFAULT_LDFLAGS="$DEFAULT_LDFLAGS -Wl,-z,relro,-z,now"])
@@ -122,13 +119,13 @@ AC_SUBST([LIBC_FORCE_LINK])
### PIE
CFLAGS_PIE=""
LDFLAGS_PIE=""
AX_CHECK_COMPILE_FLAG([-fpie], [CFLAGS_PIE="-fpie"], [], [-Werror])
AX_CHECK_COMPILE_FLAG([-fpie -pie], [CFLAGS_PIE="-fpie -pie"])
AX_CHECK_LINK_FLAG([-pie], [LDFLAGS_PIE="-pie"])
AC_SUBST([CFLAGS_PIE])
AC_SUBST([LDFLAGS_PIE])
## -rdynamic must be a link flag; DEFAULT_CFLAGS (AM_CPPFLAGS) never reaches the linker.
AX_CHECK_LINK_FLAG([-rdynamic], [DEFAULT_LDFLAGS="$DEFAULT_LDFLAGS -rdynamic"])
## Export all symbols for backtraces
AX_CHECK_COMPILE_FLAG([-rdynamic], [DEFAULT_CFLAGS="$DEFAULT_CFLAGS -rdynamic"])
### Check for -fvisibility=hidden support
gl_VISIBILITY
@@ -172,7 +169,7 @@ AC_CHECK_TYPE(sa_family_t, [], [AC_DEFINE([sa_family_t], [uint16_t], [sa_family_
AX_CHECK_ALIGNED_ACCESS_REQUIRED
# check for various headers
AC_CHECK_HEADERS([execinfo.h sys/ioctl.h])
AC_CHECK_HEADERS([execinfo.h])
### zlib
CHECK_ZLIB()

View File

@@ -629,8 +629,8 @@ static htsblk cache_readex_new(httrackp * opt, cache_back * cache,
// File exists on disk with declared cache name (this is expected!)
if (fexist_utf8(fconv(catbuff, sizeof(catbuff), previous_save))) { // un fichier existe déja
// Expected size ?
const LLint fsize = fsize_utf8(
fconv(catbuff, sizeof(catbuff), previous_save));
const size_t fsize =
fsize_utf8(fconv(catbuff, sizeof(catbuff), previous_save));
if (fsize == r.size) {
// Target name is the previous name, and the file looks good: nothing to do!
if (strcmp(previous_save, target_save) == 0) {
@@ -666,8 +666,7 @@ static htsblk cache_readex_new(httrackp * opt, cache_back * cache,
// Suppose a broken mirror, with a file being renamed: OK
else if (fexist_utf8(fconv(catbuff, sizeof(catbuff), target_save))) {
// Expected size ?
const LLint fsize =
fsize_utf8(fconv(catbuff, sizeof(catbuff), target_save));
const size_t fsize = fsize_utf8(fconv(catbuff, sizeof(catbuff), target_save));
if (fsize == r.size) {
// So far so good

View File

@@ -390,10 +390,6 @@ HTSEXT_API char *hts_convertStringSystemToUTF8(const char *s, size_t size) {
return hts_convertStringCPToUTF8(s, size, GetACP());
}
HTSEXT_API char *hts_convertStringUTF8ToSystem(const char *s, size_t size) {
return hts_convertStringCPFromUTF8(s, size, GetACP());
}
HTSEXT_API void hts_argv_utf8(int *pargc, char ***pargv) {
int wargc = 0;
LPWSTR *const wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);

View File

@@ -184,12 +184,6 @@ extern LPWSTR hts_pathToUCS2(const char *path);
**/
HTSEXT_API char *hts_convertStringSystemToUTF8(const char *s, size_t size);
/**
* Convert UTF-8 to the current system codepage. Caller frees; NULL upon error.
* This function is WIN32 specific.
**/
HTSEXT_API char *hts_convertStringUTF8ToSystem(const char *s, size_t size);
/**
* Replace the CRT's ANSI argv by a UTF-8 one decoded from the real UTF-16
* command line: every char* is UTF-8 on Windows (FOPEN, STAT, ... convert at

View File

@@ -696,19 +696,17 @@ int httpmirror(char *url1, httrackp * opt) {
// copier adresse(s) dans liste des adresses
{
char *a = url1;
const LLint list_sz = StringNotEmpty(opt->filelist)
? fsize_utf8(StringBuff(opt->filelist))
: 0;
/* two bytes reserved per list byte; -1 makes an undoublable size refused */
const LLint list_room =
list_sz > 0 ? (list_sz <= INT64_MAX / 2 ? list_sz * 2 : -1) : 0;
const size_t primary_len =
llint_grow_size_t(8192 + strlen(url1) * 2, list_room, 0);
int primary_len = 8192;
if (StringNotEmpty(opt->filelist)) {
primary_len += max(0, fsize_utf8(StringBuff(opt->filelist)) * 2);
}
primary_len += (int) strlen(url1) * 2;
// création de la première page, qui contient les liens de base à scanner
// c'est plus propre et plus logique que d'entrer à la main les liens dans la pile
// on bénéficie ainsi des vérifications et des tests du robot pour les liens "primaires"
primary = primary_len != (size_t) -1 ? (char *) malloct(primary_len) : NULL;
primary = (char *) malloct(primary_len);
if (!primary) {
printf("PANIC! : Not enough memory [%d]\n", __LINE__);
XH_extuninit;
@@ -889,7 +887,7 @@ int httpmirror(char *url1, httrackp * opt) {
}
if (filelist_buff != NULL) {
size_t filelist_ptr = 0;
int filelist_ptr = 0;
int n = 0;
char BIGSTK line[HTS_URLMAXSIZE * 2];

View File

@@ -145,7 +145,7 @@ static int hts_main_internal(int argc, char **argv, httrackp * opt) {
int argv_url = -1; // ==0 : utiliser cache et doit.log
char *argv_firsturl = NULL; // utilisé pour nommage par défaut
char *url = NULL; // URLS séparées par un espace
size_t url_sz = 65535;
int url_sz = 65535;
// the parametres
int httrack_logmode = 3; // ONE log file
@@ -224,22 +224,21 @@ static int hts_main_internal(int argc, char **argv, httrackp * opt) {
/* create x_argvblk buffer for transformed command line */
{
size_t current_size = 0;
const LLint size = fsize("config");
size_t blk_size;
int current_size = 0;
int size;
int na;
for(na = 0; na < argc; na++)
current_size += strlen(argv[na]) + 1;
/* a huge file named "config" must saturate, not wrap, the capacity */
blk_size = llint_grow_size_t(current_size, size > 0 ? size : 0, 32768);
x_argvblk = blk_size != (size_t) -1 ? (char *) malloct(blk_size) : NULL;
current_size += (int) (strlen(argv[na]) + 1);
if ((size = fsize("config")) > 0)
current_size += size;
x_argvblk = (char *) malloct(current_size + 32768);
if (x_argvblk == NULL) {
HTS_PANIC_PRINTF("Error, not enough memory");
htsmain_free();
return -1;
}
x_argvblk_size = blk_size;
x_argvblk_size = (size_t) (current_size + 32768);
x_argvblk[0] = '\0';
x_ptr = 0;
@@ -1457,29 +1456,20 @@ static int hts_main_internal(int argc, char **argv, httrackp * opt) {
FILE *fp = FOPEN(argv[na], "rb");
if (fp != NULL) {
size_t cl = strlen(url);
const size_t fzs = llint_to_size_t(fz);
const size_t capa = llint_grow_size_t(cl, fz, 8192);
int cl = (int) strlen(url);
if (capa == (size_t) -1) {
fclose(fp);
HTS_PANIC_PRINTF("File url list too large");
htsmain_free();
return -1;
}
ensureUrlCapacity(url, url_sz, capa);
ensureUrlCapacity(url, url_sz, cl + fz + 8192);
if (cl > 0) { /* don't stick! (3.43) */
url[cl] = ' ';
cl++;
}
if (fread(url + cl, 1, fzs, fp) != fzs) {
fclose(fp);
if (fread(url + cl, 1, fz, fp) != fz) {
HTS_PANIC_PRINTF("File url list could not be read");
htsmain_free();
return -1;
}
fclose(fp);
*(url + cl + fzs) = '\0';
*(url + cl + fz) = '\0';
}
}
}
@@ -2306,8 +2296,8 @@ static int hts_main_internal(int argc, char **argv, httrackp * opt) {
} else { // URL/filters
char catbuff[CATBUFF_SIZE];
const size_t urlSize = strlen(argv[na]);
const size_t capa = strlen(url) + urlSize + 32;
const int urlSize = (int) strlen(argv[na]);
const int capa = (int) (strlen(url) + urlSize + 32);
assertf(urlSize < HTS_URLMAXSIZE);
if (urlSize < HTS_URLMAXSIZE) {

View File

@@ -336,20 +336,16 @@ typedef int INTsys;
#endif
/* Socket-handle type. An unsigned integer wide enough for a Windows SOCKET;
a plain int file descriptor on POSIX. T_SOCP is its printf conversion,
'%' included: unsigned __int64 on Win64 must not be printed with "%d". */
a plain int file descriptor on POSIX. */
#ifdef _WIN32
#if defined(_WIN64)
typedef unsigned __int64 T_SOC;
#define T_SOCP "%" PRIu64
#else
typedef unsigned __int32 T_SOC;
#define T_SOCP "%" PRIu32
#endif
#else
typedef int T_SOC;
#define T_SOCP "%d"
#endif
/* Buffer size for a printed network address (IPv4 or IPv6, NUL included). */

View File

@@ -2598,15 +2598,13 @@ void deletesoc(T_SOC soc) {
if (closesocket(soc) != 0) {
int err = WSAGetLastError();
fprintf(stderr, "* error closing socket " T_SOCP ": %s\n", soc,
strerror(err));
fprintf(stderr, "* error closing socket %d: %s\n", soc, strerror(err));
}
#else
if (close(soc) != 0) {
const int err = errno;
fprintf(stderr, "* error closing socket " T_SOCP ": %s\n", soc,
strerror(err));
fprintf(stderr, "* error closing socket %d: %s\n", soc, strerror(err));
}
#endif
#if HTS_WIDE_DEBUG

View File

@@ -607,21 +607,6 @@ static HTS_UNUSED size_t llint_to_size_t(LLint o) {
}
}
/* Capacity for @p used bytes plus @p extra more plus @p slack spare;
(size_t) -1 if the total exceeds (size_t) -2 or @p extra is negative
(llint_to_size_t() would map that to a huge valid-looking size). */
static HTS_UNUSED size_t llint_grow_size_t(size_t used, LLint extra,
size_t slack) {
const size_t max = (size_t) -2; /* (size_t) -1 is the error value */
const size_t e = extra >= 0 ? llint_to_size_t(extra) : (size_t) -1;
if (e == (size_t) -1 || used > max || slack > max - used ||
e > max - used - slack) {
return (size_t) -1;
}
return used + e + slack;
}
/* dirent() compatibility */
#ifdef _WIN32
/* Holds a UTF-8 d_name: MAX_PATH (260) UTF-16 units expand to <=3 bytes each.

View File

@@ -4583,7 +4583,7 @@ int hts_wait_delayed(htsmoduleStruct * str, lien_adrfilsave *afs,
/* seen as in error */
in_error = back[b].r.statuscode;
in_error_msg[0] = 0;
strncatbuff(in_error_msg, back[b].r.msg, sizeof(in_error_msg) - 1);
strncat(in_error_msg, back[b].r.msg, sizeof(in_error_msg) - 1);
in_error_size = back[b].r.totalsize;
/* don't break, even with "don't take error pages" switch, because we need to process the slot anyway (and cache the error) */
}

View File

@@ -116,15 +116,6 @@ static HTS_UNUSED void abortf_(const char *exp, const char *file, int line) {
#endif
#define HTS_IS_NOT_CHAR_BUFFER(VAR) (!HTS_IS_CHAR_BUFFER(VAR))
/* Source capacity for the buff() family, (size_t)-1 when unknown; sizeof of the
TYPE keeps a decayed operand ("buf + 1") off -Wsizeof-array-decay. */
#if (defined(__GNUC__) && !defined(__cplusplus))
#define HTS_SIZEOF_SRC_(B) \
(HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(__typeof__(B)))
#else
#define HTS_SIZEOF_SRC_(B) (HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B))
#endif
/* Compile-time checks. */
static HTS_UNUSED void htssafe_compile_time_check_(void) {
char array[32];
@@ -214,17 +205,19 @@ static char *strncatbuff_ptr_(char *dest, const char *src, size_t n) {
#if (defined(__GNUC__) && !defined(__cplusplus))
#define strncatbuff(A, B, N) \
__builtin_choose_expr(HTS_IS_CHAR_BUFFER(A), \
strncat_safe_(A, sizeof(A), B, HTS_SIZEOF_SRC_(B), N, \
"overflow while appending '" #B \
"' to '" #A "'", \
__FILE__, __LINE__), \
strncatbuff_ptr_((A), (B), (N)))
__builtin_choose_expr( \
HTS_IS_CHAR_BUFFER(A), \
strncat_safe_(A, sizeof(A), B, \
HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), N, \
"overflow while appending '" #B "' to '" #A "'", __FILE__, \
__LINE__), \
strncatbuff_ptr_((A), (B), (N)))
#else
#define strncatbuff(A, B, N) \
(HTS_IS_NOT_CHAR_BUFFER(A) \
? strncat(A, B, N) \
: strncat_safe_(A, sizeof(A), B, HTS_SIZEOF_SRC_(B), N, \
: strncat_safe_(A, sizeof(A), B, \
HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), N, \
"overflow while appending '" #B "' to '" #A "'", \
__FILE__, __LINE__))
#endif
@@ -239,7 +232,9 @@ static char *strncatbuff_ptr_(char *dest, const char *src, size_t n) {
#define strcatbuff(A, B) \
__builtin_choose_expr( \
HTS_IS_CHAR_BUFFER(A), \
strncat_safe_(A, sizeof(A), B, HTS_SIZEOF_SRC_(B), (size_t) -1, \
strncat_safe_(A, sizeof(A), B, \
HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
(size_t) -1, \
"overflow while appending '" #B "' to '" #A "'", __FILE__, \
__LINE__), \
strcatbuff_ptr_((A), (B)))
@@ -247,7 +242,9 @@ static char *strncatbuff_ptr_(char *dest, const char *src, size_t n) {
#define strcatbuff(A, B) \
(HTS_IS_NOT_CHAR_BUFFER(A) \
? strcat(A, B) \
: strncat_safe_(A, sizeof(A), B, HTS_SIZEOF_SRC_(B), (size_t) -1, \
: strncat_safe_(A, sizeof(A), B, \
HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
(size_t) -1, \
"overflow while appending '" #B "' to '" #A "'", \
__FILE__, __LINE__))
#endif
@@ -260,17 +257,19 @@ static char *strncatbuff_ptr_(char *dest, const char *src, size_t n) {
#if (defined(__GNUC__) && !defined(__cplusplus))
#define strcpybuff(A, B) \
__builtin_choose_expr(HTS_IS_CHAR_BUFFER(A), \
strcpy_safe_(A, sizeof(A), B, HTS_SIZEOF_SRC_(B), \
"overflow while copying '" #B "' to '" #A \
"'", \
__FILE__, __LINE__), \
strcpybuff_ptr_((A), (B)))
__builtin_choose_expr( \
HTS_IS_CHAR_BUFFER(A), \
strcpy_safe_(A, sizeof(A), B, \
HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
"overflow while copying '" #B "' to '" #A "'", __FILE__, \
__LINE__), \
strcpybuff_ptr_((A), (B)))
#else
#define strcpybuff(A, B) \
(HTS_IS_NOT_CHAR_BUFFER(A) \
? strcpy(A, B) \
: strcpy_safe_(A, sizeof(A), B, HTS_SIZEOF_SRC_(B), \
: strcpy_safe_(A, sizeof(A), B, \
HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
"overflow while copying '" #B "' to '" #A "'", __FILE__, \
__LINE__))
#endif
@@ -287,24 +286,24 @@ static char *strncatbuff_ptr_(char *dest, const char *src, size_t n) {
* Append characters of "B" to "A", "A" having a maximum capacity of "S".
*/
#define strlcatbuff(A, B, S) \
strncat_safe_(A, S, B, HTS_SIZEOF_SRC_(B), (size_t) -1, \
"overflow while appending '" #B "' to '" #A "'", __FILE__, \
__LINE__)
strncat_safe_(A, S, B, HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
(size_t) -1, "overflow while appending '" #B "' to '" #A "'", \
__FILE__, __LINE__)
/**
* Append at most "N" characters of "B" to "A", "A" having a maximum capacity
* of "S".
*/
#define strlncatbuff(A, B, S, N) \
strncat_safe_(A, S, B, HTS_SIZEOF_SRC_(B), N, \
"overflow while appending '" #B "' to '" #A "'", __FILE__, \
strncat_safe_(A, S, B, HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
N, "overflow while appending '" #B "' to '" #A "'", __FILE__, \
__LINE__)
/**
* Copy characters of "B" to "A", "A" having a maximum capacity of "S".
*/
#define strlcpybuff(A, B, S) \
strcpy_safe_(A, S, B, HTS_SIZEOF_SRC_(B), \
strcpy_safe_(A, S, B, HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
"overflow while copying '" #B "' to '" #A "'", __FILE__, \
__LINE__)
@@ -423,9 +422,7 @@ static HTS_INLINE HTS_UNUSED htsbuff htsbuff_ptr_(char *buf, size_t cap) {
*/
static HTS_INLINE HTS_UNUSED void htsbuff_catn(htsbuff *b, const char *s,
size_t n) {
/* the (size_t)-1 "no limit" sentinel would reach strnlen as a bound past
PTRDIFF_MAX */
const size_t add = n != (size_t) -1 ? strnlen(s, n) : strlen(s);
const size_t add = strnlen(s, n);
/* Overflow-safe: keep the (potentially huge) 'add' alone on one side. The
maintained invariant len < cap makes 'cap - len' >= 1 (no underflow), so
'add < cap - len' cannot wrap the way 'len + add < cap' could. */

View File

@@ -481,28 +481,6 @@ static int string_safety_selftests(void) {
if (strcmp(buf, "abcd") != 0)
return 1;
/* A decayed source has no known capacity, so the whole tail must land; a
sizeof(char*) capacity would abort here instead. */
{
char src[32] = "0123456789abcdefghij";
char dst[32];
strcpybuff(dst, src + 1);
if (strcmp(dst, "123456789abcdefghij") != 0)
return 1;
}
/* Truncating append: stops at N without aborting, what the status-message
call sites rely on. */
{
char dst[10]; /* never sizeof(char*), or MSVC reads it as a pointer */
dst[0] = '\0';
strncatbuff(dst, "abcdefghijkl", sizeof(dst) - 1);
if (strcmp(dst, "abcdefghi") != 0)
return 1;
}
/* strlcpybuff: explicit-capacity copy into a pointer destination, the form
the migration moves toward */
{
@@ -909,66 +887,6 @@ static int st_charset(httrackp *opt, int argc, char **argv) {
return 0;
}
/* Oracle is the raw Win32 two-step this replaces, so a mis-wired direction or
a plain copy fails whatever the machine's ACP. */
static int st_syscharset(httrackp *opt, int argc, char **argv) {
#ifdef _WIN32
static const char *const utf8 = "caf\xC3\xA9 \xE2\x82\xAC"; /* "café €" */
const UINT cp = GetACP();
const int len = (int) strlen(utf8);
WCHAR wide[64], round[64];
char want[64];
char *sys, *back, *part;
int wn, n, lossless;
(void) opt;
(void) argc;
(void) argv;
wn = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wide,
(int) (sizeof(wide) / sizeof(wide[0])));
assertf(wn > 0);
n = WideCharToMultiByte(cp, 0, wide, wn, want, (int) sizeof(want) - 1, NULL,
NULL);
assertf(n > 0);
want[n] = '\0';
/* the ACP holds the string only if its bytes decode back to the same UTF-16;
lpUsedDefaultChar would miss a best-fit mapping (é to a bare e) */
lossless =
MultiByteToWideChar(cp, 0, want, n, round,
(int) (sizeof(round) / sizeof(round[0]))) == wn &&
memcmp(round, wide, (size_t) wn * sizeof(WCHAR)) == 0;
sys = hts_convertStringUTF8ToSystem(utf8, (size_t) len);
assertf(sys != NULL);
assertf(strcmp(sys, want) == 0);
assertf(strlen(sys) == (size_t) n); /* NUL-terminated, nothing past it */
/* the copy the ASCII fast path returns is a pass on a UTF-8 ACP only */
assertf(cp == CP_UTF8 || strcmp(sys, utf8) != 0);
/* size bounds the read: a caller-given length, not strlen() */
part = hts_convertStringUTF8ToSystem(utf8, 3);
assertf(part != NULL && strcmp(part, "caf") == 0);
freet(part);
part = hts_convertStringUTF8ToSystem(utf8, 0);
assertf(part != NULL && *part == '\0');
freet(part);
if (lossless) {
back = hts_convertStringSystemToUTF8(sys, strlen(sys));
assertf(back != NULL);
assertf(strcmp(back, utf8) == 0);
freet(back);
}
freet(sys);
printf("syscharset: acp=%u %s: OK\n", (unsigned) cp,
lossless ? "round-trip" : "one-way");
return 0;
#else
(void) opt;
(void) argc;
(void) argv;
return 77; /* WIN32-only entry point */
#endif
}
static int st_metacharset(httrackp *opt, int argc, char **argv) {
char *s;
@@ -1321,14 +1239,6 @@ static int st_strsafe(httrackp *opt, int argc, char **argv) {
htsbuff b = htsbuff_array(small);
htsbuff_cat(&b, src);
} else if (strcmp(argv[0], "overflow-src") == 0) {
/* Array source with no NUL: its capacity still comes from sizeof(), so
the bounded strlen aborts rather than running off the array. */
char nonul[6]; /* never sizeof(char*), per the note above */
char big[64];
memset(nonul, src[0], sizeof(nonul));
strcpybuff(big, nonul);
} else {
strcpybuff(small, src);
}
@@ -2023,77 +1933,6 @@ static int st_fsize(httrackp *opt, int argc, char **argv) {
return rc;
}
/* 4GB+100KB wraps to ~108KB through an int, and needs 33 unsigned bits. A
macro, not a static const: MSVC's C mode (/TC) rejects a const object
used inside another object's static initializer below (C2099). */
#define HTS_ST_GROWSIZE_OVER32 (4LL * 1024 * 1024 * 1024 + 100 * 1024)
/* llint_grow_size_t() sizes the buffer holding a whole -%S list file: the
result must be the exact 64-bit sum or a clean refusal, never a short one. */
static int st_growsize(httrackp *opt, int argc, char **argv) {
enum { REFUSE, ACCEPT, WIDTH };
static const struct {
size_t used;
LLint extra;
size_t slack;
int want;
} cases[] = {
{0, 0, 0, ACCEPT},
{10, 100, 8192, ACCEPT},
{(size_t) -2 - 8, 4, 4, ACCEPT}, /* exact fit, no room to spare */
{(size_t) -2, 0, 0, ACCEPT}, /* largest representable capacity */
{0, -1, 0, REFUSE}, /* fsize() failure */
/* -1 already maps to SIZE_MAX; only this exercises the negative guard */
{0, -4096, 0, REFUSE},
{(size_t) -1, 1, 0, REFUSE},
{(size_t) -2, 0, 1, REFUSE}, /* slack alone overruns */
{(size_t) -1 - 8, 4, 4, REFUSE}, /* total would be the error value */
{(size_t) -1 - 8, 4, 8, REFUSE},
{0, HTS_ST_GROWSIZE_OVER32, 8192,
WIDTH}, /* 32-bit size_t can't hold these */
{10, HTS_ST_GROWSIZE_OVER32, 8192, WIDTH},
};
size_t k;
int rc = 0;
(void) opt;
(void) argc;
(void) argv;
for (k = 0; k < sizeof(cases) / sizeof(cases[0]); k++) {
const size_t used = cases[k].used, slack = cases[k].slack;
const LLint extra = cases[k].extra;
const size_t got = llint_grow_size_t(used, extra, slack);
const hts_boolean refused = got == (size_t) -1 ? HTS_TRUE : HTS_FALSE;
const hts_boolean exact =
!refused && extra >= 0 && got - used - slack == (size_t) extra;
hts_boolean ok;
switch (cases[k].want) {
case ACCEPT:
ok = exact;
break;
case REFUSE:
ok = refused;
break;
default:
ok = sizeof(size_t) >= sizeof(LLint) ? exact : refused;
break;
}
if (!ok) {
fprintf(stderr,
"growsize: grow(" LLintP ", " LLintP ", " LLintP ") = " LLintP
" (want %s)\n",
(LLint) used, extra, (LLint) slack, (LLint) got,
cases[k].want == REFUSE ? "refusal" : "exact sum");
rc = 1;
}
}
printf("growsize self-test %s\n", rc == 0 ? "OK" : "FAILED");
return rc;
}
static int st_savename(httrackp *opt, int argc, char **argv) {
lien_adrfilsave afs;
cache_back cache;
@@ -4893,8 +4732,6 @@ static const struct selftest_entry {
{"mime", "<filename>", "MIME type for a filename", st_mime},
{"charset", "<charset> <hex:..|string>",
"convert a string to UTF-8 from a charset", st_charset},
{"syscharset", "", "UTF-8 <-> system codepage conversion (WIN32 only)",
st_syscharset},
{"metacharset", "<html>", "extract the <meta> charset from an HTML page",
st_metacharset},
{"isutf8", "<hex:..|string>", "is the string valid UTF-8 (1/0)", st_isutf8},
@@ -4908,8 +4745,8 @@ static const struct selftest_entry {
{"unescape-bounds", "", "unescapers reserve the NUL byte (no 1-byte OOB)",
st_unescape_bounds},
{"hashtable", "<count|file>", "coucal hashtable stress test", st_hashtable},
{"strsafe", "[overflow|overflow-buff|overflow-src [str]]",
"bounded string-op self-test", st_strsafe},
{"strsafe", "[overflow|overflow-buff [str]]", "bounded string-op self-test",
st_strsafe},
{"copyopt", "", "copy_htsopt option-copy self-test", st_copyopt},
{"pause", "", "randomized inter-file pause target self-test", st_pause},
{"relative", "<link> <curr-file>", "relative link between two paths",
@@ -4939,8 +4776,6 @@ static const struct selftest_entry {
{"sniff", "<content-type> <hex:..|text>", "MIME magic consistency",
st_sniff},
{"fsize", "<dir>", "file size past the 2GB signed-32-bit wrap", st_fsize},
{"growsize", "", "buffer capacity for a 64-bit file size (no int wrap)",
st_growsize},
{"cache", "<dir>", "cache read/write round-trip self-test", st_cache},
{"cacheindex", "", "cache-index (.ndx) parse must stay in bounds",
st_cacheindex},

View File

@@ -671,10 +671,10 @@ int __cdecl htsshow_loop(t_hts_callbackarg * carg, httrackp * opt, lien_back * b
char *eps = strchr(back[i].url_adr, '/');
int count;
if (ep != NULL && eps != NULL && ep < eps &&
(count = (int) (ep - back[i].url_adr)) < 4) {
if (ep != NULL && ep < eps
&& (count = (int) (ep - back[i].url_adr)) < 4) {
proto[0] = '\0';
strncatbuff(proto, back[i].url_adr, count);
strncat(proto, back[i].url_adr, count);
}
}
snprintf(StatsBuffer[index].state, sizeof(StatsBuffer[index].state),

View File

@@ -68,9 +68,6 @@ static int linput(FILE * fp, char *s, int max);
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <ctype.h>
#if (defined(__linux) && defined(HAVE_EXECINFO_H))
#include <execinfo.h>
@@ -188,43 +185,6 @@ static void vt_home(void) {
printf("%s%s", VT_RESET, VT_GOTOXY("1", "0"));
}
/* Last known terminal geometry; the defaults are the classic VT100 size. */
static int term_cols = 80;
static int term_rows = 24;
/* Returns HTS_TRUE if the terminal size changed since the last call. */
static hts_boolean vt_size_refresh(void) {
int cols = 0;
int rows = 0;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO info;
const HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
if (console != INVALID_HANDLE_VALUE &&
GetConsoleScreenBufferInfo(console, &info)) {
cols = info.srWindow.Right - info.srWindow.Left + 1;
rows = info.srWindow.Bottom - info.srWindow.Top + 1;
}
#elif defined(TIOCGWINSZ)
struct winsize ws;
if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) == 0) {
cols = ws.ws_col;
rows = ws.ws_row;
}
#endif
if (cols <= 0)
cols = 80;
if (rows <= 0)
rows = 24;
if (cols == term_cols && rows == term_rows)
return HTS_FALSE;
term_cols = cols;
term_rows = rows;
return HTS_TRUE;
}
//
/*
@@ -235,8 +195,7 @@ static hts_boolean vt_size_refresh(void) {
#define STYLE_STATTEXT VT_UNBOLD
#define STYLE_STATRESET VT_UNBOLD
#define NStatsBuffer 14
/* Rows the stats block and "Current job" take above the in-progress list. */
#define NStatsHeaderRows 7
#define MAX_LEN_INPROGRESS 40
static int use_show;
static httrackp *global_opt = NULL;
@@ -332,7 +291,6 @@ static int __cdecl htsshow_start(t_hts_callbackarg * carg, httrackp * opt) {
use_show = 0;
if (opt->verbosedisplay == HTS_VERBOSE_FULL) {
use_show = 1;
(void) vt_size_refresh();
vt_clear();
}
return 1;
@@ -438,10 +396,6 @@ static int __cdecl htsshow_loop(t_hts_callbackarg * carg, httrackp * opt, lien_b
prev_mytime = mytime;
/* A resize leaves stale wrapped text on screen: repaint everything. */
if (vt_size_refresh())
vt_clear();
st[0] = '\0';
qsec2str(st, stat_time);
vt_home();
@@ -481,13 +435,6 @@ static int __cdecl htsshow_loop(t_hts_callbackarg * carg, httrackp * opt, lien_b
//
t_StatsBuffer StatsBuffer[NStatsBuffer];
/* Keep the frame within the terminal, or it scrolls the stats away. */
const int nstats =
min(NStatsBuffer, max(0, term_rows - NStatsHeaderRows));
/* URL budget: half the width, i.e. the historical 40 at 80 columns. */
const int maxurl =
min((int) sizeof(StatsBuffer[0].name) - 1, max(16, term_cols / 2));
{
int i;
@@ -502,11 +449,10 @@ static int __cdecl htsshow_loop(t_hts_callbackarg * carg, httrackp * opt, lien_b
}
}
for(k = 0; k < 2; k++) { // 0: lien en cours 1: autres liens
for (j = 0; (j < 3) && (index < nstats); j++) { // passe de priorité
for(j = 0; (j < 3) && (index < NStatsBuffer); j++) { // passe de priorité
int _i;
for (_i = 0 + k; (_i < max(back_max * k, 1)) && (index < nstats);
_i++) { // no lien
for(_i = 0 + k; (_i < max(back_max * k, 1)) && (index < NStatsBuffer); _i++) { // no lien
int i = (back_index + _i) % back_max; // commencer par le "premier" (l'actuel)
if (back[i].status >= 0) { // signifie "lien actif"
@@ -581,14 +527,16 @@ static int __cdecl htsshow_loop(t_hts_callbackarg * carg, httrackp * opt, lien_b
}
}
if ((l = (int) strlen(s)) < maxurl)
if ((l = (int) strlen(s)) < MAX_LEN_INPROGRESS)
strcpybuff(StatsBuffer[index].name, s);
else {
// couper
StatsBuffer[index].name[0] = '\0';
strncatbuff(StatsBuffer[index].name, s, maxurl / 2 - 2);
strncatbuff(StatsBuffer[index].name, s,
MAX_LEN_INPROGRESS / 2 - 2);
strcatbuff(StatsBuffer[index].name, "...");
strcatbuff(StatsBuffer[index].name, s + l - maxurl / 2 + 2);
strcatbuff(StatsBuffer[index].name,
s + l - MAX_LEN_INPROGRESS / 2 + 2);
}
if (back[i].r.totalsize >= 0) { // taille prédéfinie
@@ -649,7 +597,7 @@ static int __cdecl htsshow_loop(t_hts_callbackarg * carg, httrackp * opt, lien_b
{
int i;
for (i = 0; i < nstats; i++) {
for(i = 0; i < NStatsBuffer; i++) {
if (strnotempty(StatsBuffer[i].state)) {
printf(VT_CLREOL " %s - \t%s%s \t%s / \t%s", StatsBuffer[i].state,
StatsBuffer[i].name, StatsBuffer[i].file, int2bytes(&strc,

View File

@@ -2191,7 +2191,7 @@ static PT_Element PT_ReadCache__Arc_u(PT_Index index_, const char *url,
}
if ((pos = getArcField(index->line, 2)) != NULL) {
r->msg[0] = '\0';
strncatbuff(r->msg, pos, sizeof(r->msg) - 1);
strncat(r->msg, pos, sizeof(pos) - 1);
}
while(linput(index->file, index->line, sizeof(index->line) - 1)
&& index->line[0] != '\0') {

View File

@@ -1,25 +0,0 @@
#!/bin/bash
#
# Buffer capacity for a 64-bit file size ('httrack -#test=growsize'): a -%S list
# file past 4GB must size its buffer exactly or be refused, never wrap.
set -euo pipefail
out=$(httrack -#test=growsize)
echo "$out"
test "$out" == "growsize self-test OK"
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_growsize.XXXXXX")
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT PIPE TERM
echo '<html><body>hi</body></html>' >"$tmp/index.html"
printf -- '-*/zzmarker*\n' >"$tmp/rules.txt"
# the rules file lands in the URL/filter string, echoed back by the banner
run=$(httrack -O "$tmp/out" --quiet -n "-%S" "$tmp/rules.txt" \
"file://$tmp/index.html" 2>&1) || true
printf '%s\n' "$run" | grep -q 'zzmarker' || {
echo "FAIL: -%S rules file was not loaded"
printf '%s\n' "$run"
exit 1
}

View File

@@ -47,18 +47,3 @@ case "$err" in
exit 1
;;
esac
# An array source with no NUL must abort on the source bound rather than run
# off the array: the array half of the source-capacity selection.
err=$(httrack -#test=strsafe overflow-src "x" 2>&1) || true
case "$err" in
*"strsafe: NOT aborted"*)
echo "unterminated source array was NOT caught" >&2
exit 1
;;
*"size < sizeof_source"*) ;;
*)
echo "expected htssafe source-bound abort, got: $err" >&2
exit 1
;;
esac

View File

@@ -1,21 +0,0 @@
#!/bin/bash
#
# UTF-8 <-> system codepage ('httrack -#test=syscharset'), the pair an MBCS GUI
# uses at the Win32 ANSI entry points. WIN32 only, skipped elsewhere.
set -euo pipefail
rc=0
out=$(httrack -O /dev/null -#test=syscharset) || rc=$?
echo "$out"
[ "$rc" = 77 ] && exit 77
[ "$rc" = 0 ] || exit "$rc"
case "$out" in
"syscharset: acp="*": OK") ;;
*)
echo "FAIL: unexpected report"
exit 1
;;
esac

View File

@@ -1,32 +0,0 @@
#!/bin/bash
# An ARC entry's HTTP reason phrase must survive a proxytrack --convert
# round-trip; it used to be clipped to sizeof(char*) - 1 bytes.
set -euo pipefail
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
printf 'HTTP/1.1 404 Not Found Here At All\r\nContent-Type: text/html\r\nLast-Modified: Wed, 01 Jan 2025 00:00:00 GMT\r\nContent-Length: 5\r\n\r\n' >"$dir/hdr"
printf 'hello' >"$dir/body"
alen=$(($(wc -c <"$dir/hdr") + $(wc -c <"$dir/body")))
# ARC 1.0: filedesc record and version block, then per entry
# <nl> <URL-record> <nl> <headers> <body>; the record's last field is that length.
{
printf 'filedesc://t.arc 0.0.0.0 20250101000000 text/plain 200 - - 0 t.arc 9\n'
printf '2 0 test\n'
printf '\n\n'
printf 'http://example.com/page.html 0.0.0.0 20250101000000 text/html 404 - - 0 t.arc %d\n' "$alen"
cat "$dir/hdr" "$dir/body"
} >"$dir/in.arc"
proxytrack --convert "$dir/out.arc" "$dir/in.arc" >/dev/null 2>&1
# HTTP/1.0 is the writer's own prefix (the input says 1.1), so a verbatim copy
# of the input cannot satisfy this match.
grep -aqF 'HTTP/1.0 404 Not Found Here At All' "$dir/out.arc" || {
echo "reason phrase lost in the ARC round-trip:" >&2
grep -a '^HTTP/' "$dir/out.arc" >&2 || echo "(no status line at all)" >&2
exit 1
}

View File

@@ -1,70 +0,0 @@
#!/bin/bash
# #97: a terminal resize must repaint the whole screen, and the frame must
# follow the new geometry instead of the hardcoded 80x24 one.
set -eu
: "${top_srcdir:=..}"
testdir=$(cd "$(dirname "$0")" && pwd)
# shellcheck source=tests/testlib.sh
. "${testdir}/testlib.sh"
server=$(nativepath "${testdir}/local-server.py")
root=$(nativepath "${testdir}/server-root")
driver=$(nativepath "${testdir}/pty-resize.py")
if is_windows; then
echo "no pty on Windows; skipping" >&2
exit 77
fi
python=$(find_python) || ! echo "python3 not found; skipping" >&2 || exit 77
tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/httrack_resize.XXXXXX") || exit 1
serverpid=
cleanup() {
stop_server "$serverpid"
rm -rf "$tmpdir"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
serverlog="${tmpdir}/server.log"
: >"$serverlog"
"$python" "$server" --root "$root" >"$serverlog" 2>&1 &
serverpid=$!
port=
for _ in $(seq 1 50); do
line=$(head -n1 "$serverlog" 2>/dev/null)
if test "${line%% *}" == "PORT"; then
port="${line#PORT }"
break
fi
kill -0 "$serverpid" 2>/dev/null || {
echo "server exited early: $(cat "$serverlog")"
exit 1
}
sleep 0.1
done
test -n "$port" || {
echo "could not discover server port"
exit 1
}
which httrack >/dev/null || {
echo "could not find httrack"
exit 1
}
# Trickled pages under a long path: the display keeps animating while the pty is
# resized under it, and the URL is truncated at 80 columns but not at 200.
deep="127.0.0.1:${port}/trickle/deep/a-long-directory-segment/another-long-segment"
out="${tmpdir}/crawl"
mkdir "$out"
rc=0
# The in-progress rows show host:port, not the scheme.
"$python" "$driver" "${tmpdir}/pty.raw" "${deep}/p" -- \
httrack "http://${deep}/index.html" \
-O "$out" -%v --robots=0 --disable-security-limits \
--timeout=30 --retries=1 -c4 --max-time 60 || rc=$?
test "$rc" -eq 0 || {
echo "pty capture tail:"
tail -c 600 "${tmpdir}/pty.raw" | cat -v
exit 1
}

View File

@@ -4,7 +4,6 @@
EXTRA_DIST = $(TESTS) crawl-test.sh run-all-tests.sh check-network.sh \
proxy-https-server.py socks5-server.py proxy-connect-server.py \
proxytestlib.py tls-stall-server.py warc-validate.py wacz-validate.py \
pty-resize.py \
local-crawl.sh local-server.py testlib.sh server.crt server.key \
server-root/simple/basic.html server-root/simple/link.html \
server-root/stripquery/index.html server-root/stripquery/a.html \
@@ -64,7 +63,6 @@ TESTS = \
01_engine-reconcile.test \
01_engine-expandhome.test \
01_engine-fsize.test \
01_engine-growsize.test \
01_engine-redirect.test \
01_engine-longpath-io.test \
01_engine-mirror-io.test \
@@ -79,7 +77,6 @@ TESTS = \
01_engine-status.test \
01_engine-stripquery.test \
01_engine-strsafe.test \
01_engine-syscharset.test \
01_engine-topindex.test \
01_engine-urlhack.test \
01_engine-unescape-bounds.test \
@@ -148,7 +145,6 @@ TESTS = \
50_local-contentcodings.test \
51_local-update-codec.test \
52_local-socks5.test \
53_local-proxytrack-arc-reason.test \
53_local-proxytrack-cache-corrupt.test \
54_local-update-truncate-purge.test \
55_local-chunked.test \
@@ -171,7 +167,6 @@ TESTS = \
73_local-warc.test \
74_local-warc-wacz.test \
74_local-warc-verbatim.test \
75_engine-longpath-posix.test \
76_cli-resize.test
75_engine-longpath-posix.test
CLEANFILES = check-network_sh.cache

View File

@@ -457,9 +457,6 @@ def _big_sitemap(port):
).encode()
DEEPDIR = "/trickle/deep/a-long-directory-segment/another-long-segment"
class Handler(SimpleHTTPRequestHandler):
# Quieter logging; the launcher captures httrack's own log anyway.
def log_message(self, fmt, *args):
@@ -1464,11 +1461,6 @@ class Handler(SimpleHTTPRequestHandler):
def route_trickle_index(self):
self.send_bin_index()
# #97: a path long enough that the CLI in-progress column truncates it at 80
# columns but not at 200. Its own index: /trickle/ is asserted on elsewhere.
def route_deeptrickle_index(self):
self.send_html('\t<a href="p0.bin">p0</a>\n\t<a href="p1.bin">p1</a>\n')
def route_trickle_page(self):
self.send_response(200)
self.send_header("Content-Type", "application/octet-stream")
@@ -1654,9 +1646,6 @@ class Handler(SimpleHTTPRequestHandler):
"/trickle/p5.bin": route_trickle_page,
"/trickle/p6.bin": route_trickle_page,
"/trickle/p7.bin": route_trickle_page,
DEEPDIR + "/index.html": route_deeptrickle_index,
DEEPDIR + "/p0.bin": route_trickle_page,
DEEPDIR + "/p1.bin": route_trickle_page,
"/dcancel/index.html": route_dcancel_index,
"/dcancel/p0.bin": route_dcancel_page,
"/dcancel/p1.bin": route_dcancel_page,

View File

@@ -1,130 +0,0 @@
#!/usr/bin/env python3
"""Drive httrack on a pty and check the animated display follows the terminal.
Usage: pty-resize.py <capture-file> <long-url-prefix> -- <cmd> [args...]
"""
import fcntl
import os
import pty
import select
import signal
import struct
import subprocess
import sys
import termios
import time
CLEAR = b"\033[2J"
FRAME = b"\033[1;1f" # every refresh homes here before painting the stats
LIVE = b"Bytes saved"
class Term:
def __init__(self, argv, capture):
self.capture = capture
self.buf = bytearray()
self.master, slave = pty.openpty()
self.resize(24, 80)
self.proc = subprocess.Popen(
argv, stdin=slave, stdout=slave, stderr=slave, start_new_session=True
)
os.close(slave)
def resize(self, rows, cols):
fcntl.ioctl(
self.master, termios.TIOCSWINSZ, struct.pack("HHHH", rows, cols, 0, 0)
)
def _read(self, timeout):
ready, _, _ = select.select([self.master], [], [], timeout)
if not ready:
return b""
try:
return os.read(self.master, 65536)
except OSError:
return b""
def pump(self, seconds):
"""Read for the given window; return only the bytes read during it."""
mark = len(self.buf)
end = time.monotonic() + seconds
while time.monotonic() < end:
self.buf += self._read(0.1)
return bytes(self.buf[mark:])
def wait_for(self, pattern, timeout):
mark = len(self.buf)
end = time.monotonic() + timeout
while time.monotonic() < end:
self.buf += self._read(0.1)
if pattern in bytes(self.buf[mark:]):
return True
if self.proc.poll() is not None:
break
return False
def alive(self):
return self.proc.poll() is None
def close(self):
if self.alive():
os.killpg(self.proc.pid, signal.SIGKILL)
self.proc.wait()
os.close(self.master)
with open(self.capture, "wb") as fp:
fp.write(self.buf)
def frame_heights(chunk):
"""Newline count of each complete frame in the chunk."""
return [f.count(b"\n") for f in chunk.split(FRAME)[1:-1]]
def main():
capture, long_url = sys.argv[1], sys.argv[2].encode()
argv = sys.argv[sys.argv.index("--") + 1 :]
term = Term(argv, capture)
failures = []
def check(cond, msg):
if not cond:
failures.append(msg)
return cond
try:
if not check(term.wait_for(LIVE, 15), "the display never started"):
return 1
# Steady state: a repaint here would mask any resize-driven one below.
idle = term.pump(1.0)
check(CLEAR not in idle, "repaint without a resize")
check(long_url not in idle, "long URL not truncated at 80 columns")
term.resize(24, 120)
check(term.wait_for(CLEAR, 3), "no repaint after a width-only resize")
check(CLEAR not in term.pump(1.0), "repaint kept firing after the resize")
term.resize(10, 120)
check(term.wait_for(CLEAR, 3), "no repaint after a height-only resize")
heights = frame_heights(term.pump(1.0))
check(heights, "no frame after the height change")
check(
all(h <= 9 for h in heights),
"frame overflows a 10-row terminal: %s" % heights,
)
term.resize(24, 200)
check(term.wait_for(CLEAR, 3), "no repaint after growing the terminal")
check(long_url in term.pump(1.0), "long URL still truncated at 200 columns")
check(term.alive(), "httrack died mid-run (exit %s)" % term.proc.poll())
finally:
term.close()
for msg in failures:
print("FAIL: %s" % msg)
return 1 if failures else 0
if __name__ == "__main__":
sys.exit(main())