return 1;
}
-/* Set the value stored in VAL to CLOSURE (which should point to a
- long int), allowing several postfixes, with the following syntax
- (regexp):
+/* Poor man's atof: handles only <digits>.<digits>. Returns 1 on
+ success, 0 on failure. In case of success, stores its result to
+ *DEST. */
- [0-9]+ -> bytes
- [0-9]+[kK] -> bytes * 1024
- [0-9]+[mM] -> bytes * 1024 * 1024
- inf -> 0
+static int
+simple_atof (const char *beg, const char *end, double *dest)
+{
+ double result = 0;
+
+ int seen_dot = 0;
+ int seen_digit = 0;
+ double divider = 1;
+
+ const char *p = beg;
+
+ while (p < end)
+ {
+ char ch = *p++;
+ if (ISDIGIT (ch))
+ {
+ if (!seen_dot)
+ result = (10 * result) + (ch - '0');
+ else
+ result += (ch - '0') / (divider *= 10);
+ seen_digit = 1;
+ }
+ else if (ch == '.')
+ {
+ if (!seen_dot)
+ seen_dot = 1;
+ else
+ return 0;
+ }
+ }
+ if (!seen_digit)
+ return 0;
+
+ *dest = result;
+ return 1;
+}
+
+/* Parse VAL as a number and set its value to CLOSURE (which should
+ point to a long int).
+
+ By default, the value is assumed to be in bytes. If "K", "M", or
+ "G" are appended, the value is multiplied with 1<<10, 1<<20, or
+ 1<<30, respectively. Floating point values are allowed and are
+ cast to integer before use. The idea is to be able to use things
+ like 1.5k instead of "1536".
+
+ The string "inf" is returned as 0.
+
+ In case of error, 0 is returned and memory pointed to by CLOSURE
+ remains unmodified. */
- Anything else is flagged as incorrect, and CLOSURE is unchanged. */
static int
cmd_bytes (const char *com, const char *val, void *closure)
{
- long result;
- long *out = (long *)closure;
- const char *p;
+ long mult;
+ double number;
+ const char *end = val + strlen (val);
- result = 0;
- p = val;
/* Check for "inf". */
- if (p[0] == 'i' && p[1] == 'n' && p[2] == 'f' && p[3] == '\0')
+ if (0 == strcmp (val, "inf"))
{
- *out = 0;
+ *(long *)closure = 0;
return 1;
}
- /* Search for digits and construct result. */
- for (; *p && ISDIGIT (*p); p++)
- result = (10 * result) + (*p - '0');
- /* If no digits were found, or more than one character is following
- them, bail out. */
- if (p == val || (*p != '\0' && *(p + 1) != '\0'))
+
+ /* Strip trailing whitespace. */
+ while (val < end && ISSPACE (end[-1]))
+ --end;
+
+ if (val == end)
{
- printf (_("%s: Invalid specification `%s'\n"), com, val);
+ err:
+ fprintf (stderr, _("%s: Invalid byte value `%s'\n"), com, val);
return 0;
}
- /* Search for a designator. */
- switch (TOLOWER (*p))
+
+ switch (TOLOWER (end[-1]))
{
- case '\0':
- /* None */
- break;
case 'k':
- /* Kilobytes */
- result *= 1024;
+ --end, mult = 1L<<10;
break;
case 'm':
- /* Megabytes */
- result *= (long)1024 * 1024;
+ --end, mult = 1L<<20;
break;
case 'g':
- /* Gigabytes */
- result *= (long)1024 * 1024 * 1024;
+ --end, mult = 1L<<30;
break;
default:
- printf (_("%s: Invalid specification `%s'\n"), com, val);
- return 0;
+ /* Not a recognized suffix: assume it belongs to the number.
+ (If not, atof simple_atof will raise an error.) */
+ mult = 1;
}
- *out = result;
+
+ /* Skip leading and trailing whitespace. */
+ while (val < end && ISSPACE (*val))
+ ++val;
+ while (val < end && ISSPACE (end[-1]))
+ --end;
+ if (val == end)
+ goto err;
+
+ if (!simple_atof (val, end, &number))
+ goto err;
+
+ *(long *)closure = (long)(number * mult);
return 1;
}
static int
cmd_time (const char *com, const char *val, void *closure)
{
- double result, mult, divider;
- int seen_dot, seen_digit;
-
- const char *p;
+ double number, mult;
const char *end = val + strlen (val);
- /* Skip trailing whitespace. */
- while (end > val && ISSPACE (end[-1]))
+ /* Strip trailing whitespace. */
+ while (val < end && ISSPACE (end[-1]))
--end;
if (val == end)
--end, mult = 3600; /* hours */
break;
case 'd':
- --end, mult = 86400; /* days */
+ --end, mult = 86400.0; /* days */
break;
case 'w':
- --end, mult = 604800; /* weeks */
+ --end, mult = 604800.0; /* weeks */
break;
default:
- /* Not a recognized suffix: treat it as part of number, and
- assume seconds. */
+ /* Not a recognized suffix: assume it belongs to the number.
+ (If not, atof simple_atof will raise an error.) */
mult = 1;
}
/* Skip leading and trailing whitespace. */
while (val < end && ISSPACE (*val))
++val;
- while (val > end && ISSPACE (end[-1]))
+ while (val < end && ISSPACE (end[-1]))
--end;
if (val == end)
goto err;
- /* Poor man's strtod: */
- result = 0;
- seen_dot = seen_digit = 0;
- divider = 1;
-
- p = val;
- while (p < end)
- {
- char ch = *p++;
- if (ISDIGIT (ch))
- {
- if (!seen_dot)
- result = (10 * result) + (ch - '0');
- else
- result += (ch - '0') / (divider *= 10);
- seen_digit = 1;
- }
- else if (ch == '.')
- {
- if (!seen_dot)
- seen_dot = 1;
- else
- goto err;
- }
- }
- if (!seen_digit)
+ if (!simple_atof (val, end, &number))
goto err;
- result *= mult;
- *(double *)closure = result;
+
+ *(double *)closure = number * mult;
return 1;
}
\f
\f
static struct {
- long bytes;
- double dltime;
+ long chunk_bytes;
+ double chunk_start;
+ double sleep_adjust;
} limit_data;
static void
limit_bandwidth_reset (void)
{
- limit_data.bytes = 0;
- limit_data.dltime = 0;
+ limit_data.chunk_bytes = 0;
+ limit_data.chunk_start = 0;
}
/* Limit the bandwidth by pausing the download for an amount of time.
is the number of milliseconds it took to receive them. */
static void
-limit_bandwidth (long bytes, double delta)
+limit_bandwidth (long bytes, double *dltime, struct wget_timer *timer)
{
+ double delta_t = *dltime - limit_data.chunk_start;
double expected;
- limit_data.bytes += bytes;
- limit_data.dltime += delta;
+ limit_data.chunk_bytes += bytes;
- expected = 1000.0 * limit_data.bytes / opt.limit_rate;
+ /* Calculate the amount of time we expect downloading the chunk
+ should take. If in reality it took less time, sleep to
+ compensate for the difference. */
+ expected = 1000.0 * limit_data.chunk_bytes / opt.limit_rate;
- if (expected > limit_data.dltime)
+ if (expected > delta_t)
{
- double slp = expected - limit_data.dltime;
+ double slp = expected - delta_t + limit_data.sleep_adjust;
+ double t0, t1;
if (slp < 200)
{
DEBUGP (("deferring a %.2f ms sleep (%ld/%.2f).\n",
- slp, limit_data.bytes, limit_data.dltime));
+ slp, limit_data.chunk_bytes, delta_t));
return;
}
- DEBUGP (("sleeping %.2f ms\n", slp));
+ DEBUGP (("\nsleeping %.2f ms for %ld bytes, adjust %.2f ms\n",
+ slp, limit_data.chunk_bytes, limit_data.sleep_adjust));
+
+ t0 = *dltime;
usleep ((unsigned long) (1000 * slp));
+ t1 = wtimer_elapsed (timer);
+
+ /* Due to scheduling, we probably slept slightly longer (or
+ shorter) than desired. Calculate the difference between the
+ desired and the actual sleep, and adjust the next sleep by
+ that amount. */
+ limit_data.sleep_adjust = slp - (t1 - t0);
+
+ /* Since we've called wtimer_elapsed, we might as well update
+ the caller's dltime. */
+ *dltime = t1;
}
- limit_data.bytes = 0;
- limit_data.dltime = 0;
+ limit_data.chunk_bytes = 0;
+ limit_data.chunk_start = *dltime;
}
#define MIN(i, j) ((i) <= (j) ? (i) : (j))
void *progress = NULL;
struct wget_timer *timer = wtimer_allocate ();
- double dltime = 0, last_dltime = 0;
+ double dltime = 0;
*len = restval;
goto out;
}
- /* If bandwidth is not limited, one call to wtimer_elapsed is
- sufficient. */
dltime = wtimer_elapsed (timer);
if (opt.limit_rate)
- {
- limit_bandwidth (res, dltime - last_dltime);
- dltime = wtimer_elapsed (timer);
- last_dltime = dltime;
- }
+ limit_bandwidth (res, &dltime, timer);
if (progress)
progress_update (progress, res, dltime);