mirror of
https://github.com/moparisthebest/wget
synced 2024-07-03 16:38:41 -04:00
[svn] Include ETA information in dot progress.
This commit is contained in:
parent
276f66fc14
commit
e289d2ecc4
@ -1,3 +1,18 @@
|
|||||||
|
2005-06-29 Hrvoje Niksic <hniksic@xemacs.org>
|
||||||
|
|
||||||
|
* main.c (secs_to_human_time): Use print_decimal when printing
|
||||||
|
total download time in seconds.
|
||||||
|
|
||||||
|
* progress.c (print_row_stats): Use it to print total download
|
||||||
|
time at the end of the download.
|
||||||
|
(create_image): Ditto.
|
||||||
|
|
||||||
|
* utils.c (print_decimal): New function; print small decimal
|
||||||
|
numbers with more precision than large ones.
|
||||||
|
|
||||||
|
* progress.c (print_row_stats): New function. Print ETA after the
|
||||||
|
download rate at the end of each row.
|
||||||
|
|
||||||
2005-06-28 Hrvoje Niksic <hniksic@xemacs.org>
|
2005-06-28 Hrvoje Niksic <hniksic@xemacs.org>
|
||||||
|
|
||||||
* init.c (parse_line): Check for alphanumerics.
|
* init.c (parse_line): Check for alphanumerics.
|
||||||
|
@ -168,8 +168,8 @@ convert_all_links (void)
|
|||||||
|
|
||||||
secs = ptimer_measure (timer) / 1000;
|
secs = ptimer_measure (timer) / 1000;
|
||||||
ptimer_destroy (timer);
|
ptimer_destroy (timer);
|
||||||
logprintf (LOG_VERBOSE, _("Converted %d files in %.*f seconds.\n"),
|
logprintf (LOG_VERBOSE, _("Converted %d files in %s seconds.\n"),
|
||||||
file_count, secs < 10 ? 3 : 1, secs);
|
file_count, print_decimal (secs));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_backup_file (const char *, downloaded_file_t);
|
static void write_backup_file (const char *, downloaded_file_t);
|
||||||
|
10
src/main.c
10
src/main.c
@ -643,16 +643,8 @@ secs_to_human_time (double interval)
|
|||||||
sprintf (buf, "%dh %dm %ds", hours, mins, secs);
|
sprintf (buf, "%dh %dm %ds", hours, mins, secs);
|
||||||
else if (mins)
|
else if (mins)
|
||||||
sprintf (buf, "%dm %ds", mins, secs);
|
sprintf (buf, "%dm %ds", mins, secs);
|
||||||
else if (interval >= 10)
|
|
||||||
sprintf (buf, "%ds", secs);
|
|
||||||
else
|
else
|
||||||
/* For very quick downloads show more exact timing information. */
|
sprintf (buf, "%ss", print_decimal (interval));
|
||||||
sprintf (buf, "%.*fs",
|
|
||||||
interval < 0.001 ? 0 : /* 0s instead of 0.000s */
|
|
||||||
interval < 0.01 ? 3 : /* 0.00x */
|
|
||||||
interval < 0.1 ? 2 : /* 0.0x */
|
|
||||||
1, /* 0.x, 1.x, ..., 9.x */
|
|
||||||
interval);
|
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
163
src/progress.c
163
src/progress.c
@ -205,6 +205,7 @@ struct dot_progress {
|
|||||||
|
|
||||||
int rows; /* number of rows printed so far */
|
int rows; /* number of rows printed so far */
|
||||||
int dots; /* number of dots printed in this row */
|
int dots; /* number of dots printed in this row */
|
||||||
|
|
||||||
double last_timer_value;
|
double last_timer_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -220,26 +221,28 @@ dot_create (wgint initial, wgint total)
|
|||||||
if (dp->initial_length)
|
if (dp->initial_length)
|
||||||
{
|
{
|
||||||
int dot_bytes = opt.dot_bytes;
|
int dot_bytes = opt.dot_bytes;
|
||||||
wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
|
const wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
|
||||||
|
|
||||||
int remainder = dp->initial_length % row_bytes;
|
int remainder = dp->initial_length % ROW_BYTES;
|
||||||
wgint skipped = dp->initial_length - remainder;
|
wgint skipped = dp->initial_length - remainder;
|
||||||
|
|
||||||
if (skipped)
|
if (skipped)
|
||||||
{
|
{
|
||||||
int skipped_k = skipped / 1024; /* skipped amount in K */
|
wgint skipped_k = skipped / 1024; /* skipped amount in K */
|
||||||
int skipped_k_len = numdigit (skipped_k);
|
int skipped_k_len = numdigit (skipped_k);
|
||||||
if (skipped_k_len < 5)
|
if (skipped_k_len < 6)
|
||||||
skipped_k_len = 5;
|
skipped_k_len = 6;
|
||||||
|
|
||||||
/* Align the [ skipping ... ] line with the dots. To do
|
/* Align the [ skipping ... ] line with the dots. To do
|
||||||
that, insert the number of spaces equal to the number of
|
that, insert the number of spaces equal to the number of
|
||||||
digits in the skipped amount in K. */
|
digits in the skipped amount in K. */
|
||||||
logprintf (LOG_VERBOSE, _("\n%*s[ skipping %dK ]"),
|
logprintf (LOG_VERBOSE, _("\n%*s[ skipping %sK ]"),
|
||||||
2 + skipped_k_len, "", skipped_k);
|
2 + skipped_k_len, "",
|
||||||
|
number_to_static_string (skipped_k));
|
||||||
}
|
}
|
||||||
|
|
||||||
logprintf (LOG_VERBOSE, "\n%5ldK", (long) (skipped / 1024));
|
logprintf (LOG_VERBOSE, "\n%6sK",
|
||||||
|
number_to_static_string (skipped / 1024));
|
||||||
for (; remainder >= dot_bytes; remainder -= dot_bytes)
|
for (; remainder >= dot_bytes; remainder -= dot_bytes)
|
||||||
{
|
{
|
||||||
if (dp->dots % opt.dot_spacing == 0)
|
if (dp->dots % opt.dot_spacing == 0)
|
||||||
@ -250,30 +253,88 @@ dot_create (wgint initial, wgint total)
|
|||||||
assert (dp->dots < opt.dots_in_line);
|
assert (dp->dots < opt.dots_in_line);
|
||||||
|
|
||||||
dp->accumulated = remainder;
|
dp->accumulated = remainder;
|
||||||
dp->rows = skipped / row_bytes;
|
dp->rows = skipped / ROW_BYTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dp;
|
return dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *eta_to_human_short (int, bool);
|
||||||
|
|
||||||
|
/* Prints the stats (percentage of completion, speed, ETA) for current
|
||||||
|
row. DLTIME is the time spent downloading the data in current
|
||||||
|
row.
|
||||||
|
|
||||||
|
#### This function is somewhat uglified by the fact that current
|
||||||
|
row and last row have somewhat different stats requirements. It
|
||||||
|
might be worthwhile to split it to two different functions. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_percentage (wgint bytes, wgint expected)
|
print_row_stats (struct dot_progress *dp, double dltime, bool last)
|
||||||
{
|
{
|
||||||
/* Round to the floor value in order to gauge how much data *has*
|
const wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
|
||||||
been retrieved. 12.8% will round to 12% because the 13% mark has
|
|
||||||
not yet been reached. 100% is only shown when done. */
|
/* bytes_displayed is the number of bytes indicated to the user by
|
||||||
int percentage = 100.0 * bytes / expected;
|
dots printed so far, includes the initially "skipped" amount */
|
||||||
|
wgint bytes_displayed = dp->rows * ROW_BYTES + dp->dots * opt.dot_bytes;
|
||||||
|
|
||||||
|
if (last)
|
||||||
|
/* For last row also count bytes accumulated after last dot */
|
||||||
|
bytes_displayed += dp->accumulated;
|
||||||
|
|
||||||
|
if (dp->total_length)
|
||||||
|
{
|
||||||
|
/* Round to floor value to provide gauge how much data *has*
|
||||||
|
been retrieved. 12.8% will round to 12% because the 13% mark
|
||||||
|
has not yet been reached. 100% is only shown when done. */
|
||||||
|
int percentage = 100.0 * bytes_displayed / dp->total_length;
|
||||||
logprintf (LOG_VERBOSE, "%3d%%", percentage);
|
logprintf (LOG_VERBOSE, "%3d%%", percentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
print_download_speed (struct dot_progress *dp, wgint bytes, double dltime)
|
|
||||||
{
|
{
|
||||||
logprintf (LOG_VERBOSE, " %7s",
|
static char names[] = {' ', 'K', 'M', 'G'};
|
||||||
retr_rate (bytes, dltime - dp->last_timer_value));
|
int units;
|
||||||
|
double rate;
|
||||||
|
wgint bytes_this_row;
|
||||||
|
if (!last)
|
||||||
|
bytes_this_row = ROW_BYTES;
|
||||||
|
else
|
||||||
|
/* For last row also include bytes accumulated after last dot. */
|
||||||
|
bytes_this_row = dp->dots * opt.dot_bytes + dp->accumulated;
|
||||||
|
if (dp->rows == dp->initial_length / ROW_BYTES)
|
||||||
|
/* Don't count the portion of the row belonging to initial_length */
|
||||||
|
bytes_this_row -= dp->initial_length % ROW_BYTES;
|
||||||
|
rate = calc_rate (bytes_this_row, dltime - dp->last_timer_value, &units);
|
||||||
|
logprintf (LOG_VERBOSE, " %4.*f%c",
|
||||||
|
rate >= 100 ? 0 : rate >= 9.995 ? 1 : 2,
|
||||||
|
rate, names[units]);
|
||||||
dp->last_timer_value = dltime;
|
dp->last_timer_value = dltime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!last)
|
||||||
|
{
|
||||||
|
if (dp->total_length)
|
||||||
|
{
|
||||||
|
wgint bytes_remaining = dp->total_length - bytes_displayed;
|
||||||
|
/* The quantity downloaded in this download run. */
|
||||||
|
wgint bytes_sofar = bytes_displayed - dp->initial_length;
|
||||||
|
double secs_sofar = dltime / 1000;
|
||||||
|
int eta = (int) (secs_sofar * bytes_remaining / bytes_sofar + 0.5);
|
||||||
|
logprintf (LOG_VERBOSE, " %s", eta_to_human_short (eta, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* When done, print the total download time */
|
||||||
|
double secs = dltime / 1000;
|
||||||
|
if (secs >= 10)
|
||||||
|
logprintf (LOG_VERBOSE, "=%s",
|
||||||
|
eta_to_human_short ((int) (secs + 0.5), true));
|
||||||
|
else
|
||||||
|
logprintf (LOG_VERBOSE, "=%ss", print_decimal (secs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Dot-progress backend for progress_update. */
|
/* Dot-progress backend for progress_update. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -281,7 +342,7 @@ dot_update (void *progress, wgint howmuch, double dltime)
|
|||||||
{
|
{
|
||||||
struct dot_progress *dp = progress;
|
struct dot_progress *dp = progress;
|
||||||
int dot_bytes = opt.dot_bytes;
|
int dot_bytes = opt.dot_bytes;
|
||||||
wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
|
wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
|
||||||
|
|
||||||
log_set_flush (false);
|
log_set_flush (false);
|
||||||
|
|
||||||
@ -289,7 +350,8 @@ dot_update (void *progress, wgint howmuch, double dltime)
|
|||||||
for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
|
for (; dp->accumulated >= dot_bytes; dp->accumulated -= dot_bytes)
|
||||||
{
|
{
|
||||||
if (dp->dots == 0)
|
if (dp->dots == 0)
|
||||||
logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
|
logprintf (LOG_VERBOSE, "\n%6sK",
|
||||||
|
number_to_static_string (dp->rows * ROW_BYTES / 1024));
|
||||||
|
|
||||||
if (dp->dots % opt.dot_spacing == 0)
|
if (dp->dots % opt.dot_spacing == 0)
|
||||||
logputs (LOG_VERBOSE, " ");
|
logputs (LOG_VERBOSE, " ");
|
||||||
@ -298,16 +360,14 @@ dot_update (void *progress, wgint howmuch, double dltime)
|
|||||||
++dp->dots;
|
++dp->dots;
|
||||||
if (dp->dots >= opt.dots_in_line)
|
if (dp->dots >= opt.dots_in_line)
|
||||||
{
|
{
|
||||||
wgint row_qty = row_bytes;
|
wgint row_qty = ROW_BYTES;
|
||||||
if (dp->rows == dp->initial_length / row_bytes)
|
if (dp->rows == dp->initial_length / ROW_BYTES)
|
||||||
row_qty -= dp->initial_length % row_bytes;
|
row_qty -= dp->initial_length % ROW_BYTES;
|
||||||
|
|
||||||
++dp->rows;
|
++dp->rows;
|
||||||
dp->dots = 0;
|
dp->dots = 0;
|
||||||
|
|
||||||
if (dp->total_length)
|
print_row_stats (dp, dltime, false);
|
||||||
print_percentage (dp->rows * row_bytes, dp->total_length);
|
|
||||||
print_download_speed (dp, row_qty, dltime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,35 +380,22 @@ static void
|
|||||||
dot_finish (void *progress, double dltime)
|
dot_finish (void *progress, double dltime)
|
||||||
{
|
{
|
||||||
struct dot_progress *dp = progress;
|
struct dot_progress *dp = progress;
|
||||||
int dot_bytes = opt.dot_bytes;
|
wgint ROW_BYTES = opt.dot_bytes * opt.dots_in_line;
|
||||||
wgint row_bytes = opt.dot_bytes * opt.dots_in_line;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
log_set_flush (false);
|
log_set_flush (false);
|
||||||
|
|
||||||
if (dp->dots == 0)
|
if (dp->dots == 0)
|
||||||
logprintf (LOG_VERBOSE, "\n%5ldK", (long) (dp->rows * row_bytes / 1024));
|
logprintf (LOG_VERBOSE, "\n%6sK",
|
||||||
|
number_to_static_string (dp->rows * ROW_BYTES / 1024));
|
||||||
for (i = dp->dots; i < opt.dots_in_line; i++)
|
for (i = dp->dots; i < opt.dots_in_line; i++)
|
||||||
{
|
{
|
||||||
if (i % opt.dot_spacing == 0)
|
if (i % opt.dot_spacing == 0)
|
||||||
logputs (LOG_VERBOSE, " ");
|
logputs (LOG_VERBOSE, " ");
|
||||||
logputs (LOG_VERBOSE, " ");
|
logputs (LOG_VERBOSE, " ");
|
||||||
}
|
}
|
||||||
if (dp->total_length)
|
|
||||||
{
|
|
||||||
print_percentage (dp->rows * row_bytes
|
|
||||||
+ dp->dots * dot_bytes
|
|
||||||
+ dp->accumulated,
|
|
||||||
dp->total_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
wgint row_qty = dp->dots * dot_bytes + dp->accumulated;
|
|
||||||
if (dp->rows == dp->initial_length / row_bytes)
|
|
||||||
row_qty -= dp->initial_length % row_bytes;
|
|
||||||
print_download_speed (dp, row_qty, dltime);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
print_row_stats (dp, dltime, true);
|
||||||
logputs (LOG_VERBOSE, "\n\n");
|
logputs (LOG_VERBOSE, "\n\n");
|
||||||
log_set_flush (false);
|
log_set_flush (false);
|
||||||
|
|
||||||
@ -704,8 +751,6 @@ update_speed_ring (struct bar_progress *bp, wgint howmuch, double dltime)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *eta_to_human_short (int);
|
|
||||||
|
|
||||||
#define APPEND_LITERAL(s) do { \
|
#define APPEND_LITERAL(s) do { \
|
||||||
memcpy (p, s, sizeof (s) - 1); \
|
memcpy (p, s, sizeof (s) - 1); \
|
||||||
p += sizeof (s) - 1; \
|
p += sizeof (s) - 1; \
|
||||||
@ -884,7 +929,7 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done)
|
|||||||
|
|
||||||
/* Translation note: "ETA" is English-centric, but this must
|
/* Translation note: "ETA" is English-centric, but this must
|
||||||
be short, ideally 3 chars. Abbreviate if necessary. */
|
be short, ideally 3 chars. Abbreviate if necessary. */
|
||||||
sprintf (p, _(" eta %s"), eta_to_human_short (eta));
|
sprintf (p, _(" eta %s"), eta_to_human_short (eta, false));
|
||||||
move_to_end (p);
|
move_to_end (p);
|
||||||
}
|
}
|
||||||
else if (bp->total_length > 0)
|
else if (bp->total_length > 0)
|
||||||
@ -901,15 +946,9 @@ create_image (struct bar_progress *bp, double dl_total_time, bool done)
|
|||||||
strcpy (p, _(" in "));
|
strcpy (p, _(" in "));
|
||||||
move_to_end (p); /* not p+=6, think translations! */
|
move_to_end (p); /* not p+=6, think translations! */
|
||||||
if (secs >= 10)
|
if (secs >= 10)
|
||||||
strcpy (p, eta_to_human_short ((int) (secs + 0.5)));
|
strcpy (p, eta_to_human_short ((int) (secs + 0.5), false));
|
||||||
else
|
else
|
||||||
/* For very quick downloads show more exact timing information. */
|
sprintf (p, "%ss", print_decimal (secs));
|
||||||
sprintf (p, "%.*fs",
|
|
||||||
secs < 0.001 ? 0 : /* 0s instead of 0.000s */
|
|
||||||
secs < 0.01 ? 3 : /* 0.00x */
|
|
||||||
secs < 0.1 ? 2 : /* 0.0x */
|
|
||||||
1, /* 0.x, 1.x, ..., 9.x */
|
|
||||||
secs);
|
|
||||||
move_to_end (p);
|
move_to_end (p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -984,18 +1023,20 @@ progress_handle_sigwinch (int sig)
|
|||||||
and hours are shown. This ensures brevity while still displaying
|
and hours are shown. This ensures brevity while still displaying
|
||||||
as much as possible.
|
as much as possible.
|
||||||
|
|
||||||
If SEP is false, the separator between minutes and seconds (and
|
If CONDENSED is true, the separator between minutes and seconds
|
||||||
hours and minutes, etc.) is not included, shortening the display by
|
(and hours and minutes, etc.) is not included, shortening the
|
||||||
one additional character. This is used for dot progress.
|
display by one additional character. This is used for dot
|
||||||
|
progress.
|
||||||
|
|
||||||
The display never occupies more than 7 characters of screen
|
The display never occupies more than 7 characters of screen
|
||||||
space. */
|
space. */
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
eta_to_human_short (int secs)
|
eta_to_human_short (int secs, bool condensed)
|
||||||
{
|
{
|
||||||
static char buf[10]; /* 8 should be enough, but just in case */
|
static char buf[10]; /* 8 should be enough, but just in case */
|
||||||
static int last = -1;
|
static int last = -1;
|
||||||
|
const char *space = condensed ? "" : " ";
|
||||||
|
|
||||||
/* Trivial optimization. create_image can call us every 200 msecs
|
/* Trivial optimization. create_image can call us every 200 msecs
|
||||||
(see bar_update) for fast downloads, but ETA will only change
|
(see bar_update) for fast downloads, but ETA will only change
|
||||||
@ -1007,11 +1048,11 @@ eta_to_human_short (int secs)
|
|||||||
if (secs < 100)
|
if (secs < 100)
|
||||||
sprintf (buf, "%ds", secs);
|
sprintf (buf, "%ds", secs);
|
||||||
else if (secs < 100 * 60)
|
else if (secs < 100 * 60)
|
||||||
sprintf (buf, "%dm %ds", secs / 60, secs % 60);
|
sprintf (buf, "%dm%s%ds", secs / 60, space, secs % 60);
|
||||||
else if (secs < 100 * 3600)
|
else if (secs < 100 * 3600)
|
||||||
sprintf (buf, "%dh %dm", secs / 3600, (secs / 60) % 60);
|
sprintf (buf, "%dh%s%dm", secs / 3600, space, (secs / 60) % 60);
|
||||||
else if (secs < 100 * 86400)
|
else if (secs < 100 * 86400)
|
||||||
sprintf (buf, "%dd %dh", secs / 86400, (secs / 3600) % 60);
|
sprintf (buf, "%dd%s%dh", secs / 86400, space, (secs / 3600) % 60);
|
||||||
else
|
else
|
||||||
/* even (2^31-1)/86400 doesn't overflow BUF. */
|
/* even (2^31-1)/86400 doesn't overflow BUF. */
|
||||||
sprintf (buf, "%dd", secs / 86400);
|
sprintf (buf, "%dd", secs / 86400);
|
||||||
|
35
src/utils.c
35
src/utils.c
@ -2039,3 +2039,38 @@ stable_sort (void *base, size_t nmemb, size_t size,
|
|||||||
mergesort_internal (base, temp, size, 0, nmemb - 1, cmpfun);
|
mergesort_internal (base, temp, size, 0, nmemb - 1, cmpfun);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print a decimal number. If it is equal to or larger than ten, the
|
||||||
|
number is rounded. Otherwise it is printed with one significant
|
||||||
|
digit without trailing zeros and with no more than three fractional
|
||||||
|
digits total. For example, 0.1 is printed as "0.1", 0.035 is
|
||||||
|
printed as "0.04", 0.0091 as "0.009", and 0.0003 as simply "0".
|
||||||
|
|
||||||
|
This is useful for displaying durations because it provides
|
||||||
|
order-of-magnitude information without unnecessary clutter --
|
||||||
|
long-running downloads are shown without the fractional part, and
|
||||||
|
short ones still retain one significant digit. */
|
||||||
|
|
||||||
|
const char *
|
||||||
|
print_decimal (double number)
|
||||||
|
{
|
||||||
|
static char buf[32];
|
||||||
|
double n = number >= 0 ? number : -number;
|
||||||
|
|
||||||
|
if (n >= 9.95)
|
||||||
|
/* Cut off at 9.95 because the below %.1f would round 9.96 to
|
||||||
|
"10.0" instead of "10". OTOH 9.94 will print as "9.9". */
|
||||||
|
snprintf (buf, sizeof buf, "%.0f", number);
|
||||||
|
else if (n >= 0.95)
|
||||||
|
snprintf (buf, sizeof buf, "%.1f", number);
|
||||||
|
else if (n >= 0.001)
|
||||||
|
snprintf (buf, sizeof buf, "%.1g", number);
|
||||||
|
else if (n >= 0.0005)
|
||||||
|
/* round [0.0005, 0.001) to 0.001 */
|
||||||
|
snprintf (buf, sizeof buf, "%.3f", number);
|
||||||
|
else
|
||||||
|
/* print numbers close to 0 as 0, not 0.000 */
|
||||||
|
strcpy (buf, "0");
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
@ -126,4 +126,6 @@ int base64_decode (const char *, char *);
|
|||||||
|
|
||||||
void stable_sort (void *, size_t, size_t, int (*) (const void *, const void *));
|
void stable_sort (void *, size_t, size_t, int (*) (const void *, const void *));
|
||||||
|
|
||||||
|
const char *print_decimal (double);
|
||||||
|
|
||||||
#endif /* UTILS_H */
|
#endif /* UTILS_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user