mirror of
https://github.com/curl/curl.git
synced 2026-01-18 17:21:26 +01:00
tool: support fractions for --limit-rate and --max-filesize
Allow 2.5k or 3.7M etc. Add mention in documentation. Verify in test case 1623. Closes #20266
This commit is contained in:
@@ -12,7 +12,7 @@ See-also:
|
|||||||
- speed-limit
|
- speed-limit
|
||||||
- speed-time
|
- speed-time
|
||||||
Example:
|
Example:
|
||||||
- --limit-rate 100K $URL
|
- --limit-rate 123.45K $URL
|
||||||
- --limit-rate 1000 $URL
|
- --limit-rate 1000 $URL
|
||||||
- --limit-rate 10M $URL
|
- --limit-rate 10M $URL
|
||||||
- --limit-rate 200K --max-time 60 $URL
|
- --limit-rate 200K --max-time 60 $URL
|
||||||
@@ -27,8 +27,8 @@ otherwise would be.
|
|||||||
|
|
||||||
The given speed is measured in bytes/second, unless a suffix is appended.
|
The given speed is measured in bytes/second, unless a suffix is appended.
|
||||||
Appending 'k' or 'K' counts the number as kilobytes, 'm' or 'M' makes it
|
Appending 'k' or 'K' counts the number as kilobytes, 'm' or 'M' makes it
|
||||||
megabytes, while 'g' or 'G' makes it gigabytes. The suffixes (k, M, G, T, P)
|
megabytes etc. The supported suffixes (k, M, G, T, P) are 1024-based. For
|
||||||
are 1024 based. For example 1k is 1024. Examples: 200K, 3m and 1G.
|
example 1k is 1024. Examples: 200K, 3m and 1G.
|
||||||
|
|
||||||
The rate limiting logic works on averaging the transfer speed to no more than
|
The rate limiting logic works on averaging the transfer speed to no more than
|
||||||
the set threshold over a period of multiple seconds.
|
the set threshold over a period of multiple seconds.
|
||||||
@@ -36,3 +36,7 @@ the set threshold over a period of multiple seconds.
|
|||||||
If you also use the --speed-limit option, that option takes precedence and
|
If you also use the --speed-limit option, that option takes precedence and
|
||||||
might cripple the rate-limiting slightly, to help keep the speed-limit
|
might cripple the rate-limiting slightly, to help keep the speed-limit
|
||||||
logic working.
|
logic working.
|
||||||
|
|
||||||
|
Starting in curl 8.19.0, the rate can be specified using a fraction as in
|
||||||
|
`2.5M` for two and a half megabytes per second. It only works with a period
|
||||||
|
(`.`) delimiter, independent of what your locale might prefer.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ See-also:
|
|||||||
- limit-rate
|
- limit-rate
|
||||||
Example:
|
Example:
|
||||||
- --max-filesize 100K $URL
|
- --max-filesize 100K $URL
|
||||||
|
- --max-filesize 2.6M $URL
|
||||||
---
|
---
|
||||||
|
|
||||||
# `--max-filesize`
|
# `--max-filesize`
|
||||||
@@ -22,9 +23,9 @@ transfer does not start and curl returns with exit code 63.
|
|||||||
|
|
||||||
Setting the maximum value to zero disables the limit.
|
Setting the maximum value to zero disables the limit.
|
||||||
|
|
||||||
A size modifier may be used. For example, Appending 'k' or 'K' counts the
|
A unit suffix letter can be used. Appending 'k' or 'K' counts the number as
|
||||||
number as kilobytes, 'm' or 'M' makes it megabytes, while 'g' or 'G' makes it
|
kilobytes, 'm' or 'M' makes it megabytes etc. The supported suffixes (k, M, G,
|
||||||
gigabytes. Examples: 200K, 3m and 1G. (Added in 7.58.0)
|
T, P) are 1024-based. Examples: 200K, 3m and 1G. (Added in 7.58.0)
|
||||||
|
|
||||||
**NOTE**: before curl 8.4.0, when the file size is not known prior to
|
**NOTE**: before curl 8.4.0, when the file size is not known prior to
|
||||||
download, for such files this option has no effect even if the file transfer
|
download, for such files this option has no effect even if the file transfer
|
||||||
@@ -32,3 +33,7 @@ ends up being larger than this given limit.
|
|||||||
|
|
||||||
Starting with curl 8.4.0, this option aborts the transfer if it reaches the
|
Starting with curl 8.4.0, this option aborts the transfer if it reaches the
|
||||||
threshold during transfer.
|
threshold during transfer.
|
||||||
|
|
||||||
|
Starting in curl 8.19.0, the maximum size can be specified using a fraction as
|
||||||
|
in `2.5M` for two and a half megabytes. It only works with a period (`.`)
|
||||||
|
delimiter, independent of what your locale might prefer.
|
||||||
|
|||||||
@@ -534,54 +534,93 @@ static ParameterError GetFileAndPassword(const char *nextarg, char **file,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sizeunit {
|
||||||
|
char unit; /* single lowercase ASCII letter */
|
||||||
|
curl_off_t mul;
|
||||||
|
size_t mlen; /* number of digits in 'mul', when written in decimal */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sizeunit *getunit(char unit)
|
||||||
|
{
|
||||||
|
static const struct sizeunit list[] = {
|
||||||
|
{'p', (curl_off_t)1125899906842624, 16 }, /* Peta */
|
||||||
|
{'t', (curl_off_t)1099511627776, 13 }, /* Tera */
|
||||||
|
{'g', 1073741824, 10 }, /* Giga */
|
||||||
|
{'m', 1048576, 7 }, /* Mega */
|
||||||
|
{'k', 1024, 4 }, /* Kilo */
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
for(i = 0; i < CURL_ARRAYSIZE(list); i++)
|
||||||
|
if((unit | 0x20) == list[i].unit)
|
||||||
|
return &list[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get a size parameter for '--limit-rate' or '--max-filesize'.
|
/* Get a size parameter for '--limit-rate' or '--max-filesize'.
|
||||||
* We support a 'G', 'M' or 'K' suffix too.
|
We support P, T, G, M and K (case insensitive) suffixes.
|
||||||
|
|
||||||
|
Unit test 1623
|
||||||
*/
|
*/
|
||||||
static ParameterError GetSizeParameter(const char *arg,
|
UNITTEST ParameterError GetSizeParameter(const char *arg, curl_off_t *out)
|
||||||
const char *which,
|
|
||||||
curl_off_t *value_out)
|
|
||||||
{
|
{
|
||||||
const char *unit = arg;
|
const char *unit = arg;
|
||||||
curl_off_t value;
|
curl_off_t value;
|
||||||
|
curl_off_t prec = 0;
|
||||||
|
size_t plen = 0;
|
||||||
|
curl_off_t add = 0;
|
||||||
|
curl_off_t mul = 1;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if(curlx_str_number(&unit, &value, CURL_OFF_T_MAX)) {
|
rc = curlx_str_number(&unit, &value, CURL_OFF_T_MAX);
|
||||||
warnf("invalid number specified for %s", which);
|
if(rc == STRE_OVERFLOW)
|
||||||
return PARAM_BAD_USE;
|
return PARAM_NUMBER_TOO_LARGE;
|
||||||
|
else if(rc)
|
||||||
|
return PARAM_BAD_NUMERIC;
|
||||||
|
|
||||||
|
if(!curlx_str_single(&unit, '.')) {
|
||||||
|
const char *s = unit;
|
||||||
|
if(curlx_str_number(&unit, &prec, CURL_OFF_T_MAX))
|
||||||
|
return PARAM_BAD_NUMERIC;
|
||||||
|
plen = unit - s;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!*unit)
|
if(strlen(unit) > 1)
|
||||||
unit = "b";
|
|
||||||
else if(strlen(unit) > 1)
|
|
||||||
unit = "w"; /* unsupported */
|
|
||||||
|
|
||||||
switch(*unit) {
|
|
||||||
case 'G':
|
|
||||||
case 'g':
|
|
||||||
if(value > (CURL_OFF_T_MAX / (1024 * 1024 * 1024)))
|
|
||||||
return PARAM_NUMBER_TOO_LARGE;
|
|
||||||
value *= 1024 * 1024 * 1024;
|
|
||||||
break;
|
|
||||||
case 'M':
|
|
||||||
case 'm':
|
|
||||||
if(value > (CURL_OFF_T_MAX / (1024 * 1024)))
|
|
||||||
return PARAM_NUMBER_TOO_LARGE;
|
|
||||||
value *= 1024 * 1024;
|
|
||||||
break;
|
|
||||||
case 'K':
|
|
||||||
case 'k':
|
|
||||||
if(value > (CURL_OFF_T_MAX / 1024))
|
|
||||||
return PARAM_NUMBER_TOO_LARGE;
|
|
||||||
value *= 1024;
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
case 'B':
|
|
||||||
/* for plain bytes, leave as-is */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
warnf("unsupported %s unit. Use G, M, K or B", which);
|
|
||||||
return PARAM_BAD_USE;
|
return PARAM_BAD_USE;
|
||||||
|
else if(!*unit || ((*unit | 0x20) == 'b')) {
|
||||||
|
if(plen)
|
||||||
|
/* cannot handle partial bytes */
|
||||||
|
return PARAM_BAD_USE;
|
||||||
}
|
}
|
||||||
*value_out = value;
|
else {
|
||||||
|
const struct sizeunit *su = getunit(*unit);
|
||||||
|
if(!su)
|
||||||
|
return PARAM_BAD_USE;
|
||||||
|
mul = su->mul;
|
||||||
|
|
||||||
|
if(prec) {
|
||||||
|
/* precision was provided */
|
||||||
|
curl_off_t frac = 1;
|
||||||
|
|
||||||
|
/* too many precision digits, trim them */
|
||||||
|
while(su->mlen <= plen) {
|
||||||
|
prec /= 10;
|
||||||
|
plen--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(plen--)
|
||||||
|
frac *= 10;
|
||||||
|
|
||||||
|
if((CURL_OFF_T_MAX / mul) > prec)
|
||||||
|
add = mul * prec / frac;
|
||||||
|
else
|
||||||
|
add = (mul / frac) * prec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(value > ((CURL_OFF_T_MAX - add) / mul))
|
||||||
|
return PARAM_NUMBER_TOO_LARGE;
|
||||||
|
|
||||||
|
*out = value * mul + add;
|
||||||
return PARAM_OK;
|
return PARAM_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2365,7 +2404,7 @@ static ParameterError opt_string(struct OperationConfig *config,
|
|||||||
err = getstr(&config->dns_servers, nextarg, DENY_BLANK);
|
err = getstr(&config->dns_servers, nextarg, DENY_BLANK);
|
||||||
break;
|
break;
|
||||||
case C_LIMIT_RATE: /* --limit-rate */
|
case C_LIMIT_RATE: /* --limit-rate */
|
||||||
err = GetSizeParameter(nextarg, "rate", &value);
|
err = GetSizeParameter(nextarg, &value);
|
||||||
if(!err) {
|
if(!err) {
|
||||||
config->recvpersecond = value;
|
config->recvpersecond = value;
|
||||||
config->sendpersecond = value;
|
config->sendpersecond = value;
|
||||||
@@ -2401,7 +2440,7 @@ static ParameterError opt_string(struct OperationConfig *config,
|
|||||||
err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK);
|
err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK);
|
||||||
break;
|
break;
|
||||||
case C_MAX_FILESIZE: /* --max-filesize */
|
case C_MAX_FILESIZE: /* --max-filesize */
|
||||||
err = GetSizeParameter(nextarg, "max-filesize", &value);
|
err = GetSizeParameter(nextarg, &value);
|
||||||
if(!err)
|
if(!err)
|
||||||
config->max_filesize = value;
|
config->max_filesize = value;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -375,6 +375,7 @@ ParameterError getparameter(const char *flag, const char *nextarg,
|
|||||||
ParameterError parse_cert_parameter(const char *cert_parameter,
|
ParameterError parse_cert_parameter(const char *cert_parameter,
|
||||||
char **certname,
|
char **certname,
|
||||||
char **passphrase);
|
char **passphrase);
|
||||||
|
UNITTEST ParameterError GetSizeParameter(const char *arg, curl_off_t *out);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ParameterError parse_args(int argc, argv_item_t argv[]);
|
ParameterError parse_args(int argc, argv_item_t argv[]);
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ test1590 test1591 test1592 test1593 test1594 test1595 test1596 test1597 \
|
|||||||
test1598 test1599 test1600 test1601 test1602 test1603 test1604 test1605 \
|
test1598 test1599 test1600 test1601 test1602 test1603 test1604 test1605 \
|
||||||
test1606 test1607 test1608 test1609 test1610 test1611 test1612 test1613 \
|
test1606 test1607 test1608 test1609 test1610 test1611 test1612 test1613 \
|
||||||
test1614 test1615 test1616 test1617 \
|
test1614 test1615 test1616 test1617 \
|
||||||
test1620 test1621 test1622 \
|
test1620 test1621 test1622 test1623 \
|
||||||
\
|
\
|
||||||
test1630 test1631 test1632 test1633 test1634 test1635 test1636 \
|
test1630 test1631 test1632 test1633 test1634 test1635 test1636 \
|
||||||
\
|
\
|
||||||
|
|||||||
25
tests/data/test1623
Normal file
25
tests/data/test1623
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="US-ASCII"?>
|
||||||
|
<testcase>
|
||||||
|
<info>
|
||||||
|
<keywords>
|
||||||
|
unittest
|
||||||
|
tool-sizeparser
|
||||||
|
</keywords>
|
||||||
|
</info>
|
||||||
|
|
||||||
|
# Client-side
|
||||||
|
<client>
|
||||||
|
<features>
|
||||||
|
unittest
|
||||||
|
</features>
|
||||||
|
<name>
|
||||||
|
the size parser for --limit-rate
|
||||||
|
</name>
|
||||||
|
<tool>
|
||||||
|
tool%TESTNUMBER
|
||||||
|
</tool>
|
||||||
|
</client>
|
||||||
|
|
||||||
|
<verify>
|
||||||
|
</verify>
|
||||||
|
</testcase>
|
||||||
@@ -33,4 +33,5 @@ TESTS_C = \
|
|||||||
tool1394.c \
|
tool1394.c \
|
||||||
tool1604.c \
|
tool1604.c \
|
||||||
tool1621.c \
|
tool1621.c \
|
||||||
tool1622.c
|
tool1622.c \
|
||||||
|
tool1623.c
|
||||||
|
|||||||
127
tests/tunit/tool1623.c
Normal file
127
tests/tunit/tool1623.c
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* _ _ ____ _
|
||||||
|
* Project ___| | | | _ \| |
|
||||||
|
* / __| | | | |_) | |
|
||||||
|
* | (__| |_| | _ <| |___
|
||||||
|
* \___|\___/|_| \_\_____|
|
||||||
|
*
|
||||||
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
|
*
|
||||||
|
* This software is licensed as described in the file COPYING, which
|
||||||
|
* you should have received as part of this distribution. The terms
|
||||||
|
* are also available at https://curl.se/docs/copyright.html.
|
||||||
|
*
|
||||||
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
* copies of the Software, and permit persons to whom the Software is
|
||||||
|
* furnished to do so, under the terms of the COPYING file.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: curl
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
#include "unitcheck.h"
|
||||||
|
|
||||||
|
#include "tool_getparam.h"
|
||||||
|
|
||||||
|
struct check1623 {
|
||||||
|
const char *input;
|
||||||
|
curl_off_t amount;
|
||||||
|
ParameterError err;
|
||||||
|
};
|
||||||
|
|
||||||
|
static CURLcode test_tool1623(const char *arg)
|
||||||
|
{
|
||||||
|
UNITTEST_BEGIN_SIMPLE
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
static const struct check1623 check[] = {
|
||||||
|
{ "0", 0, PARAM_OK},
|
||||||
|
{ "00", 0, PARAM_OK},
|
||||||
|
{ "000", 0, PARAM_OK},
|
||||||
|
{ "1", 1, PARAM_OK},
|
||||||
|
{ "1b", 1, PARAM_OK},
|
||||||
|
{ "99B", 99, PARAM_OK},
|
||||||
|
{ "2", 2, PARAM_OK},
|
||||||
|
{ "3", 3, PARAM_OK},
|
||||||
|
{ "4", 4, PARAM_OK},
|
||||||
|
{ "5", 5, PARAM_OK},
|
||||||
|
{ "6", 6, PARAM_OK},
|
||||||
|
{ "7", 7, PARAM_OK},
|
||||||
|
{ "77", 77, PARAM_OK},
|
||||||
|
{ "8", 8, PARAM_OK},
|
||||||
|
{ "9", 9, PARAM_OK},
|
||||||
|
{ "10", 10, PARAM_OK},
|
||||||
|
{ "010", 10, PARAM_OK},
|
||||||
|
{ "000000000000000000000000000000000010", 10, PARAM_OK},
|
||||||
|
{ "1k", 1024, PARAM_OK},
|
||||||
|
{ "2K", 2048, PARAM_OK},
|
||||||
|
{ "3k", 3072, PARAM_OK},
|
||||||
|
{ "4K", 4096, PARAM_OK},
|
||||||
|
{ "5k", 5120, PARAM_OK},
|
||||||
|
{ "6K", 6144, PARAM_OK},
|
||||||
|
{ "7k", 7168, PARAM_OK},
|
||||||
|
{ "8K", 8192, PARAM_OK},
|
||||||
|
{ "9k", 9216, PARAM_OK},
|
||||||
|
{ "10K", 10240, PARAM_OK},
|
||||||
|
{ "20M", 20971520, PARAM_OK},
|
||||||
|
{ "30G", 32212254720, PARAM_OK},
|
||||||
|
{ "40T", 43980465111040, PARAM_OK},
|
||||||
|
{ "50P", 56294995342131200, PARAM_OK},
|
||||||
|
{ "1.1k", 1126, PARAM_OK},
|
||||||
|
{ "1.01k", 1034, PARAM_OK},
|
||||||
|
{ "1.001k", 1025, PARAM_OK},
|
||||||
|
{ "1.0001k", 1024, PARAM_OK},
|
||||||
|
{ "22.1m", 23173529, PARAM_OK},
|
||||||
|
{ "22.01m", 23079157, PARAM_OK},
|
||||||
|
{ "22.001m", 23069720, PARAM_OK},
|
||||||
|
{ "22.0001m", 23068776, PARAM_OK},
|
||||||
|
{ "22.00001m", 23068682, PARAM_OK},
|
||||||
|
{ "22.000001m", 23068673, PARAM_OK},
|
||||||
|
{ "22.0000001m", 23068672, PARAM_OK},
|
||||||
|
{ "22.000000001m", 23068672, PARAM_OK},
|
||||||
|
{ "3.4", 0, PARAM_BAD_USE},
|
||||||
|
{ "3.14b", 0, PARAM_BAD_USE},
|
||||||
|
{ "5000.9P", 5630512844129278361, PARAM_OK},
|
||||||
|
{ "5000.99P", 5630614175120894197, PARAM_OK},
|
||||||
|
{ "5000.999P", 5630624308220055781, PARAM_OK},
|
||||||
|
{ "5000.9999P", 5630625321529969316, PARAM_OK},
|
||||||
|
{ "8191P", 9222246136947933184, PARAM_OK},
|
||||||
|
{ "8191.9999999P", 9223372036735343194, PARAM_OK},
|
||||||
|
{ "8192P", 0, PARAM_NUMBER_TOO_LARGE},
|
||||||
|
{ "9223372036854775807", 9223372036854775807, PARAM_OK},
|
||||||
|
{ "9223372036854775808", 0, PARAM_NUMBER_TOO_LARGE},
|
||||||
|
{ "a", 0, PARAM_BAD_NUMERIC},
|
||||||
|
{ "-2", 0, PARAM_BAD_NUMERIC},
|
||||||
|
{ "+2", 0, PARAM_BAD_NUMERIC},
|
||||||
|
{ "2,2k", 0, PARAM_BAD_USE},
|
||||||
|
{ NULL, 0, PARAM_OK } /* end of list */
|
||||||
|
};
|
||||||
|
|
||||||
|
for(i = 0; check[i].input; i++) {
|
||||||
|
bool ok = FALSE;
|
||||||
|
curl_off_t output = 0;
|
||||||
|
ParameterError err =
|
||||||
|
GetSizeParameter(check[i].input, &output);
|
||||||
|
if(err != check[i].err)
|
||||||
|
curl_mprintf("'%s' unexpectedly returned %d \n",
|
||||||
|
check[i].input, err);
|
||||||
|
else if(check[i].amount != output)
|
||||||
|
curl_mprintf("'%s' unexpectedly gave %" FMT_OFF_T "\n",
|
||||||
|
check[i].input, output);
|
||||||
|
else {
|
||||||
|
#if 0 /* enable for debugging */
|
||||||
|
if(err)
|
||||||
|
curl_mprintf("'%s' returned %d\n", check[i].input, err);
|
||||||
|
else
|
||||||
|
curl_mprintf("'%s' == %" FMT_OFF_T "\n", check[i].input, output);
|
||||||
|
#endif
|
||||||
|
ok = TRUE;
|
||||||
|
}
|
||||||
|
if(!ok)
|
||||||
|
unitfail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UNITTEST_END_SIMPLE
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user