1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.haxx.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | ***************************************************************************/ |
22 | #include "tool_setup.h" |
23 | |
24 | #ifdef HAVE_SYS_IOCTL_H |
25 | #include <sys/ioctl.h> |
26 | #endif |
27 | |
28 | #define ENABLE_CURLX_PRINTF |
29 | /* use our own printf() functions */ |
30 | #include "curlx.h" |
31 | |
32 | #include "tool_cfgable.h" |
33 | #include "tool_cb_prg.h" |
34 | #include "tool_util.h" |
35 | #include "tool_operate.h" |
36 | |
37 | #include "memdebug.h" /* keep this as LAST include */ |
38 | |
39 | #ifdef HAVE_TERMIOS_H |
40 | # include <termios.h> |
41 | #elif defined(HAVE_TERMIO_H) |
42 | # include <termio.h> |
43 | #endif |
44 | |
45 | /* 200 values generated by this perl code: |
46 | |
47 | my $pi = 3.1415; |
48 | foreach my $i (1 .. 200) { |
49 | printf "%d, ", sin($i/200 * 2 * $pi) * 5000 + 5000; |
50 | } |
51 | */ |
52 | static const unsigned int sinus[] = { |
53 | 5157, 5313, 5470, 5626, 5782, 5936, 6090, 6243, 6394, 6545, 6693, 6840, 6985, |
54 | 7128, 7269, 7408, 7545, 7679, 7810, 7938, 8064, 8187, 8306, 8422, 8535, 8644, |
55 | 8750, 8852, 8950, 9045, 9135, 9221, 9303, 9381, 9454, 9524, 9588, 9648, 9704, |
56 | 9755, 9801, 9842, 9879, 9911, 9938, 9960, 9977, 9990, 9997, 9999, 9997, 9990, |
57 | 9977, 9960, 9938, 9911, 9879, 9842, 9801, 9755, 9704, 9648, 9588, 9524, 9455, |
58 | 9381, 9303, 9221, 9135, 9045, 8950, 8852, 8750, 8645, 8535, 8422, 8306, 8187, |
59 | 8064, 7939, 7810, 7679, 7545, 7409, 7270, 7129, 6986, 6841, 6694, 6545, 6395, |
60 | 6243, 6091, 5937, 5782, 5627, 5470, 5314, 5157, 5000, 4843, 4686, 4529, 4373, |
61 | 4218, 4063, 3909, 3757, 3605, 3455, 3306, 3159, 3014, 2871, 2730, 2591, 2455, |
62 | 2321, 2190, 2061, 1935, 1813, 1693, 1577, 1464, 1355, 1249, 1147, 1049, 955, |
63 | 864, 778, 696, 618, 545, 476, 411, 351, 295, 244, 198, 157, 120, 88, 61, 39, |
64 | 22, 9, 2, 0, 2, 9, 22, 39, 61, 88, 120, 156, 198, 244, 295, 350, 410, 475, |
65 | 544, 618, 695, 777, 864, 954, 1048, 1146, 1248, 1354, 1463, 1576, 1692, 1812, |
66 | 1934, 2060, 2188, 2320, 2454, 2590, 2729, 2870, 3013, 3158, 3305, 3454, 3604, |
67 | 3755, 3908, 4062, 4216, 4372, 4528, 4685, 4842, 4999 |
68 | }; |
69 | |
70 | static void fly(struct ProgressData *bar, bool moved) |
71 | { |
72 | char buf[256]; |
73 | int pos; |
74 | int check = bar->width - 2; |
75 | |
76 | msnprintf(buf, sizeof(buf), "%*s\r" , bar->width-1, " " ); |
77 | memcpy(&buf[bar->bar], "-=O=-" , 5); |
78 | |
79 | pos = sinus[bar->tick%200] / (10000 / check); |
80 | buf[pos] = '#'; |
81 | pos = sinus[(bar->tick + 5)%200] / (10000 / check); |
82 | buf[pos] = '#'; |
83 | pos = sinus[(bar->tick + 10)%200] / (10000 / check); |
84 | buf[pos] = '#'; |
85 | pos = sinus[(bar->tick + 15)%200] / (10000 / check); |
86 | buf[pos] = '#'; |
87 | |
88 | fputs(buf, bar->out); |
89 | bar->tick += 2; |
90 | if(bar->tick >= 200) |
91 | bar->tick -= 200; |
92 | |
93 | bar->bar += (moved?bar->barmove:0); |
94 | if(bar->bar >= (bar->width - 6)) { |
95 | bar->barmove = -1; |
96 | bar->bar = bar->width - 6; |
97 | } |
98 | else if(bar->bar < 0) { |
99 | bar->barmove = 1; |
100 | bar->bar = 0; |
101 | } |
102 | } |
103 | |
104 | /* |
105 | ** callback for CURLOPT_XFERINFOFUNCTION |
106 | */ |
107 | |
108 | #define MAX_BARLENGTH 256 |
109 | |
110 | #if (SIZEOF_CURL_OFF_T == 4) |
111 | # define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) |
112 | #else |
113 | /* assume CURL_SIZEOF_CURL_OFF_T == 8 */ |
114 | # define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) |
115 | #endif |
116 | |
117 | int tool_progress_cb(void *clientp, |
118 | curl_off_t dltotal, curl_off_t dlnow, |
119 | curl_off_t ultotal, curl_off_t ulnow) |
120 | { |
121 | /* The original progress-bar source code was written for curl by Lars Aas, |
122 | and this new edition inherits some of his concepts. */ |
123 | |
124 | struct timeval now = tvnow(); |
125 | struct per_transfer *per = clientp; |
126 | struct OutStruct *outs = &per->outs; |
127 | struct OperationConfig *config = outs->config; |
128 | struct ProgressData *bar = &per->progressbar; |
129 | curl_off_t total; |
130 | curl_off_t point; |
131 | |
132 | /* Calculate expected transfer size. initial_size can be less than zero |
133 | when indicating that we are expecting to get the filesize from the |
134 | remote */ |
135 | if(bar->initial_size < 0 || |
136 | ((CURL_OFF_T_MAX - bar->initial_size) < (dltotal + ultotal))) |
137 | total = CURL_OFF_T_MAX; |
138 | else |
139 | total = dltotal + ultotal + bar->initial_size; |
140 | |
141 | /* Calculate the current progress. initial_size can be less than zero when |
142 | indicating that we are expecting to get the filesize from the remote */ |
143 | if(bar->initial_size < 0 || |
144 | ((CURL_OFF_T_MAX - bar->initial_size) < (dlnow + ulnow))) |
145 | point = CURL_OFF_T_MAX; |
146 | else |
147 | point = dlnow + ulnow + bar->initial_size; |
148 | |
149 | if(bar->calls) { |
150 | /* after first call... */ |
151 | if(total) { |
152 | /* we know the total data to get... */ |
153 | if(bar->prev == point) |
154 | /* progress didn't change since last invoke */ |
155 | return 0; |
156 | else if((tvdiff(now, bar->prevtime) < 100L) && point < total) |
157 | /* limit progress-bar updating to 10 Hz except when we're at 100% */ |
158 | return 0; |
159 | } |
160 | else { |
161 | /* total is unknown */ |
162 | if(tvdiff(now, bar->prevtime) < 100L) |
163 | /* limit progress-bar updating to 10 Hz */ |
164 | return 0; |
165 | fly(bar, point != bar->prev); |
166 | } |
167 | } |
168 | |
169 | /* simply count invokes */ |
170 | bar->calls++; |
171 | |
172 | if((total > 0) && (point != bar->prev)) { |
173 | char line[MAX_BARLENGTH + 1]; |
174 | char format[40]; |
175 | double frac; |
176 | double percent; |
177 | int barwidth; |
178 | int num; |
179 | if(point > total) |
180 | /* we have got more than the expected total! */ |
181 | total = point; |
182 | |
183 | frac = (double)point / (double)total; |
184 | percent = frac * 100.0; |
185 | barwidth = bar->width - 7; |
186 | num = (int) (((double)barwidth) * frac); |
187 | if(num > MAX_BARLENGTH) |
188 | num = MAX_BARLENGTH; |
189 | memset(line, '#', num); |
190 | line[num] = '\0'; |
191 | msnprintf(format, sizeof(format), "\r%%-%ds %%5.1f%%%%" , barwidth); |
192 | fprintf(bar->out, format, line, percent); |
193 | } |
194 | fflush(bar->out); |
195 | bar->prev = point; |
196 | bar->prevtime = now; |
197 | |
198 | if(config->readbusy) { |
199 | config->readbusy = FALSE; |
200 | curl_easy_pause(per->curl, CURLPAUSE_CONT); |
201 | } |
202 | |
203 | return 0; |
204 | } |
205 | |
206 | void progressbarinit(struct ProgressData *bar, |
207 | struct OperationConfig *config) |
208 | { |
209 | char *colp; |
210 | memset(bar, 0, sizeof(struct ProgressData)); |
211 | |
212 | /* pass this through to progress function so |
213 | * it can display progress towards total file |
214 | * not just the part that's left. (21-may-03, dbyron) */ |
215 | if(config->use_resume) |
216 | bar->initial_size = config->resume_from; |
217 | |
218 | colp = curlx_getenv("COLUMNS" ); |
219 | if(colp) { |
220 | char *endptr; |
221 | long num = strtol(colp, &endptr, 10); |
222 | if((endptr != colp) && (endptr == colp + strlen(colp)) && (num > 20) && |
223 | (num < 10000)) |
224 | bar->width = (int)num; |
225 | curl_free(colp); |
226 | } |
227 | |
228 | if(!bar->width) { |
229 | int cols = 0; |
230 | |
231 | #ifdef TIOCGSIZE |
232 | struct ttysize ts; |
233 | if(!ioctl(STDIN_FILENO, TIOCGSIZE, &ts)) |
234 | cols = ts.ts_cols; |
235 | #elif defined(TIOCGWINSZ) |
236 | struct winsize ts; |
237 | if(!ioctl(STDIN_FILENO, TIOCGWINSZ, &ts)) |
238 | cols = ts.ws_col; |
239 | #elif defined(WIN32) |
240 | { |
241 | HANDLE stderr_hnd = GetStdHandle(STD_ERROR_HANDLE); |
242 | CONSOLE_SCREEN_BUFFER_INFO console_info; |
243 | |
244 | if((stderr_hnd != INVALID_HANDLE_VALUE) && |
245 | GetConsoleScreenBufferInfo(stderr_hnd, &console_info)) { |
246 | /* |
247 | * Do not use +1 to get the true screen-width since writing a |
248 | * character at the right edge will cause a line wrap. |
249 | */ |
250 | cols = (int) |
251 | (console_info.srWindow.Right - console_info.srWindow.Left); |
252 | } |
253 | } |
254 | #endif /* TIOCGSIZE */ |
255 | bar->width = cols; |
256 | } |
257 | |
258 | if(!bar->width) |
259 | bar->width = 79; |
260 | else if(bar->width > MAX_BARLENGTH) |
261 | bar->width = MAX_BARLENGTH; |
262 | |
263 | bar->out = config->global->errors; |
264 | bar->tick = 150; |
265 | bar->barmove = 1; |
266 | } |
267 | |