-
Notifications
You must be signed in to change notification settings - Fork 421
/
progress.c
165 lines (146 loc) · 4.55 KB
/
progress.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
* Copyright (C) 2015 Bernhard Nortmann <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "progress.h"
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
/* Less reliable than clock_gettime, but does not require linking with -lrt */
inline double gettime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + (double)tv.tv_usec / 1000000.;
}
/* Calculate transfer rate (in bytes per second) */
inline double rate(size_t transferred, double elapsed)
{
if (elapsed > 0)
return (double)transferred / elapsed;
return 0.;
}
/* Estimate remaining time ("ETA") for given transfer rate */
inline double estimate(size_t remaining, double rate)
{
if (rate > 0)
return (double)remaining / rate;
return 0.;
}
/* Return ETA (in seconds) as string, formatted to minutes and seconds */
const char *format_ETA(double remaining)
{
static char result[6] = "";
int seconds = remaining + 0.5; /* simplistic round() */
if (seconds >= 0 && seconds < 6000) {
snprintf(result, sizeof(result),
"%02d:%02d", seconds / 60, seconds % 60);
return result;
}
return "--:--";
}
/* Private progress state variable */
typedef struct {
progress_cb_t callback;
size_t total;
size_t done;
double start; /* start point (timestamp) for rate and ETA calculation */
} progress_private_t;
static progress_private_t progress = {
.callback = NULL,
.start = 0.
};
/* 'External' API */
void progress_start(progress_cb_t callback, size_t expected_total)
{
progress.callback = callback;
progress.total = expected_total;
progress.done = 0;
progress.start = gettime(); /* reset start time */
}
/* Update progress status, passing information to the callback function. */
void progress_update(size_t bytes_done)
{
progress.done += bytes_done;
if (progress.callback)
progress.callback(progress.total, progress.done);
}
/* Return relative / "elapsed" time, since progress_start() */
static inline double progress_elapsed(void)
{
if (progress.start != 0.)
return gettime() - progress.start;
return 0.;
}
/* Callback function implementing a simple progress bar written to stdout */
void progress_bar(size_t total, size_t done)
{
static const int WIDTH = 48; /* # of characters to use for progress bar */
float ratio = total > 0 ? (float)done / total : 0;
int i, pos = WIDTH * ratio;
double speed = rate(done, progress_elapsed());
double eta = estimate(total - done, speed);
printf("\r%3.0f%% [", ratio * 100); /* current percentage */
for (i = 0; i < pos; i++) putchar('=');
for (i = pos; i < WIDTH; i++) putchar(' ');
if (done < total)
printf("]%6.1f kB/s, ETA %s ", kilo(speed), format_ETA(eta));
else
/* transfer complete, output totals plus a newline */
printf("] %5.0f kB, %6.1f kB/s\n", kilo(done), kilo(speed));
fflush(stdout);
}
/*
* Progress callback that emits percentage numbers, each on a separate line.
* The output is suitable for piping it into "dialog --gauge".
*
* sunxi-fel multiwrite-with-gauge <...> \
* | dialog --title "FEL upload progress" \
* --gauge "" 5 70
*/
void progress_gauge(size_t total, size_t done)
{
if (total > 0) {
printf("%.0f\n", (float)done / total * 100);
fflush(stdout);
}
}
/*
* A more sophisticated version of progress_gauge() that also updates the
* prompt (caption) with additional information. This uses a feature of
* the dialog utility that parses "XXX" delimiters - see 'man dialog'.
*
* sunxi-fel multiwrite-with-xgauge <...> \
* | dialog --title "FEL upload progress" \
* --backtitle "Please wait..." \
* --gauge "" 6 70
*/
void progress_gauge_xxx(size_t total, size_t done)
{
if (total > 0) {
double speed = rate(done, progress_elapsed());
double eta = estimate(total - done, speed);
printf("XXX\n");
printf("%.0f\n", (float)done / total * 100);
if (done < total)
printf("%zu of %zu, %.1f kB/s, ETA %s\n",
done, total, kilo(speed), format_ETA(eta));
else
printf("Done: %.1f kB, at %.1f kB/s\n",
kilo(done), kilo(speed));
printf("XXX\n");
fflush(stdout);
}
}