xf.li | bfc6e71 | 2025-02-07 01:54:34 -0800 | [diff] [blame^] | 1 | /*************************************************************************** |
| 2 | * _ _ ____ _ |
| 3 | * Project ___| | | | _ \| | |
| 4 | * / __| | | | |_) | | |
| 5 | * | (__| |_| | _ <| |___ |
| 6 | * \___|\___/|_| \_\_____| |
| 7 | * |
| 8 | * Copyright (C) 1998 - 2022, 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.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 | * SPDX-License-Identifier: curl |
| 22 | * |
| 23 | ***************************************************************************/ |
| 24 | #include "tool_setup.h" |
| 25 | |
| 26 | #ifdef HAVE_SYS_IOCTL_H |
| 27 | #include <sys/ioctl.h> |
| 28 | #endif |
| 29 | |
| 30 | #define ENABLE_CURLX_PRINTF |
| 31 | /* use our own printf() functions */ |
| 32 | #include "curlx.h" |
| 33 | |
| 34 | #include "tool_cfgable.h" |
| 35 | #include "tool_cb_prg.h" |
| 36 | #include "tool_util.h" |
| 37 | #include "tool_operate.h" |
| 38 | |
| 39 | #include "memdebug.h" /* keep this as LAST include */ |
| 40 | |
| 41 | #ifdef HAVE_TERMIOS_H |
| 42 | # include <termios.h> |
| 43 | #elif defined(HAVE_TERMIO_H) |
| 44 | # include <termio.h> |
| 45 | #endif |
| 46 | |
| 47 | /* 200 values generated by this perl code: |
| 48 | |
| 49 | my $pi = 3.1415; |
| 50 | foreach my $i (1 .. 200) { |
| 51 | printf "%d, ", sin($i/200 * 2 * $pi) * 500000 + 500000; |
| 52 | } |
| 53 | */ |
| 54 | static const unsigned int sinus[] = { |
| 55 | 515704, 531394, 547052, 562664, 578214, 593687, 609068, 624341, 639491, |
| 56 | 654504, 669364, 684057, 698568, 712883, 726989, 740870, 754513, 767906, |
| 57 | 781034, 793885, 806445, 818704, 830647, 842265, 853545, 864476, 875047, |
| 58 | 885248, 895069, 904500, 913532, 922156, 930363, 938145, 945495, 952406, |
| 59 | 958870, 964881, 970434, 975522, 980141, 984286, 987954, 991139, 993840, |
| 60 | 996054, 997778, 999011, 999752, 999999, 999754, 999014, 997783, 996060, |
| 61 | 993848, 991148, 987964, 984298, 980154, 975536, 970449, 964898, 958888, |
| 62 | 952426, 945516, 938168, 930386, 922180, 913558, 904527, 895097, 885277, |
| 63 | 875077, 864507, 853577, 842299, 830682, 818739, 806482, 793922, 781072, |
| 64 | 767945, 754553, 740910, 727030, 712925, 698610, 684100, 669407, 654548, |
| 65 | 639536, 624386, 609113, 593733, 578260, 562710, 547098, 531440, 515751, |
| 66 | 500046, 484341, 468651, 452993, 437381, 421830, 406357, 390976, 375703, |
| 67 | 360552, 345539, 330679, 315985, 301474, 287158, 273052, 259170, 245525, |
| 68 | 232132, 219003, 206152, 193590, 181331, 169386, 157768, 146487, 135555, |
| 69 | 124983, 114781, 104959, 95526, 86493, 77868, 69660, 61876, 54525, 47613, |
| 70 | 41147, 35135, 29581, 24491, 19871, 15724, 12056, 8868, 6166, 3951, 2225, |
| 71 | 990, 248, 0, 244, 982, 2212, 3933, 6144, 8842, 12025, 15690, 19832, 24448, |
| 72 | 29534, 35084, 41092, 47554, 54462, 61809, 69589, 77794, 86415, 95445, |
| 73 | 104873, 114692, 124891, 135460, 146389, 157667, 169282, 181224, 193480, |
| 74 | 206039, 218888, 232015, 245406, 259048, 272928, 287032, 301346, 315856, |
| 75 | 330548, 345407, 360419, 375568, 390841, 406221, 421693, 437243, 452854, |
| 76 | 468513, 484202, 499907 |
| 77 | }; |
| 78 | |
| 79 | static void fly(struct ProgressData *bar, bool moved) |
| 80 | { |
| 81 | char buf[256]; |
| 82 | int pos; |
| 83 | int check = bar->width - 2; |
| 84 | |
| 85 | msnprintf(buf, sizeof(buf), "%*s\r", bar->width-1, " "); |
| 86 | memcpy(&buf[bar->bar], "-=O=-", 5); |
| 87 | |
| 88 | pos = sinus[bar->tick%200] / (1000000 / check); |
| 89 | buf[pos] = '#'; |
| 90 | pos = sinus[(bar->tick + 5)%200] / (1000000 / check); |
| 91 | buf[pos] = '#'; |
| 92 | pos = sinus[(bar->tick + 10)%200] / (1000000 / check); |
| 93 | buf[pos] = '#'; |
| 94 | pos = sinus[(bar->tick + 15)%200] / (1000000 / check); |
| 95 | buf[pos] = '#'; |
| 96 | |
| 97 | fputs(buf, bar->out); |
| 98 | bar->tick += 2; |
| 99 | if(bar->tick >= 200) |
| 100 | bar->tick -= 200; |
| 101 | |
| 102 | bar->bar += (moved?bar->barmove:0); |
| 103 | if(bar->bar >= (bar->width - 6)) { |
| 104 | bar->barmove = -1; |
| 105 | bar->bar = bar->width - 6; |
| 106 | } |
| 107 | else if(bar->bar < 0) { |
| 108 | bar->barmove = 1; |
| 109 | bar->bar = 0; |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | /* |
| 114 | ** callback for CURLOPT_XFERINFOFUNCTION |
| 115 | */ |
| 116 | |
| 117 | #define MAX_BARLENGTH 256 |
| 118 | |
| 119 | #if (SIZEOF_CURL_OFF_T == 4) |
| 120 | # define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF) |
| 121 | #else |
| 122 | /* assume SIZEOF_CURL_OFF_T == 8 */ |
| 123 | # define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) |
| 124 | #endif |
| 125 | |
| 126 | int tool_progress_cb(void *clientp, |
| 127 | curl_off_t dltotal, curl_off_t dlnow, |
| 128 | curl_off_t ultotal, curl_off_t ulnow) |
| 129 | { |
| 130 | struct timeval now = tvnow(); |
| 131 | struct per_transfer *per = clientp; |
| 132 | struct OperationConfig *config = per->config; |
| 133 | struct ProgressData *bar = &per->progressbar; |
| 134 | curl_off_t total; |
| 135 | curl_off_t point; |
| 136 | |
| 137 | /* Calculate expected transfer size. initial_size can be less than zero when |
| 138 | indicating that we are expecting to get the filesize from the remote */ |
| 139 | if(bar->initial_size < 0) { |
| 140 | if(dltotal || ultotal) |
| 141 | total = dltotal + ultotal; |
| 142 | else |
| 143 | total = CURL_OFF_T_MAX; |
| 144 | } |
| 145 | else if((CURL_OFF_T_MAX - bar->initial_size) < (dltotal + ultotal)) |
| 146 | total = CURL_OFF_T_MAX; |
| 147 | else |
| 148 | total = dltotal + ultotal + bar->initial_size; |
| 149 | |
| 150 | /* Calculate the current progress. initial_size can be less than zero when |
| 151 | indicating that we are expecting to get the filesize from the remote */ |
| 152 | if(bar->initial_size < 0) { |
| 153 | if(dltotal || ultotal) |
| 154 | point = dlnow + ulnow; |
| 155 | else |
| 156 | point = CURL_OFF_T_MAX; |
| 157 | } |
| 158 | else if((CURL_OFF_T_MAX - bar->initial_size) < (dlnow + ulnow)) |
| 159 | point = CURL_OFF_T_MAX; |
| 160 | else |
| 161 | point = dlnow + ulnow + bar->initial_size; |
| 162 | |
| 163 | if(bar->calls) { |
| 164 | /* after first call... */ |
| 165 | if(total) { |
| 166 | /* we know the total data to get... */ |
| 167 | if(bar->prev == point) |
| 168 | /* progress didn't change since last invoke */ |
| 169 | return 0; |
| 170 | else if((tvdiff(now, bar->prevtime) < 100L) && point < total) |
| 171 | /* limit progress-bar updating to 10 Hz except when we're at 100% */ |
| 172 | return 0; |
| 173 | } |
| 174 | else { |
| 175 | /* total is unknown */ |
| 176 | if(tvdiff(now, bar->prevtime) < 100L) |
| 177 | /* limit progress-bar updating to 10 Hz */ |
| 178 | return 0; |
| 179 | fly(bar, point != bar->prev); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | /* simply count invokes */ |
| 184 | bar->calls++; |
| 185 | |
| 186 | if((total > 0) && (point != bar->prev)) { |
| 187 | char line[MAX_BARLENGTH + 1]; |
| 188 | char format[40]; |
| 189 | double frac; |
| 190 | double percent; |
| 191 | int barwidth; |
| 192 | int num; |
| 193 | if(point > total) |
| 194 | /* we have got more than the expected total! */ |
| 195 | total = point; |
| 196 | |
| 197 | frac = (double)point / (double)total; |
| 198 | percent = frac * 100.0; |
| 199 | barwidth = bar->width - 7; |
| 200 | num = (int) (((double)barwidth) * frac); |
| 201 | if(num > MAX_BARLENGTH) |
| 202 | num = MAX_BARLENGTH; |
| 203 | memset(line, '#', num); |
| 204 | line[num] = '\0'; |
| 205 | msnprintf(format, sizeof(format), "\r%%-%ds %%5.1f%%%%", barwidth); |
| 206 | fprintf(bar->out, format, line, percent); |
| 207 | } |
| 208 | fflush(bar->out); |
| 209 | bar->prev = point; |
| 210 | bar->prevtime = now; |
| 211 | |
| 212 | if(config->readbusy) { |
| 213 | config->readbusy = FALSE; |
| 214 | curl_easy_pause(per->curl, CURLPAUSE_CONT); |
| 215 | } |
| 216 | |
| 217 | return 0; |
| 218 | } |
| 219 | |
| 220 | void progressbarinit(struct ProgressData *bar, |
| 221 | struct OperationConfig *config) |
| 222 | { |
| 223 | char *colp; |
| 224 | memset(bar, 0, sizeof(struct ProgressData)); |
| 225 | |
| 226 | /* pass the resume from value through to the progress function so it can |
| 227 | * display progress towards total file not just the part that's left. */ |
| 228 | if(config->use_resume) |
| 229 | bar->initial_size = config->resume_from; |
| 230 | |
| 231 | colp = curlx_getenv("COLUMNS"); |
| 232 | if(colp) { |
| 233 | char *endptr; |
| 234 | long num = strtol(colp, &endptr, 10); |
| 235 | if((endptr != colp) && (endptr == colp + strlen(colp)) && (num > 20) && |
| 236 | (num < 10000)) |
| 237 | bar->width = (int)num; |
| 238 | curl_free(colp); |
| 239 | } |
| 240 | |
| 241 | if(!bar->width) { |
| 242 | int cols = 0; |
| 243 | |
| 244 | #ifdef TIOCGSIZE |
| 245 | struct ttysize ts; |
| 246 | if(!ioctl(STDIN_FILENO, TIOCGSIZE, &ts)) |
| 247 | cols = ts.ts_cols; |
| 248 | #elif defined(TIOCGWINSZ) |
| 249 | struct winsize ts; |
| 250 | if(!ioctl(STDIN_FILENO, TIOCGWINSZ, &ts)) |
| 251 | cols = ts.ws_col; |
| 252 | #elif defined(WIN32) |
| 253 | { |
| 254 | HANDLE stderr_hnd = GetStdHandle(STD_ERROR_HANDLE); |
| 255 | CONSOLE_SCREEN_BUFFER_INFO console_info; |
| 256 | |
| 257 | if((stderr_hnd != INVALID_HANDLE_VALUE) && |
| 258 | GetConsoleScreenBufferInfo(stderr_hnd, &console_info)) { |
| 259 | /* |
| 260 | * Do not use +1 to get the true screen-width since writing a |
| 261 | * character at the right edge will cause a line wrap. |
| 262 | */ |
| 263 | cols = (int) |
| 264 | (console_info.srWindow.Right - console_info.srWindow.Left); |
| 265 | } |
| 266 | } |
| 267 | #endif /* TIOCGSIZE */ |
| 268 | if(cols > 20) |
| 269 | bar->width = cols; |
| 270 | } |
| 271 | |
| 272 | if(!bar->width) |
| 273 | bar->width = 79; |
| 274 | else if(bar->width > MAX_BARLENGTH) |
| 275 | bar->width = MAX_BARLENGTH; |
| 276 | |
| 277 | bar->out = config->global->errors; |
| 278 | bar->tick = 150; |
| 279 | bar->barmove = 1; |
| 280 | } |