| 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 | |