Compare commits

...

5 Commits

Author SHA1 Message Date
Xavier Roche
0cbd5279f2 Merge pull request #404 from xroche/release/3.49.8
Curate the 3.49-8 release notes
2026-06-20 13:06:13 +02:00
Xavier Roche
05306ee4fd Curate the 3.49-8 release notes
Round out the 3.49-8 entry in history.txt and the debian changelog with the
user-facing work landed since 3.49-7: the HTTPS-proxy CONNECT tunnel, wider
srcset parsing, the crawler and parser fixes (CSS @import, xmlns, relative
paths, RFC 6265 cookies, doit.log reload), the parser and engine buffer-copy
security hardening, and brief summary lines for the API, build, CI and test
work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-06-20 13:02:51 +02:00
Xavier Roche
1d0fc0a566 Merge pull request #403 from xroche/chore/clang-format-separate-defs
Separate definition blocks in the public headers
2026-06-20 12:56:23 +02:00
Xavier Roche
a4452592b4 Separate definition blocks and canonicalize the public headers
Set SeparateDefinitionBlocks: Always in .clang-format so clang-format keeps
a blank line between adjacent definitions, then reformat the installed
(DevIncludes) headers in full. Several of them packed struct/typedef/macro
definitions with no separation and carried non-canonical spacing (char*,
__attribute__ ((x)), padded inner parens), which made them hard to read;
this brings them to the repo's clang-format-19 canonical form and inserts
the separating blank lines.

Headers only, no semantic change: out-of-tree build is clean and make check
passes (21 pass, 7 network skip, 0 fail). htsconfig.h is UTF-8 and its
French comments survive byte-for-byte (clang-format only reflowed them to 80
columns). The new option also governs future touched-line formatting of the
engine sources.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-06-20 12:52:19 +02:00
Xavier Roche
62c2364b59 Merge pull request #402 from xroche/chore/lint-all-shell-scripts
Lint every shell script with shfmt and shellcheck
2026-06-20 12:42:19 +02:00
16 changed files with 780 additions and 640 deletions

View File

@@ -16,6 +16,7 @@ BasedOnStyle: LLVM
SpaceAfterCStyleCast: true # "(int) x", overwhelmingly dominant (542 vs 7)
SortIncludes: false # C include order can be significant; never reorder
IncludeBlocks: Preserve # do not merge/reflow include groups
SeparateDefinitionBlocks: Always # blank line between definitions (readability)
# Stated explicitly for robustness against base-style drift (these match LLVM):
IndentWidth: 2

7
debian/changelog vendored
View File

@@ -1,6 +1,9 @@
httrack (3.49.8-1) unstable; urgency=medium
* New upstream release.
* New upstream release: HTTPS-proxy CONNECT tunnelling and wider srcset
parsing, a batch of crawler and parser fixes (CSS @import, xmlns
namespaces, relative paths, RFC 6265 cookies), and security hardening of
the parser and of buffer copies throughout the engine.
* Drop the OpenSSL linking exception from the license: OpenSSL 3.0+ is
Apache-2.0 and GPL-compatible, so it is no longer needed. httrack is now
plain GPL-3.0-or-later. Updated debian/copyright accordingly.
@@ -14,7 +17,7 @@ httrack (3.49.8-1) unstable; urgency=medium
the QA debcheck page. Depend on firefox-esr | chromium | www-browser
instead.
-- Xavier Roche <xavier@debian.org> Sun, 07 Jun 2026 14:29:24 +0200
-- Xavier Roche <xavier@debian.org> Sat, 20 Jun 2026 13:02:08 +0200
httrack (3.49.7-2) unstable; urgency=medium

View File

@@ -5,12 +5,31 @@ HTTrack Website Copier release history:
This file lists all changes and fixes that have been made for HTTrack
3.49-8
+ New: tunnel HTTPS downloads through the configured HTTP proxy via CONNECT (#85)
+ New: parse every candidate URL in <img> and <source> srcset lists (#326)
+ Changed: dropped the obsolete OpenSSL linking exception (OpenSSL 3.0+ is Apache-2.0 and GPL-compatible); httrack is now plain GPLv3-or-later
+ Fixed: link libhtsjava and the libtest examples directly against libc
+ Fixed: several out-of-bounds reads in the HTML/CSS parser on hostile input (#94, #396)
+ Fixed: stored XSS via an unescaped URL in the generated page footer (#165)
+ Fixed: hardened buffer copies throughout the engine against overflow
+ Fixed: capture conditional CSS @import URLs (#94)
+ Fixed: don't crawl xmlns namespace declarations as links (#191)
+ Fixed: don't mistake the method argument of XMLHttpRequest.open for a URL (#218)
+ Fixed: percent-encode parentheses when rewriting CSS url() targets (#163)
+ Fixed: collapse ../ in file:// URLs and widen relative-link handling (#137, #162)
+ Fixed: drop the obsolete $Version/$Path attributes from the request Cookie header, per RFC 6265 (#151)
+ Fixed: keep empty quoted arguments when reloading doit.log for --update/--continue (#106)
+ Fixed: raise the User-Agent and custom-header length limits (#152)
+ Fixed: abort on a long log path (lock-file buffer too small) (#183)
+ Fixed: race in lazy mutex initialization (#297)
+ Fixed: sub-second mtime precision when comparing local files on POSIX (#383)
+ Fixed: modernize OpenSSL TLS initialization for the 3.x to 4.x transition (#308)
+ Fixed: in-place changes made by the postprocess callback were not applied (Roman Sęk)
+ Fixed: "preffered" typo in the help text and man page (yosinn1-blip)
+ Fixed: corrections and updates of the Russian translation (German Aizek)
+ Fixed: corrections and updates of the Danish translation (scootergrisen)
+ Fixed: link libhtsjava and the libtest examples directly against libc
+ New: documented the public library API headers and typed the option fields as named enums
+ Fixed: numerous build, packaging, CI and test-coverage improvements (out-of-tree builds, sanitizer/distcheck CI, shell and Python linting, AppStream metainfo)
3.49-7
+ Fixed: keep generated config.h architecture-independent (Debian #1133728)

View File

@@ -48,112 +48,115 @@ Please visit our Website: http://www.httrack.com
/* Abort (with the failed byte count) when a growth allocation fails. The
array macros never return an out-of-memory error; they assert and abort. */
static void hts_record_assert_memory_failed(const size_t size) {
fprintf(stderr, "memory allocation failed (%lu bytes)", \
(long int) size); \
assertf(! "memory allocation failed"); \
fprintf(stderr, "memory allocation failed (%lu bytes)", (long int) size);
assertf(!"memory allocation failed");
}
/** Dynamic array of T elements. **/
#define TypedArray(T) \
struct { \
/** Elements. **/ \
union { \
/** Typed. **/ \
T* elts; \
/** Opaque. **/ \
void* ptr; \
} data; \
/** Count. **/ \
size_t size; \
/** Capacity. **/ \
size_t capa; \
#define TypedArray(T) \
struct { \
/** Elements. **/ \
union { \
/** Typed. **/ \
T *elts; \
/** Opaque. **/ \
void *ptr; \
} data; \
/** Count. **/ \
size_t size; \
/** Capacity. **/ \
size_t capa; \
}
/** Initializer for an empty array (no backing store, size and capacity 0). **/
#define EMPTY_TYPED_ARRAY { { NULL }, 0, 0 }
#define EMPTY_TYPED_ARRAY {{NULL}, 0, 0}
/** Array size, in elements. **/
#define TypedArraySize(A) ((A).size)
#define TypedArraySize(A) ((A).size)
/** Array capacity, in elements. **/
#define TypedArrayCapa(A) ((A).capa)
#define TypedArrayCapa(A) ((A).capa)
/**
* Remaining free space, in elements.
* Remaining free space, in elements.
* Macro, first element evaluated multiple times.
**/
#define TypedArrayRoom(A) ( TypedArrayCapa(A) - TypedArraySize(A) )
#define TypedArrayRoom(A) (TypedArrayCapa(A) - TypedArraySize(A))
/** Array elements, of type T*. **/
#define TypedArrayElts(A) ((A).data.elts)
#define TypedArrayElts(A) ((A).data.elts)
/** Array pointer, of type void*. **/
#define TypedArrayPtr(A) ((A).data.ptr)
#define TypedArrayPtr(A) ((A).data.ptr)
/** Size of T. **/
#define TypedArrayWidth(A) (sizeof(*TypedArrayElts(A)))
#define TypedArrayWidth(A) (sizeof(*TypedArrayElts(A)))
/** Nth element of the array, as an lvalue. No bounds check; N must be
< TypedArraySize(A). **/
#define TypedArrayNth(A, N) (TypedArrayElts(A)[N])
/**
* Tail of the array (outside the array).
* Tail of the array (outside the array).
* The returned pointer points to the beginning of TypedArrayRoom(A)
* free elements.
**/
#define TypedArrayTail(A) (TypedArrayNth(A, TypedArraySize(A)))
/**
* Ensure at least 'ROOM' elements can be put in the remaining space.
* After a call to this macro, TypedArrayRoom(A) is guaranteed to be at
* least equal to 'ROOM'.
**/
#define TypedArrayEnsureRoom(A, ROOM) do { \
const size_t room_ = (ROOM); \
while (TypedArrayRoom(A) < room_) { \
TypedArrayCapa(A) = TypedArrayCapa(A) < 16 ? 16 : TypedArrayCapa(A) * 2; \
} \
TypedArrayPtr(A) = realloc(TypedArrayPtr(A), \
TypedArrayCapa(A)*TypedArrayWidth(A)); \
if (TypedArrayPtr(A) == NULL) { \
hts_record_assert_memory_failed(TypedArrayCapa(A)*TypedArrayWidth(A)); \
} \
} while(0)
* Ensure at least 'ROOM' elements can be put in the remaining space.
* After a call to this macro, TypedArrayRoom(A) is guaranteed to be at
* least equal to 'ROOM'.
**/
#define TypedArrayEnsureRoom(A, ROOM) \
do { \
const size_t room_ = (ROOM); \
while (TypedArrayRoom(A) < room_) { \
TypedArrayCapa(A) = TypedArrayCapa(A) < 16 ? 16 : TypedArrayCapa(A) * 2; \
} \
TypedArrayPtr(A) = \
realloc(TypedArrayPtr(A), TypedArrayCapa(A) * TypedArrayWidth(A)); \
if (TypedArrayPtr(A) == NULL) { \
hts_record_assert_memory_failed(TypedArrayCapa(A) * TypedArrayWidth(A)); \
} \
} while (0)
/** Add an element. Macro, first element evaluated multiple times. **/
#define TypedArrayAdd(A, E) do { \
TypedArrayEnsureRoom(A, 1); \
assertf(TypedArraySize(A) < TypedArrayCapa(A)); \
TypedArrayTail(A) = (E); \
TypedArraySize(A)++; \
} while(0)
#define TypedArrayAdd(A, E) \
do { \
TypedArrayEnsureRoom(A, 1); \
assertf(TypedArraySize(A) < TypedArrayCapa(A)); \
TypedArrayTail(A) = (E); \
TypedArraySize(A)++; \
} while (0)
/**
* Add 'COUNT' elements from 'PTR'.
* Add 'COUNT' elements from 'PTR'.
* Macro, first element evaluated multiple times.
**/
#define TypedArrayAppend(A, PTR, COUNT) do { \
const size_t count_ = (COUNT); \
/* This 1-case is to benefit from type safety. */ \
if (count_ == 1) { \
TypedArrayAdd(A, *(PTR)); \
} else { \
const void *const source_ = (PTR); \
TypedArrayEnsureRoom(A, count_); \
assertf(count_ <= TypedArrayRoom(A)); \
memcpy(&TypedArrayTail(A), source_, count_ * TypedArrayWidth(A)); \
TypedArraySize(A) += count_; \
} \
} while(0)
#define TypedArrayAppend(A, PTR, COUNT) \
do { \
const size_t count_ = (COUNT); \
/* This 1-case is to benefit from type safety. */ \
if (count_ == 1) { \
TypedArrayAdd(A, *(PTR)); \
} else { \
const void *const source_ = (PTR); \
TypedArrayEnsureRoom(A, count_); \
assertf(count_ <= TypedArrayRoom(A)); \
memcpy(&TypedArrayTail(A), source_, count_ *TypedArrayWidth(A)); \
TypedArraySize(A) += count_; \
} \
} while (0)
/** Clear an array, freeing memory and clearing size and capacity. **/
#define TypedArrayFree(A) do { \
if (TypedArrayPtr(A) != NULL) { \
TypedArrayCapa(A) = TypedArraySize(A) = 0; \
free(TypedArrayPtr(A)); \
TypedArrayPtr(A) = NULL; \
} \
} while(0)
#define TypedArrayFree(A) \
do { \
if (TypedArrayPtr(A) != NULL) { \
TypedArrayCapa(A) = TypedArraySize(A) = 0; \
free(TypedArrayPtr(A)); \
TypedArrayPtr(A) = NULL; \
} \
} while (0)
#endif

View File

@@ -41,7 +41,7 @@ Please visit our Website: http://www.httrack.com
#ifdef _WIN32
#if HTS_INET6==0
#if HTS_INET6 == 0
#include <winsock2.h>
#else
@@ -49,13 +49,14 @@ Please visit our Website: http://www.httrack.com
#define WIN32_LEAN_AND_MEAN
// KB955045 (http://support.microsoft.com/kb/955045)
// To execute an application using this function on earlier versions of Windows
// (Windows 2000, Windows NT, and Windows Me/98/95), then it is mandatary to #include Ws2tcpip.h
// and also Wspiapi.h. When the Wspiapi.h header file is included, the 'getaddrinfo' function is
// #defined to the 'WspiapiGetAddrInfo' inline function in Wspiapi.h.
// (Windows 2000, Windows NT, and Windows Me/98/95), then it is mandatary to
// #include Ws2tcpip.h and also Wspiapi.h. When the Wspiapi.h header file is
// included, the 'getaddrinfo' function is #defined to the 'WspiapiGetAddrInfo'
// inline function in Wspiapi.h.
#include <ws2tcpip.h>
#include <Wspiapi.h>
//#include <winsock2.h>
//#include <tpipv6.h>
// #include <winsock2.h>
// #include <tpipv6.h>
#endif

View File

@@ -68,14 +68,15 @@ struct t_cookie {
#ifdef HTS_INTERNAL_BYTECODE
/* cookies */
int cookie_add(t_cookie * cookie, const char *cook_name, const char *cook_value,
const char *domain, const char *path);
int cookie_add(t_cookie *cookie, const char *cook_name, const char *cook_value,
const char *domain, const char *path);
int cookie_del(t_cookie * cookie, const char *cook_name, const char *domain, const char *path);
int cookie_del(t_cookie *cookie, const char *cook_name, const char *domain,
const char *path);
int cookie_load(t_cookie * cookie, const char *path, const char *name);
int cookie_load(t_cookie *cookie, const char *path, const char *name);
int cookie_save(t_cookie * cookie, const char *name);
int cookie_save(t_cookie *cookie, const char *name);
void cookie_insert(char *s, size_t s_size, const char *ins);
@@ -83,7 +84,8 @@ void cookie_delete(char *s, size_t s_size, size_t pos);
const char *cookie_get(char *buffer, const char *cookie_base, int param);
char *cookie_find(char *s, const char *cook_name, const char *domain, const char *path);
char *cookie_find(char *s, const char *cook_name, const char *domain,
const char *path);
char *cookie_nextfield(char *a);
@@ -92,12 +94,13 @@ char *cookie_nextfield(char *a);
/** Register credentials (auth = base-64 user:pass) for the prefix derived from
adr (host) and fil (path). No-op returning 0 if cookie is NULL, allocation
fails, or a matching prefix is already stored; returns 1 on insertion. */
int bauth_add(t_cookie * cookie, const char *adr, const char *fil, const char *auth);
int bauth_add(t_cookie *cookie, const char *adr, const char *fil,
const char *auth);
/** Return the stored base-64 credentials whose prefix matches adr+fil, or NULL
if none (or cookie is NULL). Returned pointer aliases the jar's bauth_chain;
caller must not free it. */
char *bauth_check(t_cookie * cookie, const char *adr, const char *fil);
char *bauth_check(t_cookie *cookie, const char *adr, const char *fil);
/** Build the auth lookup key (host + path, query string stripped, truncated at
the last '/') from adr and fil into prefix; returns prefix. Caller must

View File

@@ -52,12 +52,12 @@ Please visit our Website: http://www.httrack.com
#define DEFAULT_FTP "index.txt"
// extension par défaut pour fichiers n'en ayant pas
#define DEFAULT_EXT ".html"
#define DEFAULT_EXT ".html"
#define DEFAULT_EXT_SHORT ".htm"
//#define DEFAULT_BIN_EXT ".bin"
//#define DEFAULT_BIN_EXT_SHORT ".bin"
//#define DEFAULT_EXT ".txt"
//#define DEFAULT_EXT_SHORT ".txt"
// #define DEFAULT_BIN_EXT ".bin"
// #define DEFAULT_BIN_EXT_SHORT ".bin"
// #define DEFAULT_EXT ".txt"
// #define DEFAULT_EXT_SHORT ".txt"
// éviter les /nul, /con..
#define HTS_OVERRIDE_DOS_FOLDERS 1
@@ -87,7 +87,8 @@ Please visit our Website: http://www.httrack.com
// fast cache (build hash table)
#define HTS_FAST_CACHE 1
// le > peut être considéré comme un tag de fermeture de commentaire (<!-- > est valide)
// le > peut être considéré comme un tag de fermeture de commentaire (<!-- > est
// valide)
#define GT_ENDS_COMMENT 1
// always adds a '/' at the end if a '~' is encountered (/~smith -> /~smith/)
@@ -97,7 +98,8 @@ Please visit our Website: http://www.httrack.com
#define HTS_STRIP_DOUBLE_SLASH 0
// case-sensitive pour les dossiers et fichiers (0/1)
// [normalement 1, mais pose des problèmes (url malformée par exemple) et n'est pas très utile..
// [normalement 1, mais pose des problèmes (url malformée par exemple) et n'est
// pas très utile..
// ..et pas bcp respecté]
// REMOVED
// #define HTS_CASSE 0

View File

@@ -82,9 +82,9 @@ typedef struct t_hts_callbackarg t_hts_callbackarg;
/* Entry points of a --wrapper plug-in: hts_plug(opt, argv) is called once to
install the wrapper (argv is the wrapper's argument string), hts_unplug(opt)
once to tear it down. Both return non-zero on success. */
typedef int (*t_hts_plug) (httrackp * opt, const char *argv);
typedef int (*t_hts_plug)(httrackp *opt, const char *argv);
typedef int (*t_hts_unplug) (httrackp * opt);
typedef int (*t_hts_unplug)(httrackp *opt);
/* Engine callback prototypes. Each is one hook the engine fires at a defined
point of a mirror; a wrapper installs the ones it cares about in the
@@ -92,27 +92,27 @@ typedef int (*t_hts_unplug) (httrackp * opt);
returns are 1 to continue/accept, 0 to abort/refuse unless noted. */
/* Called once when the wrapper is installed; allocate per-run state here. */
typedef void (*t_hts_htmlcheck_init) (t_hts_callbackarg * carg);
typedef void (*t_hts_htmlcheck_init)(t_hts_callbackarg *carg);
/* Called once when the wrapper is removed; release per-run state here. */
typedef void (*t_hts_htmlcheck_uninit) (t_hts_callbackarg * carg);
typedef void (*t_hts_htmlcheck_uninit)(t_hts_callbackarg *carg);
/* Fired at the start of a mirror, after options are parsed. */
typedef int (*t_hts_htmlcheck_start) (t_hts_callbackarg * carg, httrackp * opt);
typedef int (*t_hts_htmlcheck_start)(t_hts_callbackarg *carg, httrackp *opt);
/* Fired at the end of a mirror. */
typedef int (*t_hts_htmlcheck_end) (t_hts_callbackarg * carg, httrackp * opt);
typedef int (*t_hts_htmlcheck_end)(t_hts_callbackarg *carg, httrackp *opt);
/* Fired while options are being changed, to validate or adjust them. */
typedef int (*t_hts_htmlcheck_chopt) (t_hts_callbackarg * carg, httrackp * opt);
typedef int (*t_hts_htmlcheck_chopt)(t_hts_callbackarg *carg, httrackp *opt);
/* Rewrite hook over an in-memory page: the html and len arguments point at the
buffer and its length (the callback may reallocate and resize it),
url_adresse and url_fichier name it. */
typedef int (*t_hts_htmlcheck_process) (t_hts_callbackarg * carg,
httrackp * opt, char **html, int *len,
const char *url_adresse,
const char *url_fichier);
typedef int (*t_hts_htmlcheck_process)(t_hts_callbackarg *carg, httrackp *opt,
char **html, int *len,
const char *url_adresse,
const char *url_fichier);
/* Same shape as process, run before HTML parsing. */
typedef t_hts_htmlcheck_process t_hts_htmlcheck_preprocess;
@@ -121,113 +121,111 @@ typedef t_hts_htmlcheck_process t_hts_htmlcheck_preprocess;
typedef t_hts_htmlcheck_process t_hts_htmlcheck_postprocess;
/* Inspect a page (read-only html/len) without rewriting it. */
typedef int (*t_hts_htmlcheck_check_html) (t_hts_callbackarg * carg,
httrackp * opt, char *html, int len,
const char *url_adresse,
const char *url_fichier);
typedef int (*t_hts_htmlcheck_check_html)(t_hts_callbackarg *carg,
httrackp *opt, char *html, int len,
const char *url_adresse,
const char *url_fichier);
/* Answer an engine query identified by 'question'; returns the answer string
(owned by the callback, must stay valid until the next call). */
typedef const char *(*t_hts_htmlcheck_query) (t_hts_callbackarg * carg,
httrackp * opt,
const char *question);
typedef const char *(*t_hts_htmlcheck_query)(t_hts_callbackarg *carg,
httrackp *opt,
const char *question);
/* Second query channel, same contract as query. */
typedef const char *(*t_hts_htmlcheck_query2) (t_hts_callbackarg * carg,
httrackp * opt,
const char *question);
typedef const char *(*t_hts_htmlcheck_query2)(t_hts_callbackarg *carg,
httrackp *opt,
const char *question);
/* Third query channel, same contract as query. */
typedef const char *(*t_hts_htmlcheck_query3) (t_hts_callbackarg * carg,
httrackp * opt,
const char *question);
typedef const char *(*t_hts_htmlcheck_query3)(t_hts_callbackarg *carg,
httrackp *opt,
const char *question);
/* Per-tick progress hook: 'back' is the transfer slot array of 'back_max'
entries, back_index the active one; lien_tot/lien_ntot and stats report
queue size and running totals, stat_time the elapsed time. */
typedef int (*t_hts_htmlcheck_loop) (t_hts_callbackarg * carg, httrackp * opt,
lien_back * back, int back_max,
int back_index, int lien_tot,
int lien_ntot, int stat_time,
hts_stat_struct * stats);
typedef int (*t_hts_htmlcheck_loop)(t_hts_callbackarg *carg, httrackp *opt,
lien_back *back, int back_max,
int back_index, int lien_tot, int lien_ntot,
int stat_time, hts_stat_struct *stats);
/* Veto a link (adr host, fil path) after its transfer; status is the result.
Return 0 to drop the link. */
typedef int (*t_hts_htmlcheck_check_link) (t_hts_callbackarg * carg,
httrackp * opt, const char *adr,
const char *fil, int status);
typedef int (*t_hts_htmlcheck_check_link)(t_hts_callbackarg *carg,
httrackp *opt, const char *adr,
const char *fil, int status);
/* Veto a link by its MIME type before download; return 0 to skip it. */
typedef int (*t_hts_htmlcheck_check_mime) (t_hts_callbackarg * carg,
httrackp * opt, const char *adr,
const char *fil, const char *mime,
int status);
typedef int (*t_hts_htmlcheck_check_mime)(t_hts_callbackarg *carg,
httrackp *opt, const char *adr,
const char *fil, const char *mime,
int status);
/* Fired when the mirror pauses, waiting on 'lockfile' to be removed. */
typedef void (*t_hts_htmlcheck_pause) (t_hts_callbackarg * carg, httrackp * opt,
const char *lockfile);
typedef void (*t_hts_htmlcheck_pause)(t_hts_callbackarg *carg, httrackp *opt,
const char *lockfile);
/* Fired after a file is written to disk; 'file' is the local path. */
typedef void (*t_hts_htmlcheck_filesave) (t_hts_callbackarg * carg,
httrackp * opt, const char *file);
typedef void (*t_hts_htmlcheck_filesave)(t_hts_callbackarg *carg, httrackp *opt,
const char *file);
/* Richer file-saved notification: source host/filename, local path, and flags
telling whether the file is new, modified, or left unchanged. */
typedef void (*t_hts_htmlcheck_filesave2) (t_hts_callbackarg * carg,
httrackp * opt, const char *hostname,
const char *filename,
const char *localfile, int is_new,
int is_modified, int not_updated);
typedef void (*t_hts_htmlcheck_filesave2)(t_hts_callbackarg *carg,
httrackp *opt, const char *hostname,
const char *filename,
const char *localfile, int is_new,
int is_modified, int not_updated);
/* Fired for each link parsed out of a page; 'link' may be edited in place. */
typedef int (*t_hts_htmlcheck_linkdetected) (t_hts_callbackarg * carg,
httrackp * opt, char *link);
typedef int (*t_hts_htmlcheck_linkdetected)(t_hts_callbackarg *carg,
httrackp *opt, char *link);
/* As linkdetected, plus tag_start, the markup the link was found in. */
typedef int (*t_hts_htmlcheck_linkdetected2) (t_hts_callbackarg * carg,
httrackp * opt, char *link,
const char *tag_start);
typedef int (*t_hts_htmlcheck_linkdetected2)(t_hts_callbackarg *carg,
httrackp *opt, char *link,
const char *tag_start);
/* Fired on each transfer-status change of slot 'back'. */
typedef int (*t_hts_htmlcheck_xfrstatus) (t_hts_callbackarg * carg,
httrackp * opt, lien_back * back);
typedef int (*t_hts_htmlcheck_xfrstatus)(t_hts_callbackarg *carg, httrackp *opt,
lien_back *back);
/* Choose the local save path for a URL; write it into 'save'. adr/fil name the
target, referer_adr/referer_fil the page that linked it. */
typedef int (*t_hts_htmlcheck_savename) (t_hts_callbackarg * carg,
httrackp * opt,
const char *adr_complete,
const char *fil_complete,
const char *referer_adr,
const char *referer_fil, char *save);
typedef int (*t_hts_htmlcheck_savename)(t_hts_callbackarg *carg, httrackp *opt,
const char *adr_complete,
const char *fil_complete,
const char *referer_adr,
const char *referer_fil, char *save);
/* Extended save-name hook, same signature as savename. */
typedef t_hts_htmlcheck_savename t_hts_htmlcheck_extsavename;
/* Inspect or edit the outgoing request headers in 'buff' before they are sent.
*/
typedef int (*t_hts_htmlcheck_sendhead) (t_hts_callbackarg * carg,
httrackp * opt, char *buff,
const char *adr, const char *fil,
const char *referer_adr,
const char *referer_fil,
htsblk * outgoing);
typedef int (*t_hts_htmlcheck_sendhead)(t_hts_callbackarg *carg, httrackp *opt,
char *buff, const char *adr,
const char *fil,
const char *referer_adr,
const char *referer_fil,
htsblk *outgoing);
/* Inspect the incoming response headers in 'buff' after they are received. */
typedef int (*t_hts_htmlcheck_receivehead) (t_hts_callbackarg * carg,
httrackp * opt, char *buff,
const char *adr, const char *fil,
const char *referer_adr,
const char *referer_fil,
htsblk * incoming);
typedef int (*t_hts_htmlcheck_receivehead)(t_hts_callbackarg *carg,
httrackp *opt, char *buff,
const char *adr, const char *fil,
const char *referer_adr,
const char *referer_fil,
htsblk *incoming);
/* External parser module hooks: detect claims a document type (return 1 to
take it), parse then extracts its links. 'str' carries the document. */
typedef int (*t_hts_htmlcheck_detect) (t_hts_callbackarg * carg, httrackp * opt,
htsmoduleStruct * str);
typedef int (*t_hts_htmlcheck_detect)(t_hts_callbackarg *carg, httrackp *opt,
htsmoduleStruct *str);
typedef int (*t_hts_htmlcheck_parse) (t_hts_callbackarg * carg, httrackp * opt,
htsmoduleStruct * str);
typedef int (*t_hts_htmlcheck_parse)(t_hts_callbackarg *carg, httrackp *opt,
htsmoduleStruct *str);
/* Callbacks */
#ifndef HTS_DEF_FWSTRUCT_t_hts_htmlcheck_callbacks
@@ -237,10 +235,10 @@ typedef struct t_hts_htmlcheck_callbacks t_hts_htmlcheck_callbacks;
/* Declares one named callback slot: its function pointer (typed
t_hts_htmlcheck_<NAME>) paired with the carg passed to it. */
#define DEFCALLBACK(NAME) \
struct NAME { \
t_hts_htmlcheck_ ##NAME fun; \
t_hts_callbackarg *carg; \
#define DEFCALLBACK(NAME) \
struct NAME { \
t_hts_htmlcheck_##NAME fun; \
t_hts_callbackarg *carg; \
} NAME
/* Generic, type-erased callback slot used where the hook type is opaque. */
@@ -324,18 +322,18 @@ extern const t_hts_htmlcheck_callbacks default_callbacks;
/* Internal helpers for building an HTTP request/response into the engine's
scratch buffer (opt->state.HTbuff): START resets it, PRINT appends; the
PANIC variant records a fatal error message. */
#define HT_PRINT(A) strcatbuff(opt->state.HTbuff,A);
#define HT_PRINT(A) strcatbuff(opt->state.HTbuff, A);
#define HT_REQUEST_START opt->state.HTbuff[0]='\0';
#define HT_REQUEST_START opt->state.HTbuff[0] = '\0';
#define HT_REQUEST_END
#define HTT_REQUEST_START opt->state.HTbuff[0]='\0';
#define HTT_REQUEST_START opt->state.HTbuff[0] = '\0';
#define HTT_REQUEST_END
#define HTS_REQUEST_START opt->state.HTbuff[0]='\0';
#define HTS_REQUEST_START opt->state.HTbuff[0] = '\0';
#define HTS_REQUEST_END
#define HTS_PANIC_PRINTF(S) strcpybuff(opt->state._hts_errmsg,S);
#define HTS_PANIC_PRINTF(S) strcpybuff(opt->state._hts_errmsg, S);
#endif

View File

@@ -43,10 +43,10 @@ Please visit our Website: http://www.httrack.com
configure.ac, decoupled from these). VERSION is the display form, VERSIONID
the dotted numeric form, AFF_VERSION the short form shown in footers,
LIB_VERSION the data/cache format generation. */
#define HTTRACK_VERSION "3.49-8"
#define HTTRACK_VERSIONID "3.49.8"
#define HTTRACK_AFF_VERSION "3.x"
#define HTTRACK_LIB_VERSION "2.0"
#define HTTRACK_VERSION "3.49-8"
#define HTTRACK_VERSIONID "3.49.8"
#define HTTRACK_AFF_VERSION "3.x"
#define HTTRACK_LIB_VERSION "2.0"
#ifndef HTS_NOINCLUDES
#include <stdio.h>
@@ -71,11 +71,11 @@ Please visit our Website: http://www.httrack.com
varargs starting at arg. */
#ifndef HTS_UNUSED
#ifdef __GNUC__
#define HTS_UNUSED __attribute__ ((unused))
#define HTS_UNUSED __attribute__((unused))
#define HTS_STATIC static __attribute__ ((unused))
#define HTS_STATIC static __attribute__((unused))
#define HTS_PRINTF_FUN(fmt, arg) __attribute__ ((format (printf, fmt, arg)))
#define HTS_PRINTF_FUN(fmt, arg) __attribute__((format(printf, fmt, arg)))
#else
#define HTS_UNUSED
#define HTS_STATIC static
@@ -113,7 +113,7 @@ Please visit our Website: http://www.httrack.com
#ifndef HTS_LONGLONG
#ifdef SIZEOF_LONG_LONG
#if SIZEOF_LONG_LONG==8
#if SIZEOF_LONG_LONG == 8
#define HTS_LONGLONG 1
#endif
#endif
@@ -204,12 +204,12 @@ Please visit our Website: http://www.httrack.com
#endif
#define HTS_HTTRACKRC ".httrackrc"
#define HTS_HTTRACKCNF HTS_ETCPATH"/httrack.conf"
#define HTS_HTTRACKCNF HTS_ETCPATH "/httrack.conf"
#ifdef DATADIR
#define HTS_HTTRACKDIR DATADIR"/httrack/"
#define HTS_HTTRACKDIR DATADIR "/httrack/"
#else
#define HTS_HTTRACKDIR HTS_PREFIX"/share/httrack/"
#define HTS_HTTRACKDIR HTS_PREFIX "/share/httrack/"
#endif
#endif
@@ -226,12 +226,17 @@ Please visit our Website: http://www.httrack.com
/* Copyright (C) 1998 Xavier Roche and other contributors */
#define HTTRACK_AFF_AUTHORS "[XR&CO'2014]"
#define HTS_DEFAULT_FOOTER "<!-- Mirrored from %s%s by HTTrack Website Copier/" HTTRACK_AFF_VERSION " " HTTRACK_AFF_AUTHORS ", %s -->"
#define HTS_DEFAULT_FOOTER \
"<!-- Mirrored from %s%s by HTTrack Website Copier/" HTTRACK_AFF_VERSION \
" " HTTRACK_AFF_AUTHORS ", %s -->"
#define HTTRACK_WEB "http://www.httrack.com"
#define HTS_UPDATE_WEBSITE "http://www.httrack.com/update.php3?Product=HTTrack&Version=" HTTRACK_VERSIONID "&VersionStr=" HTTRACK_VERSION "&Platform=%d&Language=%s"
#define HTS_UPDATE_WEBSITE \
"http://www.httrack.com/" \
"update.php3?Product=HTTrack&Version=" HTTRACK_VERSIONID \
"&VersionStr=" HTTRACK_VERSION "&Platform=%d&Language=%s"
#define H_CRLF "\x0d\x0a"
#define CRLF "\x0d\x0a"
#define CRLF "\x0d\x0a"
#ifdef _WIN32
#define LF "\x0d\x0a"
#else
@@ -247,13 +252,14 @@ Please visit our Website: http://www.httrack.com
return type stays compatible with the int it replaces. */
#ifndef HTS_DEF_DEFSTRUCT_hts_boolean
#define HTS_DEF_DEFSTRUCT_hts_boolean
typedef enum hts_boolean { HTS_FALSE = 0, HTS_TRUE = 1 } hts_boolean;
#endif
/* Larger/smaller of two values. Macros: arguments are evaluated twice. */
#define maximum(A,B) ( (A) > (B) ? (A) : (B) )
#define maximum(A, B) ((A) > (B) ? (A) : (B))
#define minimum(A,B) ( (A) < (B) ? (A) : (B) )
#define minimum(A, B) ((A) < (B) ? (A) : (B))
/* True when A is a non-NULL, non-empty string. */
#define strnotempty(A) (((A) != NULL && (A)[0] != '\0'))
@@ -278,10 +284,10 @@ typedef enum hts_boolean { HTS_FALSE = 0, HTS_TRUE = 1 } hts_boolean;
#endif
#else
/* See <http://gcc.gnu.org/wiki/Visibility> */
#if ( ( defined(__GNUC__) && ( __GNUC__ >= 4 ) ) \
|| ( defined(HAVE_VISIBILITY) && HAVE_VISIBILITY ) )
#if ((defined(__GNUC__) && (__GNUC__ >= 4)) || \
(defined(HAVE_VISIBILITY) && HAVE_VISIBILITY))
#define HTSEXT_API __attribute__ ((visibility ("default")))
#define HTSEXT_API __attribute__((visibility("default")))
#else
#define HTSEXT_API
#endif
@@ -335,8 +341,8 @@ typedef __int64 LLint;
typedef __int64 TStamp;
#define LLintP "%I64d"
#elif (defined(_LP64) || defined(__x86_64__) \
|| defined(__powerpc64__) || defined(__64BIT__))
#elif (defined(_LP64) || defined(__x86_64__) || defined(__powerpc64__) || \
defined(__64BIT__))
typedef long int LLint;
@@ -400,16 +406,17 @@ typedef int T_SOC;
/* Permission bits for created folders and files (mkdir and chmod).
PROTECT_FOLDER is owner-only. With HTS_ACCESS set (the default) the ACCESS_
modes also grant group/other read; otherwise they stay owner-only. */
#define HTS_PROTECT_FOLDER (S_IRUSR|S_IWUSR|S_IXUSR)
#define HTS_PROTECT_FOLDER (S_IRUSR | S_IWUSR | S_IXUSR)
#if HTS_ACCESS
#define HTS_ACCESS_FILE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
#define HTS_ACCESS_FILE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define HTS_ACCESS_FOLDER (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
#define HTS_ACCESS_FOLDER \
(S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
#else
#define HTS_ACCESS_FILE (S_IRUSR|S_IWUSR)
#define HTS_ACCESS_FILE (S_IRUSR | S_IWUSR)
#define HTS_ACCESS_FOLDER (S_IRUSR|S_IWUSR|S_IXUSR)
#define HTS_ACCESS_FOLDER (S_IRUSR | S_IWUSR | S_IXUSR)
#endif
/* Sanity-check that the required preprocessor switches are defined */
@@ -427,7 +434,11 @@ typedef int T_SOC;
#endif
/* fflush sur stdout */
#define io_flush { fflush(stdout); fflush(stdin); }
#define io_flush \
{ \
fflush(stdout); \
fflush(stdin); \
}
/* HTSLib */
@@ -447,7 +458,7 @@ typedef int T_SOC;
#ifdef _DEBUG
// trace mallocs
//#define HTS_TRACE_MALLOC
// #define HTS_TRACE_MALLOC
#ifdef HTS_TRACE_MALLOC
typedef unsigned long int t_htsboundary;
@@ -524,7 +535,13 @@ static const t_htsboundary htsboundary = 0xDEADBEEF;
#if _HTS_WIDE
extern FILE *DEBUG_fp;
#define DEBUG_W(A) { if (DEBUG_fp==NULL) DEBUG_fp=fopen("bug.out","wb"); fprintf(DEBUG_fp,":>"A); fflush(DEBUG_fp); }
#define DEBUG_W(A) \
{ \
if (DEBUG_fp == NULL) \
DEBUG_fp = fopen("bug.out", "wb"); \
fprintf(DEBUG_fp, ":>" A); \
fflush(DEBUG_fp); \
}
#undef _
#define _ ,
#endif

View File

@@ -69,41 +69,41 @@ typedef struct hash_struct hash_struct;
#define HTS_DEF_FWSTRUCT_htsmoduleStruct
typedef struct htsmoduleStruct htsmoduleStruct;
#endif
typedef int (*t_htsAddLink) (htsmoduleStruct * str, char *link);
typedef int (*t_htsAddLink)(htsmoduleStruct *str, char *link);
/** Per-object context passed to a parser module for one downloaded file.
Field access classes are noted; engine owns all pointers unless stated. */
struct htsmoduleStruct {
/* Read-only elements */
const char *filename; /* filename (C:\My Web Sites\...) */
int size; /* size of filename (should be > 0) */
const char *mime; /* MIME type of the object */
const char *url_host; /* incoming hostname (www.foo.com) */
const char *url_file; /* incoming filename (/bar/bar.gny) */
const char *filename; /* filename (C:\My Web Sites\...) */
int size; /* size of filename (should be > 0) */
const char *mime; /* MIME type of the object */
const char *url_host; /* incoming hostname (www.foo.com) */
const char *url_file; /* incoming filename (/bar/bar.gny) */
/* Write-only */
const char *wrapper_name; /* name of wrapper (static string) */
char *err_msg; /* if an error occurred, the error message (max. 1KB) */
const char *wrapper_name; /* name of wrapper (static string) */
char *err_msg; /* if an error occurred, the error message (max. 1KB) */
/* Read/Write */
int relativeToHtmlLink; /* set this to 1 if all urls you pass to addLink
are in fact relative to the html file where your
module was originally */
int relativeToHtmlLink; /* set this to 1 if all urls you pass to addLink
are in fact relative to the html file where your
module was originally */
/* Callbacks */
t_htsAddLink addLink; /* call this function when links are
being detected. it if not your responsability to decide
if the engine will keep them, or not. */
t_htsAddLink addLink; /* call this function when links are
being detected. it if not your responsability to
decide if the engine will keep them, or not. */
/* Optional */
char *localLink; /* if non null, the engine will write there the local
relative filename of the link added by addLink(), or
the absolute path if the link was refused by the wizard */
int localLinkSize; /* size of the optionnal buffer */
char *localLink; /* if non null, the engine will write there the local
relative filename of the link added by addLink(), or
the absolute path if the link was refused by the wizard */
int localLinkSize; /* size of the optionnal buffer */
/* User-defined */
void *userdef; /* can be used by callback routines
*/
void *userdef; /* can be used by callback routines
*/
/* The parser httrackp structure (may be used) */
httrackp *opt;
@@ -117,7 +117,6 @@ struct htsmoduleStruct {
int *ptr_;
const char *page_charset_;
/* Internal use - please don't touch */
};
#ifdef __cplusplus
@@ -126,11 +125,11 @@ extern "C" {
/** Module lifecycle hooks. Init/PlugInit return 1 on success, 0 on failure;
Exit returns its own status (ignored by the engine). */
typedef int (*t_htsWrapperInit) (char *fn, char *args);
typedef int (*t_htsWrapperInit)(char *fn, char *args);
typedef int (*t_htsWrapperExit) (void);
typedef int (*t_htsWrapperExit)(void);
typedef int (*t_htsWrapperPlugInit) (char *args);
typedef int (*t_htsWrapperPlugInit)(char *args);
/* Library internal definictions */
#ifdef HTS_INTERNAL_BYTECODE
@@ -138,7 +137,7 @@ typedef int (*t_htsWrapperPlugInit) (char *args);
/** Capabilities string ("-noV6", "-nossl", ...) followed by "+name" for each
loaded module. Returned pointer aliases opt->state.HTbuff; do not free, and
it is overwritten by the next call. */
HTSEXT_API const char *hts_get_version_info(httrackp * opt);
HTSEXT_API const char *hts_get_version_info(httrackp *opt);
/** Static capabilities string set by htspe_init(); valid for the process
lifetime, do not free. */
@@ -154,7 +153,7 @@ extern void htspe_uninit(void);
/** Run the external-parser callbacks for the object described by str.
Returns the parse callback result (>=0) on a handled object, or -1 if no
module claimed it or its wrapper_name is blacklisted. */
extern int hts_parse_externals(htsmoduleStruct * str);
extern int hts_parse_externals(htsmoduleStruct *str);
/** Nonzero if IPv6 support was compiled in (== HTS_INET6). */
extern int V6_is_available;

View File

@@ -112,10 +112,10 @@ struct SOCaddr {
/** Pointer to the port field (network byte order) for the active family.
Asserts on NULL or an unset/unknown family. */
static HTS_INLINE HTS_UNUSED in_port_t* SOCaddr_sinport_(SOCaddr *const addr,
const char *file, const int line) {
static HTS_INLINE HTS_UNUSED in_port_t *
SOCaddr_sinport_(SOCaddr *const addr, const char *file, const int line) {
assertf_(addr != NULL, file, line);
switch(addr->m_addr.sa.sa_family) {
switch (addr->m_addr.sa.sa_family) {
case AF_INET:
return &addr->m_addr.in.sin_port;
break;
@@ -125,7 +125,7 @@ static HTS_INLINE HTS_UNUSED in_port_t* SOCaddr_sinport_(SOCaddr *const addr,
break;
#endif
default:
assertf_(! "invalid structure", file, line);
assertf_(!"invalid structure", file, line);
return 0;
break;
}
@@ -133,10 +133,11 @@ static HTS_INLINE HTS_UNUSED in_port_t* SOCaddr_sinport_(SOCaddr *const addr,
/** Length of the active sockaddr (sockaddr_in or sockaddr_in6), or 0 if the
family is unset/unknown. The 0 case doubles as the "not valid" test. */
static HTS_INLINE HTS_UNUSED socklen_t SOCaddr_size_(const SOCaddr*const addr,
const char *file, const int line) {
static HTS_INLINE HTS_UNUSED socklen_t SOCaddr_size_(const SOCaddr *const addr,
const char *file,
const int line) {
assertf_(addr != NULL, file, line);
switch(addr->m_addr.sa.sa_family) {
switch (addr->m_addr.sa.sa_family) {
case AF_INET:
return sizeof(addr->m_addr.in);
break;
@@ -152,8 +153,8 @@ static HTS_INLINE HTS_UNUSED socklen_t SOCaddr_size_(const SOCaddr*const addr,
}
/** Reset to the unset state (family AF_UNSPEC), making the address invalid. */
static HTS_INLINE HTS_UNUSED void SOCaddr_clear_(SOCaddr*const addr,
const char *file, const int line) {
static HTS_INLINE HTS_UNUSED void
SOCaddr_clear_(SOCaddr *const addr, const char *file, const int line) {
assertf_(addr != NULL, file, line);
addr->m_addr.sa.sa_family = AF_UNSPEC;
}
@@ -191,14 +192,16 @@ static HTS_INLINE HTS_UNUSED void SOCaddr_clear_(SOCaddr*const addr,
/** Set the port (host-order argument, stored network-order) on the active
* family. */
#define SOCaddr_initport(server, port) do { \
SOCaddr_sinport(server) = htons((in_port_t) (port)); \
} while(0)
#define SOCaddr_initport(server, port) \
do { \
SOCaddr_sinport(server) = htons((in_port_t) (port)); \
} while (0)
/** Initialize as an all-zero IPv4 wildcard (INADDR_ANY) address; returns its
sockaddr length. */
static HTS_INLINE HTS_UNUSED socklen_t SOCaddr_initany_(SOCaddr*const addr,
const char *file, const int line) {
static HTS_INLINE HTS_UNUSED socklen_t SOCaddr_initany_(SOCaddr *const addr,
const char *file,
const int line) {
assertf_(addr != NULL, file, line);
memset(&addr->m_addr.in, 0, sizeof(addr->m_addr.in));
addr->m_addr.in.sin_family = AF_INET;
@@ -206,17 +209,20 @@ static HTS_INLINE HTS_UNUSED socklen_t SOCaddr_initany_(SOCaddr*const addr,
}
/** Initialize server as an IPv4 wildcard (INADDR_ANY) address. */
#define SOCaddr_initany(server) do { \
SOCaddr_initany_(&(server), __FILE__, __LINE__); \
} while(0)
#define SOCaddr_initany(server) \
do { \
SOCaddr_initany_(&(server), __FILE__, __LINE__); \
} while (0)
/** Populate server from data. data_size selects the source form: a full
sockaddr_in / sockaddr_in6, or a raw 4-byte (IPv4) / 16-byte (IPv6) address
with port zeroed. Any other size leaves an AF_INET shell. Returns the
resulting sockaddr length. */
static HTS_UNUSED socklen_t SOCaddr_copyaddr_(SOCaddr*const server,
const void *data, const size_t data_size,
const char *file, const int line) {
static HTS_UNUSED socklen_t SOCaddr_copyaddr_(SOCaddr *const server,
const void *data,
const size_t data_size,
const char *file,
const int line) {
assertf_(server != NULL, file, line);
assertf_(data != NULL, file, line);
@@ -248,32 +254,35 @@ static HTS_UNUSED socklen_t SOCaddr_copyaddr_(SOCaddr*const server,
/** Copy hpaddr (length hpsize) into server, writing the result length into the
lvalue server_len (int). See SOCaddr_copyaddr_ for accepted forms. */
#define SOCaddr_copyaddr(server, server_len, hpaddr, hpsize) do { \
server_len = (int) SOCaddr_copyaddr_(&(server), hpaddr, hpsize, __FILE__, __LINE__); \
} while(0)
#define SOCaddr_copyaddr(server, server_len, hpaddr, hpsize) \
do { \
server_len = (int) SOCaddr_copyaddr_(&(server), hpaddr, hpsize, __FILE__, \
__LINE__); \
} while (0)
/** Like SOCaddr_copyaddr but discards the result length. */
#define SOCaddr_copyaddr2(server, hpaddr, hpsize) do { \
(void) SOCaddr_copyaddr_(&(server), hpaddr, hpsize, __FILE__, __LINE__); \
} while(0)
#define SOCaddr_copyaddr2(server, hpaddr, hpsize) \
do { \
(void) SOCaddr_copyaddr_(&(server), hpaddr, hpsize, __FILE__, __LINE__); \
} while (0)
/** Copy one SOCaddr (src) into another (dest), preserving family and port. */
#define SOCaddr_copy_SOCaddr(dest, src) do { \
SOCaddr_copyaddr_(&(dest), &(src).m_addr.sa, SOCaddr_size(src), __FILE__, __LINE__); \
} while(0)
#define SOCaddr_copy_SOCaddr(dest, src) \
do { \
SOCaddr_copyaddr_(&(dest), &(src).m_addr.sa, SOCaddr_size(src), __FILE__, \
__LINE__); \
} while (0)
/** Write the numeric (dotted/colon) host of ss into namebuf (capacity
namebuflen), scope id stripped. On failure namebuf becomes "". */
static HTS_UNUSED void SOCaddr_inetntoa_(char *namebuf, size_t namebuflen,
SOCaddr *const ss,
const char *file, const int line) {
static HTS_UNUSED void SOCaddr_inetntoa_(char *namebuf, size_t namebuflen,
SOCaddr *const ss, const char *file,
const int line) {
assertf_(namebuf != NULL, file, line);
assertf_(ss != NULL, file, line);
if (getnameinfo(&ss->m_addr.sa, sizeof(ss->m_addr),
namebuf, namebuflen,
NULL, 0,
NI_NUMERICHOST) == 0) {
if (getnameinfo(&ss->m_addr.sa, sizeof(ss->m_addr), namebuf, namebuflen, NULL,
0, NI_NUMERICHOST) == 0) {
/* remove scope id(s) */
char *const pos = strchr(namebuf, '%');
if (pos != NULL) {
@@ -285,11 +294,12 @@ static HTS_UNUSED void SOCaddr_inetntoa_(char *namebuf, size_t namebuflen,
}
/** Numeric host of ss into namebuf (capacity namebuflen); "" on failure. */
#define SOCaddr_inetntoa(namebuf, namebuflen, ss) \
#define SOCaddr_inetntoa(namebuf, namebuflen, ss) \
SOCaddr_inetntoa_(namebuf, namebuflen, &(ss), __FILE__, __LINE__)
/** Single-char family tag: '1' for IPv4, '2' otherwise (used in the cache). */
#define SOCaddr_getproto(ss) ( SOCaddr_size(ss) == sizeof(struct sockaddr_in) ? '1' : '2')
#define SOCaddr_getproto(ss) \
(SOCaddr_size(ss) == sizeof(struct sockaddr_in) ? '1' : '2')
/** Length type for socket APIs (getsockname, accept, ...). */
typedef socklen_t SOClen;

View File

@@ -72,6 +72,7 @@ typedef struct String String;
#endif
#ifndef HTS_DEF_STRUCT_String
#define HTS_DEF_STRUCT_String
struct String {
char *buffer_;
size_t length_;
@@ -80,7 +81,7 @@ struct String {
#endif
/* Defines */
#define CATBUFF_SIZE (STRING_SIZE*2*2)
#define CATBUFF_SIZE (STRING_SIZE * 2 * 2)
#define STRING_SIZE 2048
@@ -108,7 +109,7 @@ struct htsfilters {
};
/* User callbacks chain */
typedef int (*htscallbacksfncptr) (void);
typedef int (*htscallbacksfncptr)(void);
typedef struct htscallbacks htscallbacks;
@@ -179,6 +180,7 @@ typedef struct lien_url lien_url;
#ifndef HTS_DEF_DEFSTRUCT_hts_log_type
#define HTS_DEF_DEFSTRUCT_hts_log_type
typedef enum hts_log_type {
LOG_PANIC,
LOG_ERROR,
@@ -278,16 +280,17 @@ struct htslibhandles {
/* Javascript parser flags */
typedef enum htsparsejava_flags {
HTSPARSE_NONE = 0, // don't parse
HTSPARSE_DEFAULT = 1, // parse default (all)
HTSPARSE_NO_CLASS = 2, // don't parse .java
HTSPARSE_NO_JAVASCRIPT = 4, // don't parse .js
HTSPARSE_NO_AGGRESSIVE = 8 // don't aggressively parse .js or .java
HTSPARSE_NONE = 0, // don't parse
HTSPARSE_DEFAULT = 1, // parse default (all)
HTSPARSE_NO_CLASS = 2, // don't parse .java
HTSPARSE_NO_JAVASCRIPT = 4, // don't parse .js
HTSPARSE_NO_AGGRESSIVE = 8 // don't aggressively parse .js or .java
} htsparsejava_flags;
/* Link-rewriting style for saved pages (opt->urlmode). */
#ifndef HTS_DEF_DEFSTRUCT_hts_urlmode
#define HTS_DEF_DEFSTRUCT_hts_urlmode
typedef enum hts_urlmode {
HTS_URLMODE_ABSOLUTE = 0, /**< absolute URL (http://host/path) everywhere */
HTS_URLMODE_ABSOLUTE_FILE = 1, /**< legacy file: form, unused */
@@ -301,6 +304,7 @@ typedef enum hts_urlmode {
/* Cache policy for updates and retries (opt->cache). */
#ifndef HTS_DEF_DEFSTRUCT_hts_cachemode
#define HTS_DEF_DEFSTRUCT_hts_cachemode
typedef enum hts_cachemode {
HTS_CACHE_NONE = 0, /**< no cache */
HTS_CACHE_PRIORITY = 1, /**< cache takes priority over the network */
@@ -311,6 +315,7 @@ typedef enum hts_cachemode {
/* Interactive wizard level (opt->wizard). */
#ifndef HTS_DEF_DEFSTRUCT_hts_wizard
#define HTS_DEF_DEFSTRUCT_hts_wizard
typedef enum hts_wizard {
HTS_WIZARD_NONE = 0, /**< no wizard */
HTS_WIZARD_ASK = 1, /**< wizard asks questions */
@@ -321,6 +326,7 @@ typedef enum hts_wizard {
/* robots.txt / meta-robots obedience level (opt->robots). */
#ifndef HTS_DEF_DEFSTRUCT_hts_robots
#define HTS_DEF_DEFSTRUCT_hts_robots
typedef enum hts_robots {
HTS_ROBOTS_NEVER = 0, /**< ignore robots rules */
HTS_ROBOTS_SOMETIMES = 1, /**< partial obedience (default) */
@@ -406,34 +412,34 @@ struct httrackp {
/* */
hts_wizard wizard; /**< interactive wizard level (none/ask/auto) */
hts_boolean flush; /**< fflush() log files after each write */
int travel; /**< link-following scope (same domain, etc.) */
int seeker; /**< allowed direction: go up and/or down the tree */
int depth; /**< maximum recursion depth (-rN) */
int extdepth; /**< maximum recursion depth outside the start domain */
int travel; /**< link-following scope (same domain, etc.) */
int seeker; /**< allowed direction: go up and/or down the tree */
int depth; /**< maximum recursion depth (-rN) */
int extdepth; /**< maximum recursion depth outside the start domain */
hts_urlmode
urlmode; /**< saved-link rewriting style (relative, absolute, etc.) */
hts_boolean no_type_change; // do not change file type according to MIME
hts_log_type debug; /**< debug logging level */
int getmode; /**< what to fetch (HTML, images, ...) bitmask */
FILE *log; /**< informational log stream; NULL mutes it */
FILE *errlog; /**< error log stream; NULL mutes it */
LLint maxsite; /**< max total bytes for the whole mirror */
LLint maxfile_nonhtml; /**< max bytes per non-HTML file */
LLint maxfile_html; /**< max bytes per HTML file */
int maxsoc; /**< max simultaneous sockets (-cN) */
LLint fragment; /**< split site after this many bytes */
hts_boolean no_type_change; // do not change file type according to MIME
hts_log_type debug; /**< debug logging level */
int getmode; /**< what to fetch (HTML, images, ...) bitmask */
FILE *log; /**< informational log stream; NULL mutes it */
FILE *errlog; /**< error log stream; NULL mutes it */
LLint maxsite; /**< max total bytes for the whole mirror */
LLint maxfile_nonhtml; /**< max bytes per non-HTML file */
LLint maxfile_html; /**< max bytes per HTML file */
int maxsoc; /**< max simultaneous sockets (-cN) */
LLint fragment; /**< split site after this many bytes */
hts_boolean
nearlink; /**< also fetch images/data adjacent to a page but off-site */
hts_boolean makeindex; /**< build a top-level index.html */
hts_boolean kindex; /**< build a keyword index */
hts_boolean delete_old; /**< delete locally obsolete files after update */
int timeout; /**< connection timeout in seconds */
int rateout; /**< minimum transfer rate (bytes/s) before abort */
int maxtime; /**< max total mirror duration in seconds */
int maxrate; /**< max transfer rate cap (bytes/s) */
float maxconn; /**< max connections per second */
int waittime; /**< scheduled start time (wall-clock seconds) */
hts_cachemode cache; /**< cache generation mode */
int timeout; /**< connection timeout in seconds */
int rateout; /**< minimum transfer rate (bytes/s) before abort */
int maxtime; /**< max total mirror duration in seconds */
int maxrate; /**< max transfer rate cap (bytes/s) */
float maxconn; /**< max connections per second */
int waittime; /**< scheduled start time (wall-clock seconds) */
hts_cachemode cache; /**< cache generation mode */
// int aff_progress; // progress bar
hts_boolean shell; /**< driven by a shell over stdin/stdout pipes */
t_proxy proxy; /**< proxy configuration */
@@ -446,12 +452,12 @@ struct httrackp {
hts_boolean
delayed_cached; // delayed type check can be cached to speedup updates
hts_boolean mimehtml; /**< produce a single MIME/MHTML archive */
hts_boolean user_agent_send; /**< send a User-Agent header */
String user_agent; /**< User-Agent value (e.g. httrack/1.0) */
String referer; /**< Referer value to send */
String from; /**< From value to send */
String path_log; /**< directory for cache and logs */
String path_html; /**< output directory for the mirror */
hts_boolean user_agent_send; /**< send a User-Agent header */
String user_agent; /**< User-Agent value (e.g. httrack/1.0) */
String referer; /**< Referer value to send */
String from; /**< From value to send */
String path_log; /**< directory for cache and logs */
String path_html; /**< output directory for the mirror */
String path_html_utf8; /**< output directory for the mirror, UTF-8 form */
String path_bin; /**< directory for HTML templates */
int retry; /**< extra retries on a failed transfer */
@@ -459,49 +465,49 @@ struct httrackp {
hts_boolean maketrack; /**< maintain an operations-statistics log */
int parsejava; /**< Java/JS parsing mode; see htsparsejava_flags */
int hostcontrol; /**< ban slow/timing-out hosts; see hts_hostcontrol bits */
hts_boolean errpage; /**< generate an error page on 404 and similar */
hts_boolean errpage; /**< generate an error page on 404 and similar */
hts_boolean
check_type; /**< probe unknown-type links (cgi/asp/dir) and follow moves
*/
hts_boolean all_in_cache; /**< keep all retrieved data in the cache */
hts_robots robots; /**< robots.txt handling level */
hts_boolean all_in_cache; /**< keep all retrieved data in the cache */
hts_robots robots; /**< robots.txt handling level */
hts_boolean external; /**< render external links as error pages */
hts_boolean passprivacy; /**< strip passwords from external links */
hts_boolean includequery; /**< include the query string in saved names */
hts_boolean mirror_first_page; /**< only mirror the links of the first page */
String sys_com; /**< system command to run */
hts_boolean sys_com_exec; /**< actually execute sys_com */
hts_boolean accept_cookie; /**< accept and send cookies */
t_cookie *cookie; /**< cookie store */
hts_boolean http10; /**< force HTTP/1.0 */
hts_boolean nokeepalive; /**< disable keep-alive */
hts_boolean nocompression; /**< disable content compression */
hts_boolean sizehack; /**< treat same-size response as "updated" */
hts_boolean urlhack; // force "url normalization" to avoid loops
hts_boolean tolerant; /**< accept an incorrect Content-Length */
String sys_com; /**< system command to run */
hts_boolean sys_com_exec; /**< actually execute sys_com */
hts_boolean accept_cookie; /**< accept and send cookies */
t_cookie *cookie; /**< cookie store */
hts_boolean http10; /**< force HTTP/1.0 */
hts_boolean nokeepalive; /**< disable keep-alive */
hts_boolean nocompression; /**< disable content compression */
hts_boolean sizehack; /**< treat same-size response as "updated" */
hts_boolean urlhack; // force "url normalization" to avoid loops
hts_boolean tolerant; /**< accept an incorrect Content-Length */
hts_boolean
parseall; /**< parse aggressively, including unknown tags with links */
hts_boolean parsedebug; /**< parser debug mode */
hts_boolean norecatch; /**< do not re-fetch files the user deleted locally */
hts_verbosedisplay verbosedisplay; /**< animated text progress display */
String footer; /**< footer/info line injected into pages */
int maxcache; /**< in-memory cache backing limit (bytes) */
String footer; /**< footer/info line injected into pages */
int maxcache; /**< in-memory cache backing limit (bytes) */
// int maxcache_anticipate; // maximum links to anticipate (upper bound)
hts_boolean ftp_proxy; /**< use the HTTP proxy for FTP too */
String filelist; /**< file listing URLs to include */
String urllist; /**< file listing filters to include */
htsfilters filters; /**< filter pointers (+/-pattern rules) */
hash_struct *hash; // hash structure
lien_url **liens; // links
int lien_tot; // top index of "links" heap (always out-of-range)
lien_buffers *liensbuf; // links buffers
robots_wizard *robotsptr; // robots ptr
String lang_iso; /**< Accept-Language value (en, fr, ...) */
String accept; // Accept:
String headers; // Additional headers
String mimedefs; // ext1=mimetype1\next2=mimetype2..
String mod_blacklist; /**< blacklisted modules */
hts_boolean convert_utf8; // filenames UTF-8 conversion (3.46)
hts_boolean ftp_proxy; /**< use the HTTP proxy for FTP too */
String filelist; /**< file listing URLs to include */
String urllist; /**< file listing filters to include */
htsfilters filters; /**< filter pointers (+/-pattern rules) */
hash_struct *hash; // hash structure
lien_url **liens; // links
int lien_tot; // top index of "links" heap (always out-of-range)
lien_buffers *liensbuf; // links buffers
robots_wizard *robotsptr; // robots ptr
String lang_iso; /**< Accept-Language value (en, fr, ...) */
String accept; // Accept:
String headers; // Additional headers
String mimedefs; // ext1=mimetype1\next2=mimetype2..
String mod_blacklist; /**< blacklisted modules */
hts_boolean convert_utf8; // filenames UTF-8 conversion (3.46)
//
int maxlink; /**< max number of links */
int maxfilter; /**< max number of filters */
@@ -587,17 +593,17 @@ typedef struct htsrequest htsrequest;
struct htsrequest {
short int user_agent_send; /**< send a User-Agent header */
short int http11; /**< sign the request as HTTP/1.1 rather than HTTP/1.0 */
short int nokeepalive; /**< disable keep-alive */
short int range_used; /**< a Range header is in use */
short int nocompression; /**< disable compression */
short int flush_garbage; // recycled
const char *user_agent; /**< User-Agent value */
const char *referer; /**< Referer value */
const char *from; /**< From value */
const char *lang_iso; /**< Accept-Language value */
const char *accept; /**< Accept value */
const char *headers; /**< extra request headers */
htsrequest_proxy proxy; /**< proxy for this request */
short int nokeepalive; /**< disable keep-alive */
short int range_used; /**< a Range header is in use */
short int nocompression; /**< disable compression */
short int flush_garbage; // recycled
const char *user_agent; /**< User-Agent value */
const char *referer; /**< Referer value */
const char *from; /**< From value */
const char *lang_iso; /**< Accept-Language value */
const char *accept; /**< Accept value */
const char *headers; /**< extra request headers */
htsrequest_proxy proxy; /**< proxy for this request */
};
/* Result of a connection / header fetch. */
@@ -629,8 +635,8 @@ struct htsblk {
short int is_file; /**< 1 if a file descriptor rather than a socket */
T_SOC soc; /**< socket id */
SOCaddr address; /**< peer IP address */
int address_size; // IP address structure length (unused internally)
FILE *fp; /**< file handle for file:// */
int address_size; // IP address structure length (unused internally)
FILE *fp; /**< file handle for file:// */
#if HTS_USEOPENSSL
short int ssl; /**< nonzero if this is an SSL connection (https) */
// BIO* ssl_soc; // SSL structure
@@ -712,7 +718,7 @@ struct lien_back {
LLint chunk_blocksize; /**< data size declared by the chunk */
LLint compressed_size; /**< compressed size (stats only) */
//
//int links_index; // to access liens[links_index]
// int links_index; // to access liens[links_index]
//
char info[256]; /**< status text, e.g. for FTP */
int stop_ftp; /**< stop flag for FTP */

View File

@@ -48,7 +48,7 @@ Please visit our Website: http://www.httrack.com
/** Assert error callback. **/
#ifndef HTS_DEF_FWSTRUCT_htsErrorCallback
#define HTS_DEF_FWSTRUCT_htsErrorCallback
typedef void (*htsErrorCallback) (const char *msg, const char *file, int line);
typedef void (*htsErrorCallback)(const char *msg, const char *file, int line);
#ifdef __cplusplus
extern "C" {
#endif
@@ -58,12 +58,13 @@ HTSEXT_API htsErrorCallback hts_get_error_callback(void);
#endif
#endif
#define HTSSAFE_ABORT_FUNCTION(A,B,C) do { \
htsErrorCallback callback = hts_get_error_callback(); \
if (callback != NULL) { \
callback(A,B,C); \
} \
} while(0)
#define HTSSAFE_ABORT_FUNCTION(A, B, C) \
do { \
htsErrorCallback callback = hts_get_error_callback(); \
if (callback != NULL) { \
callback(A, B, C); \
} \
} while (0)
#endif
@@ -75,7 +76,8 @@ HTSEXT_API htsErrorCallback hts_get_error_callback(void);
/**
* Fatal assertion check.
*/
#define assertf__(exp, sexp, file, line) (void) ( (exp) || (abortf_(sexp, file, line), 0) )
#define assertf__(exp, sexp, file, line) \
(void) ((exp) || (abortf_(sexp, file, line), 0))
/**
* Fatal assertion check.
@@ -106,12 +108,13 @@ static HTS_UNUSED void abortf_(const char *exp, const char *file, int line) {
#if (defined(__GNUC__) && !defined(__cplusplus))
/* Note: char[] and const char[] are compatible */
#define HTS_IS_CHAR_BUFFER(VAR) ( __builtin_types_compatible_p ( typeof (VAR), char[] ) )
#define HTS_IS_CHAR_BUFFER(VAR) \
(__builtin_types_compatible_p(typeof(VAR), char[]))
#else
/* Note: a bit lame as char[8] won't be seen. */
#define HTS_IS_CHAR_BUFFER(VAR) ( sizeof(VAR) != sizeof(char*) )
#define HTS_IS_CHAR_BUFFER(VAR) (sizeof(VAR) != sizeof(char *))
#endif
#define HTS_IS_NOT_CHAR_BUFFER(VAR) ( ! HTS_IS_CHAR_BUFFER(VAR) )
#define HTS_IS_NOT_CHAR_BUFFER(VAR) (!HTS_IS_CHAR_BUFFER(VAR))
/* Compile-time checks. */
static HTS_UNUSED void htssafe_compile_time_check_(void) {
@@ -201,60 +204,74 @@ 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_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), N, \
"overflow while appending '" #B "' to '"#A"'", __FILE__, __LINE__), \
strncatbuff_ptr_((A), (B), (N)) )
#define strncatbuff(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_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), N, \
"overflow while appending '" #B "' to '"#A"'", __FILE__, __LINE__) )
#define strncatbuff(A, B, N) \
(HTS_IS_NOT_CHAR_BUFFER(A) \
? strncat(A, 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
/**
* Append characters of "B" to "A".
* If "A" is a char[] variable whose size is not sizeof(char*), then the size
* If "A" is a char[] variable whose size is not sizeof(char*), then the size
* is assumed to be the capacity of this array.
*/
#if (defined(__GNUC__) && !defined(__cplusplus))
#define strcatbuff(A, B) __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), (size_t) -1, \
"overflow while appending '" #B "' to '"#A"'", __FILE__, __LINE__), \
strcatbuff_ptr_((A), (B)) )
#define strcatbuff(A, B) \
__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), \
(size_t) -1, \
"overflow while appending '" #B "' to '" #A "'", __FILE__, \
__LINE__), \
strcatbuff_ptr_((A), (B)))
#else
#define strcatbuff(A, B) \
( HTS_IS_NOT_CHAR_BUFFER(A) \
? strcat(A, B) \
: 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__) )
#define strcatbuff(A, B) \
(HTS_IS_NOT_CHAR_BUFFER(A) \
? strcat(A, B) \
: 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
/**
* Copy characters from "B" to "A".
* If "A" is a char[] variable whose size is not sizeof(char*), then the size
* If "A" is a char[] variable whose size is not sizeof(char*), then the size
* is assumed to be the capacity of this array.
*/
#if (defined(__GNUC__) && !defined(__cplusplus))
#define strcpybuff(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)) )
#define strcpybuff(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_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
"overflow while copying '" #B "' to '"#A"'", __FILE__, __LINE__) )
#define strcpybuff(A, B) \
(HTS_IS_NOT_CHAR_BUFFER(A) \
? strcpy(A, 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
/*
@@ -268,10 +285,10 @@ 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_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), (size_t) -1, \
"overflow while appending '" #B "' to '"#A"'", __FILE__, __LINE__)
#define strlcatbuff(A, B, S) \
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
@@ -285,17 +302,18 @@ static char *strncatbuff_ptr_(char *dest, const char *src, size_t n) {
/**
* Copy characters of "B" to "A", "A" having a maximum capacity of "S".
*/
#define strlcpybuff(A, B, S) \
strcpy_safe_(A, S, B, \
HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
"overflow while copying '" #B "' to '"#A"'", __FILE__, __LINE__)
#define strlcpybuff(A, B, S) \
strcpy_safe_(A, S, B, HTS_IS_NOT_CHAR_BUFFER(B) ? (size_t) -1 : sizeof(B), \
"overflow while copying '" #B "' to '" #A "'", __FILE__, \
__LINE__)
/** strnlen replacement (autotools). **/
#if ( ! defined(_WIN32) && ! defined(HAVE_STRNLEN) )
#if (!defined(_WIN32) && !defined(HAVE_STRNLEN))
static HTS_UNUSED size_t strnlen(const char *s, size_t maxlen) {
size_t i;
for(i = 0 ; i < maxlen && s[i] != '\0' ; i++) ;
for (i = 0; i < maxlen && s[i] != '\0'; i++)
;
return i;
}
#endif
@@ -304,13 +322,14 @@ static HTS_UNUSED size_t strnlen(const char *s, size_t maxlen) {
Aborts if source is NULL or has no NUL within that capacity. The sentinel
sizeof_source == (size_t)-1 means "capacity unknown", and falls back to the
unbounded strlen (used when the source is a pointer rather than an array). */
static HTS_INLINE HTS_UNUSED size_t strlen_safe_(const char *source, const size_t sizeof_source,
static HTS_INLINE HTS_UNUSED size_t strlen_safe_(const char *source,
const size_t sizeof_source,
const char *file, int line) {
size_t size;
assertf_( source != NULL, file, line );
size = sizeof_source != (size_t) -1
? strnlen(source, sizeof_source) : strlen(source);
assertf_( size < sizeof_source, file, line );
assertf_(source != NULL, file, line);
size = sizeof_source != (size_t) -1 ? strnlen(source, sizeof_source)
: strlen(source);
assertf_(size < sizeof_source, file, line);
return size;
}
@@ -319,10 +338,10 @@ static HTS_INLINE HTS_UNUSED size_t strlen_safe_(const char *source, const size_
source's capacity or (size_t)-1 if unknown. Aborts if the result (existing
dest length + appended bytes + NUL) would not fit sizeof_dest: this NEVER
truncates. Always NUL-terminates on success. */
static HTS_INLINE HTS_UNUSED char* strncat_safe_(char *const dest, const size_t sizeof_dest,
const char *const source, const size_t sizeof_source,
const size_t n,
const char *exp, const char *file, int line) {
static HTS_INLINE HTS_UNUSED char *
strncat_safe_(char *const dest, const size_t sizeof_dest,
const char *const source, const size_t sizeof_source,
const size_t n, const char *exp, const char *file, int line) {
const size_t source_len = strlen_safe_(source, sizeof_source, file, line);
const size_t dest_len = strlen_safe_(dest, sizeof_dest, file, line);
/* note: "size_t is an unsigned integral type" ((size_t) -1 is positive) */
@@ -337,12 +356,14 @@ static HTS_INLINE HTS_UNUSED char* strncat_safe_(char *const dest, const size_t
/* Core bounded copy: empties dest then appends all of source via
strncat_safe_. sizeof_dest is dest's total capacity (NUL included). Aborts
(no truncation) if source plus its NUL would not fit. */
static HTS_INLINE HTS_UNUSED char* strcpy_safe_(char *const dest, const size_t sizeof_dest,
const char *const source, const size_t sizeof_source,
const char *exp, const char *file, int line) {
static HTS_INLINE HTS_UNUSED char *
strcpy_safe_(char *const dest, const size_t sizeof_dest,
const char *const source, const size_t sizeof_source,
const char *exp, const char *file, int line) {
assertf_(sizeof_dest != 0, file, line);
dest[0] = '\0';
return strncat_safe_(dest, sizeof_dest, source, sizeof_source, (size_t) -1, exp, file, line);
return strncat_safe_(dest, sizeof_dest, source, sizeof_source, (size_t) -1,
exp, file, line);
}
/**
@@ -360,9 +381,9 @@ static HTS_INLINE HTS_UNUSED char* strcpy_safe_(char *const dest, const size_t s
* htsbuff_ptr(). The buffer is kept NUL-terminated; htsbuff_str() returns it.
*/
typedef struct {
char *buf; /* backing buffer (kept NUL-terminated) */
size_t cap; /* total capacity of buf, including the NUL */
size_t len; /* current length, excluding the NUL */
char *buf; /* backing buffer (kept NUL-terminated) */
size_t cap; /* total capacity of buf, including the NUL */
size_t len; /* current length, excluding the NUL */
} htsbuff;
static HTS_INLINE HTS_UNUSED htsbuff htsbuff_ptr_(char *buf, size_t cap) {
@@ -384,23 +405,29 @@ static HTS_INLINE HTS_UNUSED htsbuff htsbuff_ptr_(char *buf, size_t cap) {
#if (defined(__GNUC__) && !defined(__cplusplus))
/* 0 for an array, a -1 array-size compile error for a pointer. */
#define htsbuff_must_be_array_(A) \
(sizeof(char[1 - 2 * !!__builtin_types_compatible_p(typeof(A), typeof(&(A)[0]))]) - 1)
#define htsbuff_must_be_array_(A) \
(sizeof(char[1 - 2 * !!__builtin_types_compatible_p(typeof(A), \
typeof(&(A)[0]))]) - \
1)
#define htsbuff_array(ARR) htsbuff_ptr_((ARR), sizeof(ARR) + htsbuff_must_be_array_(ARR))
#define htsbuff_array(ARR) \
htsbuff_ptr_((ARR), sizeof(ARR) + htsbuff_must_be_array_(ARR))
#else
#define htsbuff_array(ARR) htsbuff_ptr_((ARR), sizeof(ARR))
#endif
/** Builder over pointer P of known capacity N (N includes the NUL). */
#define htsbuff_ptr(P, N) htsbuff_ptr_((P), (N))
#define htsbuff_ptr(P, N) htsbuff_ptr_((P), (N))
/** Append at most n characters of s (stopping at its NUL). Aborts on overflow. */
static HTS_INLINE HTS_UNUSED void htsbuff_catn(htsbuff *b, const char *s, size_t n) {
/** Append at most n characters of s (stopping at its NUL). Aborts on overflow.
*/
static HTS_INLINE HTS_UNUSED void htsbuff_catn(htsbuff *b, const char *s,
size_t n) {
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. */
assertf__(add < b->cap - b->len, "htsbuff append overflow", __FILE__, __LINE__);
assertf__(add < b->cap - b->len, "htsbuff append overflow", __FILE__,
__LINE__);
memcpy(b->buf + b->len, s, add);
b->len += add;
b->buf[b->len] = '\0';
@@ -433,15 +460,21 @@ static HTS_INLINE HTS_UNUSED const char *htsbuff_str(const htsbuff *b) {
added bounds checking. freet() also NULLs the freed pointer and tolerates
NULL. memcpybuff() despite the name is a raw memcpy: the caller owns the
bounds. */
#define malloct(A) malloc(A)
#define malloct(A) malloc(A)
#define calloct(A,B) calloc((A), (B))
#define calloct(A, B) calloc((A), (B))
#define freet(A) do { if ((A) != NULL) { free(A); (A) = NULL; } } while(0)
#define freet(A) \
do { \
if ((A) != NULL) { \
free(A); \
(A) = NULL; \
} \
} while (0)
#define strdupt(A) strdup(A)
#define strdupt(A) strdup(A)
#define realloct(A,B) realloc(A, B)
#define realloct(A, B) realloc(A, B)
#define memcpybuff(A, B, N) memcpy((A), (B), (N))

View File

@@ -41,11 +41,11 @@ Please visit our Website: http://www.httrack.com
/* GCC extension */
#ifndef HTS_UNUSED
#ifdef __GNUC__
#define HTS_UNUSED __attribute__ ((unused))
#define HTS_UNUSED __attribute__((unused))
#define HTS_STATIC static __attribute__ ((unused))
#define HTS_STATIC static __attribute__((unused))
#define HTS_PRINTF_FUN(fmt, arg) __attribute__ ((format (printf, fmt, arg)))
#define HTS_PRINTF_FUN(fmt, arg) __attribute__((format(printf, fmt, arg)))
#else
#define HTS_UNUSED
#define HTS_STATIC static
@@ -60,6 +60,7 @@ typedef struct String String;
#endif
#ifndef HTS_DEF_STRUCT_String
#define HTS_DEF_STRUCT_String
/**
* Growable owned string.
*
@@ -86,7 +87,7 @@ struct String {
/** Allocator **/
#ifndef STRING_REALLOC
#define STRING_REALLOC(BUFF, SIZE) ( (char*) realloc(BUFF, SIZE) )
#define STRING_REALLOC(BUFF, SIZE) ((char *) realloc(BUFF, SIZE))
#define STRING_FREE(BUFF) free(BUFF)
#endif
@@ -96,11 +97,11 @@ struct String {
#endif
/** Initializer for an empty String (NULL buffer). Use to declare or reset. **/
#define STRING_EMPTY { (char*) NULL, 0, 0 }
#define STRING_EMPTY {(char *) NULL, 0, 0}
/** Read-only buffer pointer. NULL until the String has been written to.
Invalidated by any subsequent growing operation. **/
#define StringBuff(BLK) ( (const char*) ((BLK).buffer_) )
#define StringBuff(BLK) ((const char *) ((BLK).buffer_))
/** Read/write buffer pointer. Same NULL/invalidation rules as StringBuff. **/
#define StringBuffRW(BLK) ((BLK).buffer_)
@@ -109,56 +110,60 @@ struct String {
#define StringLength(BLK) ((BLK).length_)
/** Non-zero if the String holds at least one byte. **/
#define StringNotEmpty(BLK) ( StringLength(BLK) > 0 )
#define StringNotEmpty(BLK) (StringLength(BLK) > 0)
/** Allocated capacity in bytes, including room for the terminating NUL. **/
#define StringCapacity(BLK) ((BLK).capacity_)
/** Byte at POS (read). No bounds check; POS must be < StringLength. **/
#define StringSub(BLK, POS) ( StringBuff(BLK)[POS] )
#define StringSub(BLK, POS) (StringBuff(BLK)[POS])
/** Byte at POS (read/write). No bounds check; POS must be < StringLength. **/
#define StringSubRW(BLK, POS) ( StringBuffRW(BLK)[POS] )
#define StringSubRW(BLK, POS) (StringBuffRW(BLK)[POS])
/** Subcharacter (read/write) **/
#define StringSubRW(BLK, POS) ( StringBuffRW(BLK)[POS] )
#define StringSubRW(BLK, POS) (StringBuffRW(BLK)[POS])
/** Byte POS positions from the end (read). POS==1 is the last byte. **/
#define StringRight(BLK, POS) ( StringBuff(BLK)[StringLength(BLK) - POS] )
#define StringRight(BLK, POS) (StringBuff(BLK)[StringLength(BLK) - POS])
/** Byte POS positions from the end (read/write). POS==1 is the last byte. **/
#define StringRightRW(BLK, POS) ( StringBuffRW(BLK)[StringLength(BLK) - POS] )
#define StringRightRW(BLK, POS) (StringBuffRW(BLK)[StringLength(BLK) - POS])
/** Drop the last byte and re-terminate. Undefined if the String is empty
(no length check; would underflow). **/
#define StringPopRight(BLK) do { \
StringBuffRW(BLK)[--StringLength(BLK)] = '\0'; \
} while(0)
#define StringPopRight(BLK) \
do { \
StringBuffRW(BLK)[--StringLength(BLK)] = '\0'; \
} while (0)
/** Grow so capacity_ >= CAPACITY (total bytes, including the NUL). May realloc
(invalidating prior buffer pointers); aborts via STRING_ASSERT on OOM.
Never shrinks. **/
#define StringRoomTotal(BLK, CAPACITY) do { \
const size_t capacity_ = (size_t) (CAPACITY); \
while ((BLK).capacity_ < capacity_) { \
if ((BLK).capacity_ < 16) { \
(BLK).capacity_ = 16; \
} else { \
(BLK).capacity_ *= 2; \
} \
(BLK).buffer_ = STRING_REALLOC((BLK).buffer_, (BLK).capacity_); \
STRING_ASSERT((BLK).buffer_ != NULL); \
} \
} while(0)
#define StringRoomTotal(BLK, CAPACITY) \
do { \
const size_t capacity_ = (size_t) (CAPACITY); \
while ((BLK).capacity_ < capacity_) { \
if ((BLK).capacity_ < 16) { \
(BLK).capacity_ = 16; \
} else { \
(BLK).capacity_ *= 2; \
} \
(BLK).buffer_ = STRING_REALLOC((BLK).buffer_, (BLK).capacity_); \
STRING_ASSERT((BLK).buffer_ != NULL); \
} \
} while (0)
/** Reserve room for SIZE more bytes beyond the current length (plus the NUL).
May realloc, invalidating prior buffer pointers. **/
#define StringRoom(BLK, SIZE) StringRoomTotal(BLK, StringLength(BLK) + (SIZE) + 1)
#define StringRoom(BLK, SIZE) \
StringRoomTotal(BLK, StringLength(BLK) + (SIZE) + 1)
/** Reserve room for SIZE more bytes and return the (post-realloc) RW buffer,
for appending in place. Does not update length_; the caller must. **/
#define StringBuffN(BLK, SIZE) StringBuffN_(&(BLK), SIZE)
HTS_STATIC char *StringBuffN_(String * blk, int size) {
HTS_STATIC char *StringBuffN_(String *blk, int size) {
StringRoom(*blk, size);
return StringBuffRW(*blk);
}
@@ -166,40 +171,44 @@ HTS_STATIC char *StringBuffN_(String * blk, int size) {
/** Zero the fields (NULL buffer, no allocation). Use on an uninitialized
String only; does NOT free an existing buffer (use StringFree to reset
an owned one), so calling it on a live String leaks. **/
#define StringInit(BLK) do { \
(BLK).buffer_ = NULL; \
(BLK).capacity_ = 0; \
(BLK).length_ = 0; \
} while(0)
#define StringInit(BLK) \
do { \
(BLK).buffer_ = NULL; \
(BLK).capacity_ = 0; \
(BLK).length_ = 0; \
} while (0)
/** Truncate to length 0, keeping the allocation. Forces a non-NULL buffer
(allocates if empty) and writes the leading NUL, so StringBuff is "". **/
#define StringClear(BLK) do { \
(BLK).length_ = 0; \
StringRoom(BLK, 0); \
(BLK).buffer_[0] = '\0'; \
} while(0)
#define StringClear(BLK) \
do { \
(BLK).length_ = 0; \
StringRoom(BLK, 0); \
(BLK).buffer_[0] = '\0'; \
} while (0)
/** Set length_ to SIZE, or to strlen(buffer_) if SIZE is negative. Caller
asserts SIZE fits the existing content; does not (re)allocate. **/
#define StringSetLength(BLK, SIZE) do { \
if (SIZE >= 0) { \
(BLK).length_ = SIZE; \
} else { \
(BLK).length_ = strlen((BLK).buffer_); \
} \
} while(0)
#define StringSetLength(BLK, SIZE) \
do { \
if (SIZE >= 0) { \
(BLK).length_ = SIZE; \
} else { \
(BLK).length_ = strlen((BLK).buffer_); \
} \
} while (0)
/** Release the owned buffer and reset to the empty state (NULL buffer).
Idempotent; safe on an already-empty String. **/
#define StringFree(BLK) do { \
if ((BLK).buffer_ != NULL) { \
STRING_FREE((BLK).buffer_); \
(BLK).buffer_ = NULL; \
} \
(BLK).capacity_ = 0; \
(BLK).length_ = 0; \
} while(0)
#define StringFree(BLK) \
do { \
if ((BLK).buffer_ != NULL) { \
STRING_FREE((BLK).buffer_); \
(BLK).buffer_ = NULL; \
} \
(BLK).capacity_ = 0; \
(BLK).length_ = 0; \
} while (0)
/** Take ownership of a NUL-terminated heap string STR (the String will free
it). Frees any current buffer first. STR MUST have been allocated by an
@@ -207,48 +216,52 @@ HTS_STATIC char *StringBuffN_(String * blk, int size) {
freed or used by the caller afterwards. length_/capacity_ are set to
strlen(STR) (capacity_ here excludes the NUL, so the next append reallocs).
**/
#define StringSetBuffer(BLK, STR) do { \
size_t len__ = strlen( STR ); \
StringFree(BLK); \
(BLK).buffer_ = ( STR ); \
(BLK).capacity_ = len__; \
(BLK).length_ = len__; \
} while(0)
#define StringSetBuffer(BLK, STR) \
do { \
size_t len__ = strlen(STR); \
StringFree(BLK); \
(BLK).buffer_ = (STR); \
(BLK).capacity_ = len__; \
(BLK).length_ = len__; \
} while (0)
/** Append SIZE raw bytes from STR (NULs allowed as data). Grows as needed and
re-terminates with a NUL after the appended bytes. STR must not alias
BLK's buffer (a realloc would invalidate it). **/
#define StringMemcat(BLK, STR, SIZE) do { \
const char* str_mc_ = (STR); \
const size_t size_mc_ = (size_t) (SIZE); \
StringRoom(BLK, size_mc_); \
if (size_mc_ > 0) { \
memcpy((BLK).buffer_ + (BLK).length_, str_mc_, size_mc_); \
(BLK).length_ += size_mc_; \
} \
*((BLK).buffer_ + (BLK).length_) = '\0'; \
} while(0)
#define StringMemcat(BLK, STR, SIZE) \
do { \
const char *str_mc_ = (STR); \
const size_t size_mc_ = (size_t) (SIZE); \
StringRoom(BLK, size_mc_); \
if (size_mc_ > 0) { \
memcpy((BLK).buffer_ + (BLK).length_, str_mc_, size_mc_); \
(BLK).length_ += size_mc_; \
} \
*((BLK).buffer_ + (BLK).length_) = '\0'; \
} while (0)
/** Replace content with SIZE raw bytes from STR (NULs allowed as data).
Same non-aliasing requirement as StringMemcat. **/
#define StringMemcpy(BLK, STR, SIZE) do { \
(BLK).length_ = 0; \
StringMemcat(BLK, STR, SIZE); \
} while(0)
#define StringMemcpy(BLK, STR, SIZE) \
do { \
(BLK).length_ = 0; \
StringMemcat(BLK, STR, SIZE); \
} while (0)
/** Append one byte and re-terminate. Grows as needed. **/
#define StringAddchar(BLK, c) do { \
String * const s__ = &(BLK); \
char c__ = (c); \
StringRoom(*s__, 1); \
StringBuffRW(*s__)[StringLength(*s__)++] = c__; \
StringBuffRW(*s__)[StringLength(*s__) ] = 0; \
} while(0)
#define StringAddchar(BLK, c) \
do { \
String *const s__ = &(BLK); \
char c__ = (c); \
StringRoom(*s__, 1); \
StringBuffRW(*s__)[StringLength(*s__)++] = c__; \
StringBuffRW(*s__)[StringLength(*s__)] = 0; \
} while (0)
/** Hand the buffer to the caller and reset the String to empty (NULL buffer).
The returned pointer is now owned by the caller, who must STRING_FREE() it.
Returns NULL if the String was empty. **/
HTS_STATIC char *StringAcquire(String * blk) {
HTS_STATIC char *StringAcquire(String *blk) {
char *buff = StringBuffRW(*blk);
StringBuffRW(*blk) = NULL;
@@ -259,7 +272,7 @@ HTS_STATIC char *StringAcquire(String * blk) {
/** Return an independent deep copy of *src (its own allocation). The caller
owns the result and must StringFree it. **/
HTS_STATIC String StringDup(const String * src) {
HTS_STATIC String StringDup(const String *src) {
String s = STRING_EMPTY;
StringMemcat(s, StringBuff(*src), StringLength(*src));
@@ -270,7 +283,7 @@ HTS_STATIC String StringDup(const String * src) {
ownership transfers and the caller keeps no dangling alias. Frees any
current buffer first. *str MUST be allocator-compatible (see
StringSetBuffer). No-op if str or *str is NULL. **/
HTS_STATIC void StringAttach(String * blk, char **str) {
HTS_STATIC void StringAttach(String *blk, char **str) {
StringFree(*blk);
if (str != NULL && *str != NULL) {
StringBuffRW(*blk) = *str;
@@ -281,43 +294,46 @@ HTS_STATIC void StringAttach(String * blk, char **str) {
/** Append the C string STR (up to its NUL). No-op if STR is NULL. STR must not
alias BLK's buffer. **/
#define StringCat(BLK, STR) do { \
const char *const str__ = ( STR ); \
if (str__ != NULL) { \
const size_t size__ = strlen(str__); \
StringMemcat(BLK, str__, size__); \
} \
} while(0)
#define StringCat(BLK, STR) \
do { \
const char *const str__ = (STR); \
if (str__ != NULL) { \
const size_t size__ = strlen(str__); \
StringMemcat(BLK, str__, size__); \
} \
} while (0)
/** Append at most SIZE leading bytes of the C string STR. No-op if STR is
NULL. STR must not alias BLK's buffer. **/
#define StringCatN(BLK, STR, SIZE) do { \
const char *str__ = ( STR ); \
if (str__ != NULL) { \
size_t size__ = strlen(str__); \
if (size__ > (SIZE)) { \
size__ = (SIZE); \
} \
StringMemcat(BLK, str__, size__); \
} \
} while(0)
#define StringCatN(BLK, STR, SIZE) \
do { \
const char *str__ = (STR); \
if (str__ != NULL) { \
size_t size__ = strlen(str__); \
if (size__ > (SIZE)) { \
size__ = (SIZE); \
} \
StringMemcat(BLK, str__, size__); \
} \
} while (0)
/** Replace content with at most SIZE leading bytes of the C string STR.
If STR is NULL, clears to "". STR must not alias BLK's buffer. **/
#define StringCopyN(BLK, STR, SIZE) do { \
const char *str__ = ( STR ); \
const size_t usize__ = (SIZE); \
(BLK).length_ = 0; \
if (str__ != NULL) { \
size_t size__ = strlen(str__); \
if (size__ > usize__ ) { \
size__ = usize__; \
} \
StringMemcat(BLK, str__, size__); \
} else { \
StringClear(BLK); \
} \
} while(0)
#define StringCopyN(BLK, STR, SIZE) \
do { \
const char *str__ = (STR); \
const size_t usize__ = (SIZE); \
(BLK).length_ = 0; \
if (str__ != NULL) { \
size_t size__ = strlen(str__); \
if (size__ > usize__) { \
size__ = usize__; \
} \
StringMemcat(BLK, str__, size__); \
} else { \
StringClear(BLK); \
} \
} while (0)
/** Replace blk's content with a copy of String blk2. blk and blk2 must be
distinct Strings (use StringCopyOverlapped if they may be the same). **/
@@ -326,23 +342,25 @@ HTS_STATIC void StringAttach(String * blk, char **str) {
/** Replace content with a copy of the C string STR. If STR is NULL, clears to
"". STR must not alias BLK's buffer (use StringCopyOverlapped if it might).
**/
#define StringCopy(BLK, STR) do { \
const char *str__ = ( STR ); \
if (str__ != NULL) { \
size_t size__ = strlen(str__); \
StringMemcpy(BLK, str__, size__); \
} else { \
StringClear(BLK); \
} \
} while(0)
#define StringCopy(BLK, STR) \
do { \
const char *str__ = (STR); \
if (str__ != NULL) { \
size_t size__ = strlen(str__); \
StringMemcpy(BLK, str__, size__); \
} else { \
StringClear(BLK); \
} \
} while (0)
/** Like StringCopy but safe when STR aliases BLK's own buffer: copies via a
temporary, so a self-copy or overlap is well-defined. **/
#define StringCopyOverlapped(BLK, STR) do { \
String s__ = STRING_EMPTY; \
StringCopy(s__, STR); \
StringCopyS(BLK, s__); \
StringFree(s__); \
} while(0)
#define StringCopyOverlapped(BLK, STR) \
do { \
String s__ = STRING_EMPTY; \
StringCopy(s__, STR); \
StringCopyS(BLK, s__); \
StringFree(s__); \
} while (0)
#endif

View File

@@ -57,17 +57,17 @@ extern "C" {
#endif
/** Legacy no-op retained for ABI compatibility; always returns 1. */
HTSEXT_API int htswrap_init(void); // LEGACY
HTSEXT_API int htswrap_init(void); // LEGACY
/** Legacy no-op retained for ABI compatibility; always returns 1. */
HTSEXT_API int htswrap_free(void); // LEGACY
HTSEXT_API int htswrap_free(void); // LEGACY
#ifdef __cplusplus
}
#endif
//HTSEXT_API int htswrap_add(httrackp * opt, const char *name, void *fct);
//HTSEXT_API uintptr_t htswrap_read(httrackp * opt, const char *name);
// HTSEXT_API int htswrap_add(httrackp * opt, const char *name, void *fct);
// HTSEXT_API uintptr_t htswrap_read(httrackp * opt, const char *name);
#endif

View File

@@ -73,6 +73,7 @@ typedef struct strc_int2bytes2 strc_int2bytes2;
#endif
#ifndef HTS_DEF_DEFSTRUCT_hts_log_type
#define HTS_DEF_DEFSTRUCT_hts_log_type
/** Log severity levels, most to least severe. A message is emitted only if its
level is <= opt->debug. LOG_ERRNO is a flag OR'd into the level to append
": <strerror(errno)>" to the message. */
@@ -97,7 +98,7 @@ typedef struct hts_stat_struct hts_stat_struct;
retain them. */
#ifndef HTS_DEF_FWSTRUCT_htsErrorCallback
#define HTS_DEF_FWSTRUCT_htsErrorCallback
typedef void (*htsErrorCallback) (const char *msg, const char *file, int line);
typedef void (*htsErrorCallback)(const char *msg, const char *file, int line);
#endif
/* Helpers for plugging callbacks
@@ -111,29 +112,35 @@ requires: htsdefines.h */
* CALLBACKARG_USERDEF(). Allocates a t_hts_callbackarg with hts_malloc (not
* checked for OOM); it is freed by hts_free_opt().
*/
#define CHAIN_FUNCTION(OPT, MEMBER, FUNCTION, ARGUMENT) do { \
t_hts_callbackarg *carg = (t_hts_callbackarg*) hts_malloc(sizeof(t_hts_callbackarg)); \
carg->userdef = ( ARGUMENT ); \
carg->prev.fun = (void*) ( OPT )->callbacks_fun-> MEMBER .fun; \
carg->prev.carg = ( OPT )->callbacks_fun-> MEMBER .carg; \
( OPT )->callbacks_fun-> MEMBER .fun = ( FUNCTION ); \
( OPT )->callbacks_fun-> MEMBER .carg = carg; \
} while(0)
#define CHAIN_FUNCTION(OPT, MEMBER, FUNCTION, ARGUMENT) \
do { \
t_hts_callbackarg *carg = \
(t_hts_callbackarg *) hts_malloc(sizeof(t_hts_callbackarg)); \
carg->userdef = (ARGUMENT); \
carg->prev.fun = (void *) (OPT)->callbacks_fun->MEMBER.fun; \
carg->prev.carg = (OPT)->callbacks_fun->MEMBER.carg; \
(OPT)->callbacks_fun->MEMBER.fun = (FUNCTION); \
(OPT)->callbacks_fun->MEMBER.carg = carg; \
} while (0)
/* The following helpers are useful only if you know that an existing callback migh be existing before before the call to CHAIN_FUNCTION()
If your functions were added just after hts_create_opt(), no need to make the previous function check */
/* The following helpers are useful only if you know that an existing callback
migh be existing before before the call to CHAIN_FUNCTION() If your functions
were added just after hts_create_opt(), no need to make the previous function
check */
/** Inside a chained callback, return the ARGUMENT pointer originally passed to
CHAIN_FUNCTION(), or NULL when CARG is NULL. */
#define CALLBACKARG_USERDEF(CARG) ( ( (CARG) != NULL ) ? (CARG)->userdef : NULL )
#define CALLBACKARG_USERDEF(CARG) (((CARG) != NULL) ? (CARG)->userdef : NULL)
/** Return the callback of type NAME that this one chained over, cast to its
function-pointer type, or NULL. Call it to forward to the prior handler. */
#define CALLBACKARG_PREV_FUN(CARG, NAME) ( (t_hts_htmlcheck_ ##NAME) ( ( (CARG) != NULL ) ? (CARG)->prev.fun : NULL ) )
#define CALLBACKARG_PREV_FUN(CARG, NAME) \
((t_hts_htmlcheck_##NAME)(((CARG) != NULL) ? (CARG)->prev.fun : NULL))
/** Return the carg of the callback this one chained over (pass it when
forwarding to the CALLBACKARG_PREV_FUN result), or NULL. */
#define CALLBACKARG_PREV_CARG(CARG) ( ( (CARG) != NULL ) ? (CARG)->prev.carg : NULL )
#define CALLBACKARG_PREV_CARG(CARG) \
(((CARG) != NULL) ? (CARG)->prev.carg : NULL)
/* Functions */
@@ -162,7 +169,7 @@ HTSEXT_API int hts_main(int argc, char **argv);
hts_main() to set options or plug callbacks on opt first. Blocks until the
mirror ends and returns the engine exit code. The caller keeps ownership of
opt and must release it with hts_free_opt(). */
HTSEXT_API int hts_main2(int argc, char **argv, httrackp * opt);
HTSEXT_API int hts_main2(int argc, char **argv, httrackp *opt);
/* Options handling */
/** Allocate and default-initialize an option set, preloading the bundled parser
@@ -174,7 +181,7 @@ HTSEXT_API httrackp *hts_create_opt(void);
modules, DNS cache, owned strings, and the structure). NULL is accepted. The
pointer is invalid afterward. Do not call while a mirror is running on that
opt; wait until hts_has_stopped() is true. */
HTSEXT_API void hts_free_opt(httrackp * opt);
HTSEXT_API void hts_free_opt(httrackp *opt);
/** Return sizeof(httrackp) as the library sees it, for caller-vs-library struct
ABI mismatch checks. */
@@ -184,16 +191,16 @@ HTSEXT_API size_t hts_sizeof_opt(void);
Returns NULL if opt is NULL. The result aliases a single process-global
static: it is not thread-safe and is overwritten by the next call, so copy
out the fields you need. */
HTSEXT_API const hts_stat_struct* hts_get_stats(httrackp * opt);
HTSEXT_API const hts_stat_struct *hts_get_stats(httrackp *opt);
/** Legacy no-op retained for API compatibility. */
HTSEXT_API void set_wrappers(httrackp * opt); /* LEGACY */
HTSEXT_API void set_wrappers(httrackp *opt); /* LEGACY */
/** Load a plugin shared library and run its hts_plug(opt, argv) entry point. On
success the handle is recorded in opt and unloaded by hts_free_opt().
@return 1 if loaded and hts_plug succeeded; 0 if loaded but hts_plug was
missing or refused; -1 if the library could not be loaded. */
HTSEXT_API int plug_wrapper(httrackp * opt, const char *moduleName,
HTSEXT_API int plug_wrapper(httrackp *opt, const char *moduleName,
const char *argv);
/** Install the process-global assertion/error callback (NULL clears it). Not
@@ -212,12 +219,12 @@ HTSEXT_API hts_boolean hts_log(httrackp *opt, const char *prefix,
/** printf-style log at level @p type (an hts_log_type, optionally |LOG_ERRNO).
Forwards to the registered log callback, and when the level is <= opt->debug
also to opt->log. @p format must be non-NULL. */
HTSEXT_API void hts_log_print(httrackp * opt, int type, const char *format,
...) HTS_PRINTF_FUN(3, 4);
HTSEXT_API void hts_log_print(httrackp *opt, int type, const char *format, ...)
HTS_PRINTF_FUN(3, 4);
/** va_list form of hts_log_print(). @p opt may be NULL (only the callback
runs). Preserves errno. @p format must be non-NULL. */
HTSEXT_API void hts_log_vprint(httrackp * opt, int type, const char *format,
HTSEXT_API void hts_log_vprint(httrackp *opt, int type, const char *format,
va_list args);
/** Install the process-global log callback invoked by hts_log_vprint() for
@@ -231,7 +238,7 @@ hts_set_log_vprint_callback(void (*callback)(httrackp *opt, int type,
result is written into and aliases a 2048-byte scratch buffer inside opt: it
is valid until that buffer is next used, and must not be freed. opt must be
non-NULL. */
HTSEXT_API const char *hts_get_version_info(httrackp * opt);
HTSEXT_API const char *hts_get_version_info(httrackp *opt);
/** Static build-features string (TLS, zlib, ipv6, and so on). Process-global
storage; do not free or modify. */
@@ -241,21 +248,22 @@ HTSEXT_API const char *hts_is_available(void);
HTSEXT_API const char *hts_version(void);
/* Wrapper functions */
HTSEXT_API int htswrap_init(void); // DEPRECATED - DUMMY FUNCTION
HTSEXT_API int htswrap_init(void); // DEPRECATED - DUMMY FUNCTION
HTSEXT_API int htswrap_free(void); // DEPRECATED - DUMMY FUNCTION
HTSEXT_API int htswrap_free(void); // DEPRECATED - DUMMY FUNCTION
/** Register callback @p fct under @p name in opt's callback table (for example
"start", "check-html", "linkdetected"). Returns 1 on success, 0 if @p name
is not a known slot. Prefer CHAIN_FUNCTION(), which preserves any prior
callback. */
HTSEXT_API int htswrap_add(httrackp * opt, const char *name, void *fct);
HTSEXT_API int htswrap_add(httrackp *opt, const char *name, void *fct);
/** Return the function pointer registered under @p name in opt as a uintptr_t,
or 0 if none or unknown. */
HTSEXT_API uintptr_t htswrap_read(httrackp * opt, const char *name);
HTSEXT_API uintptr_t htswrap_read(httrackp *opt, const char *name);
/* Internal library allocators, if a different libc is being used by the client */
/* Internal library allocators, if a different libc is being used by the client
*/
/** strdup() through the library allocator. Returns a heap copy freed with
hts_free(), or NULL on failure. */
HTSEXT_API char *hts_strdup(const char *string);
@@ -272,13 +280,13 @@ HTSEXT_API void *hts_realloc(void *const data, const size_t size);
HTSEXT_API void hts_free(void *data);
/* Other functions */
HTSEXT_API int hts_resetvar(void); // DEPRECATED - DUMMY FUNCTION
HTSEXT_API int hts_resetvar(void); // DEPRECATED - DUMMY FUNCTION
/** (Re)build the top-level index.html aggregating every mirror project found
under @p path. @p binpath is the data root used to locate the
templates/topindex-*.html files, falling back to built-in templates. Writes
<path>/index.html. @return 1 on success, 0 on failure. */
HTSEXT_API int hts_buildtopindex(httrackp * opt, const char *path,
HTSEXT_API int hts_buildtopindex(httrackp *opt, const char *path,
const char *binpath);
/** Scan every mirror project under @p path and return a CRLF-separated list:
@@ -321,14 +329,14 @@ HTSEXT_API hts_boolean catch_url(T_SOC soc, char *url, char *method,
/** Whether the engine is parsing HTML. Returns 0 if not, otherwise the percent
done (at least 1). @p flag >= 0 also requests a progress refresh; pass a
negative value to query without side effects. */
HTSEXT_API int hts_is_parsing(httrackp * opt, int flag);
HTSEXT_API int hts_is_parsing(httrackp *opt, int flag);
/** Current background phase: 0 none, 1 testing links, 2 purge, 3, 4 scheduling,
5 waiting for a slot. */
HTSEXT_API int hts_is_testing(httrackp * opt);
HTSEXT_API int hts_is_testing(httrackp *opt);
/** Nonzero once the engine has begun its exit sequence. */
HTSEXT_API int hts_is_exiting(httrackp * opt);
HTSEXT_API int hts_is_exiting(httrackp *opt);
/*HTSEXT_API int hts_setopt(httrackp* opt); DEPRECATED ; see copy_htsopt() */
@@ -344,15 +352,15 @@ HTSEXT_API hts_boolean hts_resetaddurl(httrackp *opt);
/** Apply the runtime-tunable options from @p from onto @p to, to adjust a live
mirror. Only fields set to a non-sentinel value are copied; the rest of @p
to is left untouched. The user-agent string is deep-copied. @return 0. */
HTSEXT_API int copy_htsopt(const httrackp * from, httrackp * to);
HTSEXT_API int copy_htsopt(const httrackp *from, httrackp *to);
/** Return the engine's last error message, or NULL. The string is owned by
@p opt; do not free it, and use it only while @p opt lives. */
HTSEXT_API char *hts_errmsg(httrackp * opt);
HTSEXT_API char *hts_errmsg(httrackp *opt);
/** Get or set the transfer-pause flag. @p p >= 0 sets it (nonzero means
paused); a negative value queries. @return the current pause flag. */
HTSEXT_API int hts_setpause(httrackp * opt, int);
HTSEXT_API int hts_setpause(httrackp *opt, int);
/** Ask the running mirror to terminate (sets the stop flag under the state
lock, so it is safe to call from another thread). @p force is currently
@@ -363,15 +371,15 @@ HTSEXT_API int hts_request_stop(httrackp *opt, hts_boolean force);
/** Queue a single in-progress file, by URL, to be cancelled by the engine.
@p url is copied internally. Takes the state lock, so it is thread-safe.
@return the underlying push result. */
HTSEXT_API int hts_cancel_file_push(httrackp * opt, const char *url);
HTSEXT_API int hts_cancel_file_push(httrackp *opt, const char *url);
/** Cancel the in-progress link-testing phase. Effective only while a test runs.
*/
HTSEXT_API void hts_cancel_test(httrackp * opt);
HTSEXT_API void hts_cancel_test(httrackp *opt);
/** Cancel the in-progress HTML parsing. Effective only while parsing is active.
*/
HTSEXT_API void hts_cancel_parsing(httrackp * opt);
HTSEXT_API void hts_cancel_parsing(httrackp *opt);
/** Nonzero once the mirror has fully ended. Read under the engine state lock,
so safe to poll from another thread. Wait for this before hts_free_opt(). */
@@ -416,19 +424,19 @@ HTSEXT_API void qsec2str(char *st, TStamp t);
is reused, and a given strc is not reentrant. Use one strc per
concurrently-live result. */
/** Format @p n as a decimal string into @p strc and return it. */
HTSEXT_API char *int2char(strc_int2bytes2 * strc, int n);
HTSEXT_API char *int2char(strc_int2bytes2 *strc, int n);
/** Format byte count @p n as "<num><unit>" (B/KiB/MiB/GiB and so on) into
@p strc and return it. */
HTSEXT_API char *int2bytes(strc_int2bytes2 * strc, LLint n);
HTSEXT_API char *int2bytes(strc_int2bytes2 *strc, LLint n);
/** Format a transfer rate @p n as "<num><unit>/s" into @p strc and return it.
*/
HTSEXT_API char *int2bytessec(strc_int2bytes2 * strc, long int n);
HTSEXT_API char *int2bytessec(strc_int2bytes2 *strc, long int n);
/** Split byte count @p n into number and unit, returning a 2-element array
{number, unit} stored inside @p strc. */
HTSEXT_API char **int2bytes2(strc_int2bytes2 * strc, LLint n);
HTSEXT_API char **int2bytes2(strc_int2bytes2 *strc, LLint n);
/** Skip any "user[:pass]@" identification prefix in a URL, returning a pointer
into the argument past it (or past the protocol if none). The result aliases
@@ -490,40 +498,50 @@ HTSEXT_API void unescape_amp(char *s);
/** Percent-escape only spaces (' ' becomes "%20"); copy everything else
* verbatim. */
HTSEXT_API size_t escape_spc_url(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t escape_spc_url(const char *const src, char *const dest,
const size_t size);
/** Aggressively percent-escape @p src for use as a single URL path segment
(reserved, delimiter, unwise, special, avoid and mark characters). */
HTSEXT_API size_t escape_in_url(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t escape_in_url(const char *const src, char *const dest,
const size_t size);
/** Percent-escape @p src as a URI, escaping only what is necessary and keeping
'/' and other reserved characters. */
HTSEXT_API size_t escape_uri(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t escape_uri(const char *const src, char *const dest,
const size_t size);
/** Like escape_uri() for a UTF-8 URI: also escapes reserved characters other
than '/'. */
HTSEXT_API size_t escape_uri_utf(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t escape_uri_utf(const char *const src, char *const dest,
const size_t size);
/** Minimal "make safe" escape: percent-escapes only '"', ' ' and control
characters, leaving an already-formed URL otherwise intact. */
HTSEXT_API size_t escape_check_url(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t escape_check_url(const char *const src, char *const dest,
const size_t size);
/** Append-variant of escape_spc_url(): escapes @p src after the existing
NUL-terminated content of @p dest. Returns the bytes appended (excluding the
NUL). */
HTSEXT_API size_t append_escape_spc_url(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t append_escape_spc_url(const char *const src, char *const dest,
const size_t size);
/** Append-variant of escape_in_url(). See append_escape_spc_url(). */
HTSEXT_API size_t append_escape_in_url(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t append_escape_in_url(const char *const src, char *const dest,
const size_t size);
/** Append-variant of escape_uri(). See append_escape_spc_url(). */
HTSEXT_API size_t append_escape_uri(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t append_escape_uri(const char *const src, char *const dest,
const size_t size);
/** Append-variant of escape_uri_utf(). See append_escape_spc_url(). */
HTSEXT_API size_t append_escape_uri_utf(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t append_escape_uri_utf(const char *const src, char *const dest,
const size_t size);
/** Append-variant of escape_check_url(). See append_escape_spc_url(). */
HTSEXT_API size_t append_escape_check_url(const char *const src, char *const dest, const size_t size);
HTSEXT_API size_t append_escape_check_url(const char *const src,
char *const dest, const size_t size);
/** In-place variant of escape_spc_url(): escapes the NUL-terminated string in
@p dest back into @p dest. */
@@ -543,32 +561,39 @@ HTSEXT_API size_t inplace_escape_check_url(char *const dest, const size_t size);
/** Same escaping as escape_check_url() but returns @p dest instead of the byte
count. */
HTSEXT_API char *escape_check_url_addr(const char *const src, char *const dest, const size_t size);
HTSEXT_API char *escape_check_url_addr(const char *const src, char *const dest,
const size_t size);
/** Build a MIME/MHTML content-id token in @p dest from @p adr and @p fil:
escape_in_url() both, then replace every '%' with 'X' so the result is one
opaque token. */
HTSEXT_API size_t make_content_id(const char *const adr, const char *const fil, char *const dest, const size_t size);
HTSEXT_API size_t make_content_id(const char *const adr, const char *const fil,
char *const dest, const size_t size);
/** Low-level percent-escaper backing the escape_* family. @p mode selects the
character class to escape: 0 check_url, 1 in_url, 2 spc_url, 3 uri,
30 uri_utf. @p max_size is the dest capacity including the NUL. */
HTSEXT_API size_t x_escape_http(const char *const s, char *const dest, const size_t max_size, const int mode);
HTSEXT_API size_t x_escape_http(const char *const s, char *const dest,
const size_t max_size, const int mode);
/** Strip all control characters (byte value < 32) from @p s in place. */
HTSEXT_API void escape_remove_control(char *const s);
/** HTML-escape for text output: rewrite '&' to "&amp;" and pass every other
byte through unchanged. */
HTSEXT_API size_t escape_for_html_print(const char *const s, char *const dest, const size_t size);
HTSEXT_API size_t escape_for_html_print(const char *const s, char *const dest,
const size_t size);
/** Like escape_for_html_print() but also convert every high byte (>= 128) to a
numeric entity "&#xNN;". */
HTSEXT_API size_t escape_for_html_print_full(const char *const s, char *const dest, const size_t size);
HTSEXT_API size_t escape_for_html_print_full(const char *const s,
char *const dest,
const size_t size);
/** Percent-decode @p s into @p catbuff (capacity @p size) and return @p
catbuff. Decodes every "%xx" hex escape. */
HTSEXT_API char *unescape_http(char *const catbuff, const size_t size, const char *const s);
HTSEXT_API char *unescape_http(char *const catbuff, const size_t size,
const char *const s);
/** Percent-decode @p s into @p catbuff, but only the escapes that are safe to
decode while keeping a valid URI (reserved, delimiter, unwise, control and
@@ -589,17 +614,16 @@ HTSEXT_API hts_boolean get_httptype_sized(httrackp *opt, char *s, size_t ssize,
HTS_MIMETYPE_SIZE capacity. */
HTS_DEPRECATED("use get_httptype_sized(opt, s, ssize, fil, flag)")
HTSEXT_API void get_httptype(httrackp * opt, char *s, const char *fil,
int flag);
HTSEXT_API void get_httptype(httrackp *opt, char *s, const char *fil, int flag);
/** Classify @p fil by its extension: 0 unknown, 1 known non-HTML, 2 known HTML.
Consults the built-in table then user --assume rules. 0 for a NULL @p fil.
*/
HTSEXT_API int is_knowntype(httrackp * opt, const char *fil);
HTSEXT_API int is_knowntype(httrackp *opt, const char *fil);
/** Like is_knowntype() but consults only the user --assume rules: 0 no rule,
1 non-HTML, 2 HTML. */
HTSEXT_API int is_userknowntype(httrackp * opt, const char *fil);
HTSEXT_API int is_userknowntype(httrackp *opt, const char *fil);
/** 1 if @p fil, an extension such as "asp" or "php" (not a full filename), is a
known dynamic-page type, else 0. */
@@ -624,7 +648,7 @@ HTSEXT_API hts_boolean guess_httptype_sized(httrackp *opt, char *s,
HTS_MIMETYPE_SIZE capacity. */
HTS_DEPRECATED("use guess_httptype_sized(opt, s, ssize, fil)")
HTSEXT_API void guess_httptype(httrackp * opt, char *s, const char *fil);
HTSEXT_API void guess_httptype(httrackp *opt, char *s, const char *fil);
/* Ugly string tools */
/* These take a caller scratch buffer catbuff of capacity size and return it. On
@@ -633,11 +657,13 @@ HTSEXT_API void guess_httptype(httrackp * opt, char *s, const char *fil);
time), not a pointer. */
/** Concatenate @p a and @p b into @p catbuff (NULL or empty operands are
* skipped). */
HTSEXT_API char *concat(char *catbuff, size_t size, const char *a, const char *b);
HTSEXT_API char *concat(char *catbuff, size_t size, const char *a,
const char *b);
/** Like concat(a, b) but convert '/' to the platform path separator (Windows).
*/
HTSEXT_API char *fconcat(char *catbuff, size_t size, const char *a, const char *b);
HTSEXT_API char *fconcat(char *catbuff, size_t size, const char *a,
const char *b);
/** Copy @p a into @p catbuff, converting '/' to the platform path separator
(Windows). */
@@ -719,7 +745,7 @@ HTSEXT_API FILE *hts_fopen_utf8(const char *path, const char *mode);
#define STAT hts_stat_utf8
typedef struct _stat STRUCT_STAT;
HTSEXT_API int hts_stat_utf8(const char *path, STRUCT_STAT * buf);
HTSEXT_API int hts_stat_utf8(const char *path, STRUCT_STAT *buf);
#define UNLINK hts_unlink_utf8
HTSEXT_API int hts_unlink_utf8(const char *pathname);
@@ -731,12 +757,12 @@ HTSEXT_API int hts_rename_utf8(const char *oldpath, const char *newpath);
HTSEXT_API int hts_mkdir_utf8(const char *pathname);
#define UTIME(A,B) hts_utime_utf8(A,B)
#define UTIME(A, B) hts_utime_utf8(A, B)
typedef struct _utimbuf STRUCT_UTIMBUF;
HTSEXT_API int hts_utime_utf8(const char *filename,
const STRUCT_UTIMBUF * times);
const STRUCT_UTIMBUF *times);
#else
#define FOPEN fopen
#define STAT stat
@@ -748,7 +774,7 @@ typedef struct stat STRUCT_STAT;
typedef struct utimbuf STRUCT_UTIMBUF;
#define UTIME(A,B) utime(A,B)
#define UTIME(A, B) utime(A, B)
#endif
#define HTS_DEF_FILEAPI
#endif
@@ -756,20 +782,21 @@ typedef struct utimbuf STRUCT_UTIMBUF;
/** Macro aimed to break at build-time if a size is not a sizeof() strictly
* greater than sizeof(char*). **/
#undef COMPILE_TIME_CHECK_SIZE
#define COMPILE_TIME_CHECK_SIZE(A) (void) ((void (*)(char[A - sizeof(char*) - 1])) NULL)
#define COMPILE_TIME_CHECK_SIZE(A) \
(void) ((void (*)(char[A - sizeof(char *) - 1])) NULL)
/** Macro aimed to break at compile-time if a size is not a sizeof() strictly
* greater than sizeof(char*). **/
#undef RUNTIME_TIME_CHECK_SIZE
#define RUNTIME_TIME_CHECK_SIZE(A) assertf((A) != sizeof(void*))
#define RUNTIME_TIME_CHECK_SIZE(A) assertf((A) != sizeof(void *))
#define fconv(A,B,C) (COMPILE_TIME_CHECK_SIZE(B), fconv(A,B,C))
#define fconv(A, B, C) (COMPILE_TIME_CHECK_SIZE(B), fconv(A, B, C))
#define concat(A,B,C,D) (COMPILE_TIME_CHECK_SIZE(B), concat(A,B,C,D))
#define concat(A, B, C, D) (COMPILE_TIME_CHECK_SIZE(B), concat(A, B, C, D))
#define fconcat(A,B,C,D) (COMPILE_TIME_CHECK_SIZE(B), fconcat(A,B,C,D))
#define fconcat(A, B, C, D) (COMPILE_TIME_CHECK_SIZE(B), fconcat(A, B, C, D))
#define fslash(A,B,C) (COMPILE_TIME_CHECK_SIZE(B), fslash(A,B,C))
#define fslash(A, B, C) (COMPILE_TIME_CHECK_SIZE(B), fslash(A, B, C))
#ifdef __cplusplus
}