Compare commits

...

6 Commits

Author SHA1 Message Date
Xavier Roche
edd52bf3be Bound optalias_check's output buffers (thread their sizes)
optalias_check() wrote into caller-provided char* buffers with unchecked ops:
the param0 case did strcpybuff/strcatbuff of command+param into return_argv[0],
which can exceed the buffer, and the syntax-error paths sprintf()'d an option
name into return_error -- which is only 256 bytes in the config-file caller, so
a long option overflows it. Both are the overflow the audit flagged.

Thread return_argv_size and return_error_size through the (internal,
non-exported) signature; copy with strlcpybuff/strlcatbuff and format with
snprintf, so an over-long value aborts/truncates instead of overrunning. Update
both callers to pass their real sizes.

Leaves the shared cmdl_ins macro (the cmdl_* family wants its block size
threaded too -- a separate cleanup).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 13:47:12 +02:00
Xavier Roche
9eb2a344a9 Merge pull request #335 from xroche/cleanup/infostatuscode-const
Return HTTP status reason phrases via a const-returning switch
2026-06-14 13:18:16 +02:00
Xavier Roche
348a7d8cb2 Return HTTP status reason phrases via a const-returning switch
infostatuscode() was a ~60-case switch, each arm strcpybuff()-ing a literal into
the caller's char* msg: 42 unchecked pointer-destination copies of static data.
Keep the same O(1) switch dispatch but have it return the phrase instead of
copying -- new public infostatuscode_const(int) -> const char* (or NULL) -- and
do the copy in a thin wrapper.

infostatuscode() preserves exact behavior: a known code overwrites msg; an
unknown code keeps any caller-provided message, else writes "Unknown error".
The single remaining copy uses strlcpybuff with the documented 64-byte minimum
(longest phrase is 31; all callers pass >= 80).

Drops 42 pointer-destination warnings (htslib.c 56 -> 14; tree 179 -> 137).
No dispatch regression: it stays a switch (jump table), no allocation, no
per-call scan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 13:14:23 +02:00
Xavier Roche
5f81741ac5 Merge pull request #332 from xroche/cleanup/url_savename-htsbuff
Convert the url_savename template renderer to htsbuff
2026-06-14 13:01:32 +02:00
Xavier Roche
0cf14c4e88 Convert the url_savename template renderer to htsbuff
The savename_type == -1 userdef renderer walked afs->save with a raw char*
cursor, doing "b += strlen(b)" after each write, and strcpybuff(b, ...) on that
char* was unchecked (the pointer-destination case). That manual pointer math is
where the function's off-by-one / strlen-based hazards lived.

Convert the cursor to an htsbuff over afs->save (capacity sizeof = the full
HTS_URLMAXSIZE*2 buffer): every append is now bounds-checked and the pointer
math is gone. The loop's truncation guard becomes "sb.len < HTS_URLMAXSIZE",
preserving the existing cap-at-1024 behavior; the 2x buffer means a write only
aborts where it would previously have overrun. Add htsbuff_catc for the
single-character appends ('%', '.', literal copy).

Removes 35 pointer-destination warnings (htsname.c 51 -> 9; the renderer is now
warning-free). Behavior verified identical: the pre-change and new binaries
produce byte-identical output across 14 -N templates (%n %N %t %p %h %H %M %q %r
%% %[param], the short %s variants, and literals) crawling a local site.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 12:59:29 +02:00
Xavier Roche
29a07ff487 Merge pull request #334 from xroche/cleanup/git-format-hook
Add an opt-in pre-commit hook that auto-formats changed C lines
2026-06-14 12:58:42 +02:00
7 changed files with 180 additions and 227 deletions

View File

@@ -266,7 +266,9 @@ const char *hts_optalias[][4] = {
return value: number of arguments treated (0 if error)
*/
int optalias_check(int argc, const char *const *argv, int n_arg,
int *return_argc, char **return_argv, char *return_error) {
int *return_argc, char **return_argv,
size_t return_argv_size, char *return_error,
size_t return_error_size) {
return_error[0] = '\0';
*return_argc = 1;
if (argv[n_arg][0] == '-')
@@ -323,9 +325,10 @@ int optalias_check(int argc, const char *const *argv, int n_arg,
/* Copy parameters? */
if (need_param == 2) {
if ((n_arg + 1 >= argc) || (argv[n_arg + 1][0] == '-')) { /* no supplemental parameter */
sprintf(return_error,
"Syntax error:\n\tOption %s needs to be followed by a parameter: %s <param>\n\t%s\n",
command, command, _NOT_NULL(optalias_help(command)));
snprintf(return_error, return_error_size,
"Syntax error:\n\tOption %s needs to be followed by a "
"parameter: %s <param>\n\t%s\n",
command, command, _NOT_NULL(optalias_help(command)));
return 0;
}
strcpybuff(param, argv[n_arg + 1]);
@@ -338,35 +341,36 @@ int optalias_check(int argc, const char *const *argv, int n_arg,
/* Must be alone (-P /tmp) */
if (strcmp(hts_optalias[pos][2], "param1") == 0) {
strcpybuff(return_argv[0], command);
strcpybuff(return_argv[1], param);
strlcpybuff(return_argv[0], command, return_argv_size);
strlcpybuff(return_argv[1], param, return_argv_size);
*return_argc = 2; /* 2 parameters returned */
}
/* Alone with parameter (+*.gif) */
else if (strcmp(hts_optalias[pos][2], "param0") == 0) {
/* Command */
strcpybuff(return_argv[0], command);
strcatbuff(return_argv[0], param);
strlcpybuff(return_argv[0], command, return_argv_size);
strlcatbuff(return_argv[0], param, return_argv_size);
}
/* Together (-c8) */
else {
/* Command */
strcpybuff(return_argv[0], command);
strlcpybuff(return_argv[0], command, return_argv_size);
/* Parameters accepted */
if (strncmp(hts_optalias[pos][2], "param", 5) == 0) {
/* --cache=off or --index=on */
if (strcmp(param, "off") == 0)
strcatbuff(return_argv[0], "0");
strlcatbuff(return_argv[0], "0", return_argv_size);
else if (strcmp(param, "on") == 0) {
// on is the default
// strcatbuff(return_argv[0],"1");
} else
strcatbuff(return_argv[0], param);
strlcatbuff(return_argv[0], param, return_argv_size);
}
*return_argc = 1; /* 1 parameter returned */
}
} else {
sprintf(return_error, "Unknown option: %s\n", command);
snprintf(return_error, return_error_size, "Unknown option: %s\n",
command);
return 0;
}
return need_param;
@@ -380,15 +384,16 @@ int optalias_check(int argc, const char *const *argv, int n_arg,
if ((strcmp(hts_optalias[pos][2], "param1") == 0)
|| (strcmp(hts_optalias[pos][2], "param0") == 0)) {
if ((n_arg + 1 >= argc) || (argv[n_arg + 1][0] == '-')) { /* no supplemental parameter */
sprintf(return_error,
"Syntax error:\n\tOption %s needs to be followed by a parameter: %s <param>\n\t%s\n",
argv[n_arg], argv[n_arg],
_NOT_NULL(optalias_help(argv[n_arg])));
snprintf(return_error, return_error_size,
"Syntax error:\n\tOption %s needs to be followed by a "
"parameter: %s <param>\n\t%s\n",
argv[n_arg], argv[n_arg],
_NOT_NULL(optalias_help(argv[n_arg])));
return 0;
}
/* Copy parameters */
strcpybuff(return_argv[0], argv[n_arg]);
strcpybuff(return_argv[1], argv[n_arg + 1]);
strlcpybuff(return_argv[0], argv[n_arg], return_argv_size);
strlcpybuff(return_argv[1], argv[n_arg + 1], return_argv_size);
/* And return */
*return_argc = 2; /* 2 parameters returned */
return 2; /* 2 parameters used */
@@ -397,7 +402,7 @@ int optalias_check(int argc, const char *const *argv, int n_arg,
}
/* Copy and return other unknown option */
strcpybuff(return_argv[0], argv[n_arg]);
strlcpybuff(return_argv[0], argv[n_arg], return_argv_size);
return 1;
}
@@ -524,9 +529,10 @@ int optinclude_file(const char *name, int *argc, char **argv, char *x_argvblk,
strcatbuff(_tmp_argv[0], a);
strcpybuff(_tmp_argv[1], b);
result =
optalias_check(2, (const char *const *) tmp_argv, 0, &return_argc,
(tmp_argv + 2), return_error);
result = optalias_check(2, (const char *const *) tmp_argv, 0,
&return_argc, (tmp_argv + 2),
sizeof(_tmp_argv[0]), return_error,
sizeof(return_error));
if (!result) {
printf("%s\n", return_error);
} else {

View File

@@ -38,7 +38,9 @@ Please visit our Website: http://www.httrack.com
#ifdef HTS_INTERNAL_BYTECODE
extern const char *hts_optalias[][4];
int optalias_check(int argc, const char *const *argv, int n_arg,
int *return_argc, char **return_argv, char *return_error);
int *return_argc, char **return_argv,
size_t return_argv_size, char *return_error,
size_t return_error_size);
int optalias_find(const char *token);
const char *optalias_help(const char *token);
int optreal_find(const char *token);

View File

@@ -211,6 +211,10 @@ static int string_safety_selftests(void) {
htsbuff_cpy(&b, "xyz"); /* reset */
if (strcmp(htsbuff_str(&b), "xyz") != 0 || b.len != 3)
return 1;
htsbuff_catc(&b, '!'); /* single character */
if (strcmp(htsbuff_str(&b), "xyz!") != 0 || b.len != 4)
return 1;
}
/* boundary: filling to exactly cap-1 must succeed (one more aborts, which the
@@ -381,10 +385,10 @@ static int hts_main_internal(int argc, char **argv, httrackp * opt) {
/* Vérifier argv[] non vide */
if (strnotempty(argv[na])) {
/* Vérifier Commande (alias) */
result =
optalias_check(argc, (const char *const *) argv, na, &tmp_argc,
(char **) tmp_argv, tmp_error);
/* Resolve an option alias, if any */
result = optalias_check(argc, (const char *const *) argv, na, &tmp_argc,
(char **) tmp_argv, sizeof(_tmp_argv[0]),
tmp_error, sizeof(tmp_error));
if (!result) {
HTS_PANIC_PRINTF(tmp_error);
htsmain_free();

View File

@@ -1660,138 +1660,107 @@ void treathead(t_cookie * cookie, const char *adr, const char *fil, htsblk * ret
}
}
// transforme le message statuscode en chaîne
HTSEXT_API void infostatuscode(char *msg, int statuscode) {
// HTTP status code -> reason phrase (per RFC), or NULL if unknown.
HTSEXT_API const char *infostatuscode_const(int statuscode) {
// O(1) dispatch (the compiler builds a jump table); the phrases are static.
switch (statuscode) {
// Erreurs HTTP, selon RFC
case 100:
strcpybuff(msg, "Continue");
break;
return "Continue";
case 101:
strcpybuff(msg, "Switching Protocols");
break;
return "Switching Protocols";
case 200:
strcpybuff(msg, "OK");
break;
return "OK";
case 201:
strcpybuff(msg, "Created");
break;
return "Created";
case 202:
strcpybuff(msg, "Accepted");
break;
return "Accepted";
case 203:
strcpybuff(msg, "Non-Authoritative Information");
break;
return "Non-Authoritative Information";
case 204:
strcpybuff(msg, "No Content");
break;
return "No Content";
case 205:
strcpybuff(msg, "Reset Content");
break;
return "Reset Content";
case 206:
strcpybuff(msg, "Partial Content");
break;
return "Partial Content";
case 300:
strcpybuff(msg, "Multiple Choices");
break;
return "Multiple Choices";
case 301:
strcpybuff(msg, "Moved Permanently");
break;
return "Moved Permanently";
case 302:
strcpybuff(msg, "Moved Temporarily");
break;
return "Moved Temporarily";
case 303:
strcpybuff(msg, "See Other");
break;
return "See Other";
case 304:
strcpybuff(msg, "Not Modified");
break;
return "Not Modified";
case 305:
strcpybuff(msg, "Use Proxy");
break;
return "Use Proxy";
case 306:
strcpybuff(msg, "Undefined 306 error");
break;
return "Undefined 306 error";
case 307:
strcpybuff(msg, "Temporary Redirect");
break;
return "Temporary Redirect";
case 400:
strcpybuff(msg, "Bad Request");
break;
return "Bad Request";
case 401:
strcpybuff(msg, "Unauthorized");
break;
return "Unauthorized";
case 402:
strcpybuff(msg, "Payment Required");
break;
return "Payment Required";
case 403:
strcpybuff(msg, "Forbidden");
break;
return "Forbidden";
case 404:
strcpybuff(msg, "Not Found");
break;
return "Not Found";
case 405:
strcpybuff(msg, "Method Not Allowed");
break;
return "Method Not Allowed";
case 406:
strcpybuff(msg, "Not Acceptable");
break;
return "Not Acceptable";
case 407:
strcpybuff(msg, "Proxy Authentication Required");
break;
return "Proxy Authentication Required";
case 408:
strcpybuff(msg, "Request Time-out");
break;
return "Request Time-out";
case 409:
strcpybuff(msg, "Conflict");
break;
return "Conflict";
case 410:
strcpybuff(msg, "Gone");
break;
return "Gone";
case 411:
strcpybuff(msg, "Length Required");
break;
return "Length Required";
case 412:
strcpybuff(msg, "Precondition Failed");
break;
return "Precondition Failed";
case 413:
strcpybuff(msg, "Request Entity Too Large");
break;
return "Request Entity Too Large";
case 414:
strcpybuff(msg, "Request-URI Too Large");
break;
return "Request-URI Too Large";
case 415:
strcpybuff(msg, "Unsupported Media Type");
break;
return "Unsupported Media Type";
case 416:
strcpybuff(msg, "Requested Range Not Satisfiable");
break;
return "Requested Range Not Satisfiable";
case 417:
strcpybuff(msg, "Expectation Failed");
break;
return "Expectation Failed";
case 500:
strcpybuff(msg, "Internal Server Error");
break;
return "Internal Server Error";
case 501:
strcpybuff(msg, "Not Implemented");
break;
return "Not Implemented";
case 502:
strcpybuff(msg, "Bad Gateway");
break;
return "Bad Gateway";
case 503:
strcpybuff(msg, "Service Unavailable");
break;
return "Service Unavailable";
case 504:
strcpybuff(msg, "Gateway Time-out");
break;
return "Gateway Time-out";
case 505:
strcpybuff(msg, "HTTP Version Not Supported");
break;
//
return "HTTP Version Not Supported";
default:
if (strnotempty(msg) == 0)
strcpybuff(msg, "Unknown error");
break;
return NULL;
}
}
// Write the status code's reason phrase into msg. For an unknown code, keep any
// caller-provided message, otherwise fall back to a default. Callers provide a
// buffer of at least 64 bytes (the longest reason phrase is 31).
HTSEXT_API void infostatuscode(char *msg, int statuscode) {
const char *const text = infostatuscode_const(statuscode);
if (text != NULL) {
strlcpybuff(msg, text, 64);
} else if (strnotempty(msg) == 0) {
strlcpybuff(msg, "Unknown error", 64);
}
}

View File

@@ -767,7 +767,7 @@ int url_savename(lien_adrfilsave *const afs,
// ajouter nom du site éventuellement en premier
if (opt->savename_type == -1) { // utiliser savename_userdef! (%h%p/%n%q.%t)
const char *a = StringBuff(opt->savename_userdef);
char *b = afs->save;
htsbuff sb = htsbuff_array(afs->save);
/*char *nom_pos=NULL,*dot_pos=NULL; // Position nom et point */
char tok;
@@ -787,17 +787,16 @@ int url_savename(lien_adrfilsave *const afs,
}
*/
// Construire nom
while((*a) && (((int) (b - afs->save)) < HTS_URLMAXSIZE)) { // parser, et pas trop long..
// build the name
while ((*a) && (sb.len < HTS_URLMAXSIZE)) { // parse, but not too long
if (*a == '%') {
int short_ver = 0;
a++;
if (*a == 's') {
if (*a == 's') { // '%s...' selects the short (8.3) form
short_ver = 1;
a++;
}
*b = '\0';
switch (tok = *a++) {
case '[': // %[param:prefix_if_not_empty:suffix_if_not_empty:empty_replacement:notfound_replacement]
if (strchr(a, ']')) {
@@ -834,8 +833,7 @@ int url_savename(lien_adrfilsave *const afs,
}
if (cp) {
c = cp + strlen(name[0]); /* jumps "param=" */
strcpybuff(b, name[1]); /* prefix */
b += strlen(b);
htsbuff_cat(&sb, name[1]); /* prefix */
if (*c != '\0' && *c != '&') {
char *d = name[0];
@@ -846,110 +844,90 @@ int url_savename(lien_adrfilsave *const afs,
*d = '\0';
d = unescape_http(catbuff, sizeof(catbuff), name[0]);
if (d && *d) {
strcpybuff(b, d); /* value */
b += strlen(b);
htsbuff_cat(&sb, d); /* value */
} else {
strcpybuff(b, name[3]); /* empty replacement if any */
b += strlen(b);
htsbuff_cat(&sb, name[3]); /* empty replacement if any */
}
} else {
strcpybuff(b, name[3]); /* empty replacement if any */
b += strlen(b);
htsbuff_cat(&sb, name[3]); /* empty replacement if any */
}
strcpybuff(b, name[2]); /* suffix */
b += strlen(b);
htsbuff_cat(&sb, name[2]); /* suffix */
} else {
strcpybuff(b, name[4]); /* not found replacement if any */
b += strlen(b);
htsbuff_cat(&sb, name[4]); /* not found replacement if any */
}
} else {
strcpybuff(b, name[4]); /* not found replacement if any */
b += strlen(b);
htsbuff_cat(&sb, name[4]); /* not found replacement if any */
}
}
break;
case '%':
*b++ = '%';
htsbuff_catc(&sb, '%');
break;
case 'n': // nom sans ext
*b = '\0';
case 'n': // name without extension
if (dot_pos) {
if (!short_ver) // Noms longs
strncatbuff(b, nom_pos, (int) (dot_pos - nom_pos));
if (!short_ver)
htsbuff_catn(&sb, nom_pos, (int) (dot_pos - nom_pos));
else
strncatbuff(b, nom_pos, min((int) (dot_pos - nom_pos), 8));
htsbuff_catn(&sb, nom_pos, min((int) (dot_pos - nom_pos), 8));
} else {
if (!short_ver) // Noms longs
strcpybuff(b, nom_pos);
if (!short_ver)
htsbuff_cat(&sb, nom_pos);
else
strncatbuff(b, nom_pos, 8);
htsbuff_catn(&sb, nom_pos, 8);
}
b += strlen(b); // pointer à la fin
break;
case 'N': // nom avec ext
// RECOPIE NOM + EXT
*b = '\0';
case 'N': // name with extension
if (dot_pos) {
if (!short_ver) // Noms longs
strncatbuff(b, nom_pos, (int) (dot_pos - nom_pos));
if (!short_ver)
htsbuff_catn(&sb, nom_pos, (int) (dot_pos - nom_pos));
else
strncatbuff(b, nom_pos, min((int) (dot_pos - nom_pos), 8));
htsbuff_catn(&sb, nom_pos, min((int) (dot_pos - nom_pos), 8));
} else {
if (!short_ver) // Noms longs
strcpybuff(b, nom_pos);
if (!short_ver)
htsbuff_cat(&sb, nom_pos);
else
strncatbuff(b, nom_pos, 8);
htsbuff_catn(&sb, nom_pos, 8);
}
b += strlen(b); // pointer à la fin
*b = '.';
++b;
// RECOPIE NOM + EXT
*b = '\0';
htsbuff_catc(&sb, '.');
if (dot_pos) {
if (!short_ver) // Noms longs
strcpybuff(b, dot_pos + 1);
if (!short_ver)
htsbuff_cat(&sb, dot_pos + 1);
else
strncatbuff(b, dot_pos + 1, 3);
htsbuff_catn(&sb, dot_pos + 1, 3);
} else {
if (!short_ver) // Noms longs
strcpybuff(b, DEFAULT_EXT + 1); // pas de..
if (!short_ver)
htsbuff_cat(&sb, DEFAULT_EXT + 1); // skip the leading dot
else
strcpybuff(b, DEFAULT_EXT_SHORT + 1); // pas de..
htsbuff_cat(&sb, DEFAULT_EXT_SHORT + 1); // skip the leading dot
}
b += strlen(b); // pointer à la fin
//
break;
case 't': // ext
*b = '\0';
case 't': // extension
if (dot_pos) {
if (!short_ver) // Noms longs
strcpybuff(b, dot_pos + 1);
if (!short_ver)
htsbuff_cat(&sb, dot_pos + 1);
else
strncatbuff(b, dot_pos + 1, 3);
htsbuff_catn(&sb, dot_pos + 1, 3);
} else {
if (!short_ver) // Noms longs
strcpybuff(b, DEFAULT_EXT + 1); // pas de..
if (!short_ver)
htsbuff_cat(&sb, DEFAULT_EXT + 1); // skip the leading dot
else
strcpybuff(b, DEFAULT_EXT_SHORT + 1); // pas de..
htsbuff_cat(&sb, DEFAULT_EXT_SHORT + 1); // skip the leading dot
}
b += strlen(b); // pointer à la fin
break;
case 'p': // path sans dernier /
*b = '\0';
if (nom_pos != fil + 1) { // pas: /index.html (chemin nul)
if (!short_ver) { // Noms longs
strncatbuff(b, fil, (int) (nom_pos - fil) - 1);
case 'p': // path without trailing /
if (nom_pos !=
fil + 1) { // skip when the path is empty (e.g. /index.html)
if (!short_ver) {
htsbuff_catn(&sb, fil, (int) (nom_pos - fil) - 1);
} else {
char BIGSTK pth[HTS_URLMAXSIZE * 2], n83[HTS_URLMAXSIZE * 2];
pth[0] = n83[0] = '\0';
//
strncatbuff(pth, fil, (int) (nom_pos - fil) - 1);
long_to_83(opt->savename_83, n83, pth);
strcpybuff(b, n83);
htsbuff_cat(&sb, n83);
}
}
b += strlen(b); // pointer à la fin
break;
case 'h': // host (IDNA decoded if suitable)
// IDNA / RFC 3492 (Punycode) handling for HTTP(s)
@@ -957,62 +935,50 @@ int url_savename(lien_adrfilsave *const afs,
DECLARE_ADR(final_adr);
/* Copy address */
*b = '\0';
if (!short_ver)
strcpybuff(b, final_adr);
htsbuff_cat(&sb, final_adr);
else
strcpybuff(b, final_adr);
htsbuff_cat(&sb, final_adr);
/* release */
RELEASE_ADR();
}
b += strlen(b); // pointer à la fin
break;
case 'H': // host, raw (old mode)
*b = '\0';
case 'H': // host, raw (old mode)
if (protocol == PROTOCOL_FILE) {
if (!short_ver) // Noms longs
strcpybuff(b, "localhost");
if (!short_ver)
htsbuff_cat(&sb, "localhost");
else
strcpybuff(b, "local");
htsbuff_cat(&sb, "local");
} else {
if (!short_ver) // Noms longs
strcpybuff(b, print_adr);
if (!short_ver)
htsbuff_cat(&sb, print_adr);
else
strncatbuff(b, print_adr, 8);
htsbuff_catn(&sb, print_adr, 8);
}
b += strlen(b); // pointer à la fin
break;
case 'M': /* host/address?query MD5 (128-bits) */
*b = '\0';
{
char digest[32 + 2];
char BIGSTK buff[HTS_URLMAXSIZE * 2];
case 'M': /* host/address?query MD5 (128-bits) */
{
char digest[32 + 2];
char BIGSTK buff[HTS_URLMAXSIZE * 2];
digest[0] = buff[0] = '\0';
strcpybuff(buff, adr);
strcatbuff(buff, fil_complete);
domd5mem(buff, strlen(buff), digest, 1);
strcpybuff(b, digest);
}
b += strlen(b); // pointer à la fin
break;
digest[0] = buff[0] = '\0';
strcpybuff(buff, adr);
strcatbuff(buff, fil_complete);
domd5mem(buff, strlen(buff), digest, 1);
htsbuff_cat(&sb, digest);
} break;
case 'Q':
case 'q': /* query MD5 (128-bits/16-bits)
GENERATED ONLY IF query string exists! */
{
char md5[32 + 2];
case 'q': /* query MD5 (128-bits/16-bits)
GENERATED ONLY IF query string exists! */
{
char md5[32 + 2];
*b = '\0';
strncatbuff(b, url_md5(md5, fil_complete), (tok == 'Q') ? 32 : 4);
b += strlen(b); // pointer à la fin
}
break;
htsbuff_catn(&sb, url_md5(md5, fil_complete), (tok == 'Q') ? 32 : 4);
} break;
case 'r':
case 'R': // protocol
*b = '\0';
strcatbuff(b, protocol_str[protocol]);
b += strlen(b); // pointer à la fin
htsbuff_cat(&sb, protocol_str[protocol]);
break;
/* Patch by Juan Fco Rodriguez to get the full query string */
@@ -1021,19 +987,17 @@ int url_savename(lien_adrfilsave *const afs,
char *d = strchr(fil_complete, '?');
if (d != NULL) {
strcatbuff(b, d);
b += strlen(b);
htsbuff_cat(&sb, d);
}
}
break;
}
} else
*b++ = *a++;
htsbuff_catc(&sb, *a++);
}
*b++ = '\0';
//
// Types prédéfinis
// predefined types
//
}

View File

@@ -351,6 +351,13 @@ static HTS_INLINE HTS_UNUSED void htsbuff_cat(htsbuff *b, const char *s) {
htsbuff_catn(b, s, (size_t) -1);
}
/** Append a single character (including '\0' as data). Aborts on overflow. */
static HTS_INLINE HTS_UNUSED void htsbuff_catc(htsbuff *b, char c) {
assertf__(1 < b->cap - b->len, "htsbuff append overflow", __FILE__, __LINE__);
b->buf[b->len++] = c;
b->buf[b->len] = '\0';
}
/** Reset content to s. Aborts on overflow. */
static HTS_INLINE HTS_UNUSED void htsbuff_cpy(htsbuff *b, const char *s) {
b->len = 0;

View File

@@ -193,6 +193,7 @@ HTSEXT_API int structcheck(const char *path);
HTSEXT_API int structcheck_utf8(const char *path);
HTSEXT_API int dir_exists(const char *path);
HTSEXT_API void infostatuscode(char *msg, int statuscode);
HTSEXT_API const char *infostatuscode_const(int statuscode);
HTSEXT_API TStamp mtime_local(void);
HTSEXT_API void qsec2str(char *st, TStamp t);
HTSEXT_API char *int2char(strc_int2bytes2 * strc, int n);