tool: support fractions for --limit-rate and --max-filesize
No binary (build failed)
—
(log yok)
| @@ -12,7 +12,7 @@ See-also: | |
| - speed-limit | |
| - speed-time | |
| Example: | |
| - | - --limit-rate 100K $URL |
| + | - --limit-rate 123.45K $URL |
| - --limit-rate 1000 $URL | |
| - --limit-rate 10M $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. | |
| 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) |
| - | are 1024 based. For example 1k is 1024. Examples: 200K, 3m and 1G. |
| + | megabytes etc. The supported suffixes (k, M, G, T, P) are 1024-based. For |
| + | example 1k is 1024. Examples: 200K, 3m and 1G. |
| The rate limiting logic works on averaging the transfer speed to no more than | |
| 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 | |
| might cripple the rate-limiting slightly, to help keep the speed-limit | |
| 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 | |
| Example: | |
| - --max-filesize 100K $URL | |
| + | - --max-filesize 2.6M $URL |
| --- | |
| # `--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. | |
| - | A size modifier may be used. For example, Appending 'k' or 'K' counts the |
| - | number as kilobytes, 'm' or 'M' makes it megabytes, while 'g' or 'G' makes it |
| - | gigabytes. Examples: 200K, 3m and 1G. (Added in 7.58.0) |
| + | A unit suffix letter can be used. Appending 'k' or 'K' counts the number as |
| + | kilobytes, 'm' or 'M' makes it megabytes etc. The supported suffixes (k, M, G, |
| + | 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 | |
| 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 | |
| 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; | |
| } | |
| + | 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'. | |
| - | * 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, |
| - | const char *which, |
| - | curl_off_t *value_out) |
| + | UNITTEST ParameterError GetSizeParameter(const char *arg, curl_off_t *out) |
| { | |
| const char *unit = arg; | |
| 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; |
| + | |
| + | rc = curlx_str_number(&unit, &value, CURL_OFF_T_MAX); |
| + | if(rc == STRE_OVERFLOW) |
| + | return PARAM_NUMBER_TOO_LARGE; |
| + | else if(rc) |
| + | return PARAM_BAD_NUMERIC; |
| - | if(curlx_str_number(&unit, &value, CURL_OFF_T_MAX)) { |
| - | warnf("invalid number specified for %s", which); |
| + | 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(strlen(unit) > 1) |
| return PARAM_BAD_USE; | |
| + | else if(!*unit || ((*unit | 0x20) == 'b')) { |
| + | if(plen) |
| + | /* cannot handle partial bytes */ |
| + | return PARAM_BAD_USE; |
| } | |
| + | else { |
| + | const struct sizeunit *su = getunit(*unit); |
| + | if(!su) |
| + | return PARAM_BAD_USE; |
| + | mul = su->mul; |
| - | if(!*unit) |
| - | unit = "b"; |
| - | else if(strlen(unit) > 1) |
| - | unit = "w"; /* unsupported */ |
| + | if(prec) { |
| + | /* precision was provided */ |
| + | curl_off_t frac = 1; |
| - | 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; |
| + | /* 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; |
| + | } |
| } | |
| - | *value_out = value; |
| + | if(value > ((CURL_OFF_T_MAX - add) / mul)) |
| + | return PARAM_NUMBER_TOO_LARGE; |
| + | |
| + | *out = value * mul + add; |
| return PARAM_OK; | |
| } | |
| @@ -2365,7 +2404,7 @@ static ParameterError opt_string(struct OperationConfig *config, | |
| err = getstr(&config->dns_servers, nextarg, DENY_BLANK); | |
| break; | |
| case C_LIMIT_RATE: /* --limit-rate */ | |
| - | err = GetSizeParameter(nextarg, "rate", &value); |
| + | err = GetSizeParameter(nextarg, &value); |
| if(!err) { | |
| config->recvpersecond = value; | |
| config->sendpersecond = value; | |
| @@ -2401,7 +2440,7 @@ static ParameterError opt_string(struct OperationConfig *config, | |
| err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK); | |
| break; | |
| case C_MAX_FILESIZE: /* --max-filesize */ | |
| - | err = GetSizeParameter(nextarg, "max-filesize", &value); |
| + | err = GetSizeParameter(nextarg, &value); |
| if(!err) | |
| config->max_filesize = value; | |
| break; | |
| @@ -375,6 +375,7 @@ ParameterError getparameter(const char *flag, const char *nextarg, | |
| ParameterError parse_cert_parameter(const char *cert_parameter, | |
| char **certname, | |
| char **passphrase); | |
| + | UNITTEST ParameterError GetSizeParameter(const char *arg, curl_off_t *out); |
| #endif | |
| 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 \ | |
| test1606 test1607 test1608 test1609 test1610 test1611 test1612 test1613 \ | |
| test1614 test1615 test1616 test1617 \ | |
| - | test1620 test1621 test1622 \ |
| + | test1620 test1621 test1622 test1623 \ |
| \ | |
| test1630 test1631 test1632 test1633 test1634 test1635 test1636 \ | |
| \ | |
| @@ -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 \ | |
| tool1604.c \ | |
| tool1621.c \ | |
| - | tool1622.c |
| + | tool1622.c \ |
| + | tool1623.c |
| @@ -0,0 +1,127 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 |
| + | } |