├── install.sh ├── README.md ├── advcpmv-0.6-8.24.patch ├── advcpmv-0.5-8.21.patch ├── advcpmv-0.8-8.30.patch ├── advcpmv-0.8-8.31.patch ├── advcpmv-0.7-8.25.patch ├── advcpmv-0.8-8.28.patch ├── advcpmv-0.8-8.32.patch └── advcpmv-0.9-9.5.patch /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | ADVCPMV_VERSION=${1:-0.9} 6 | CORE_UTILS_VERSION=${2:-9.5} 7 | 8 | curl -LO https://ftp.gnu.org/gnu/coreutils/coreutils-$CORE_UTILS_VERSION.tar.xz 9 | tar xvJf coreutils-$CORE_UTILS_VERSION.tar.xz 10 | rm coreutils-$CORE_UTILS_VERSION.tar.xz 11 | ( 12 | cd coreutils-$CORE_UTILS_VERSION/ 13 | curl -LO https://raw.githubusercontent.com/jarun/advcpmv/master/advcpmv-$ADVCPMV_VERSION-$CORE_UTILS_VERSION.patch 14 | patch -p1 -i advcpmv-$ADVCPMV_VERSION-$CORE_UTILS_VERSION.patch 15 | ./configure 16 | make 17 | cp ./src/cp ../advcp 18 | cp ./src/mv ../advmv 19 | ) 20 | rm -rf coreutils-$CORE_UTILS_VERSION 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![advcpmv](https://web.archive.org/web/20131217004029im_/http://beatex.org/web/advcopy/advcpmv-screen-20130313.png) 2 | 3 | ## Advanced Copy 4 | 5 | Advanced Copy is a mod for the GNU cp and GNU mv tools which adds a progress bar and provides some info on what's going on. It was written by Florian Zwicke and released under the GPL. 6 | 7 | This repository exists because the [Advanced Copy](http://beatex.org/web/advancedcopy.html) website appears to be dead. You can still find it via the [Internet Archive](https://web.archive.org/web/20131115171331/http://beatex.org/web/advancedcopy.html). 8 | 9 | advcpmv-0.5-8.21.patch was the last patch released by the author (on February 14, 2013). advcpmv-0.6-8.24.patch (and following) are simply a rebase of that on top of the 8.24 version of coreutils. 10 | 11 | ## Build instructions 12 | 13 | Requires the following dependencies: 14 | 15 | - patch 16 | - gcc 17 | 18 | Run the following command to download, patch, compile coreutils and generate the files: `./advcpmv/advcp` and `./advcpmv/advmv`. 19 | 20 | Bash: 21 | 22 | ``` 23 | curl https://raw.githubusercontent.com/jarun/advcpmv/master/install.sh --create-dirs -o ./advcpmv/install.sh && (cd advcpmv && sh install.sh) 24 | ``` 25 | 26 | Fish: 27 | 28 | ``` 29 | curl https://raw.githubusercontent.com/jarun/advcpmv/master/install.sh --create-dirs -o ./advcpmv/install.sh; and begin; cd advcpmv; and sh install.sh; end 30 | ``` 31 | 32 | To install an older version than the latest one, you can specify the version by passing it as an argument to the install script (at the end of the command, before the closing parenthesis). For example, if you want to install `advcpmv-0.8-8.32.patch` you would modify the command above like so. 33 | 34 | ``` 35 | ... sh install.sh 0.8 8.32 ... 36 | ``` 37 | 38 | ## Usage 39 | 40 | ### Change your behaviour 41 | 42 | You can install the binaries and use `cpg -g` and `mvg -g` instead of cp and mv: 43 | 44 | ``` 45 | sudo mv ./advcpmv/advcp /usr/local/bin/cpg 46 | sudo mv ./advcpmv/advmv /usr/local/bin/mvg 47 | ``` 48 | 49 | Progress bar does not work with reflink (introduced v9.0 onwards). So reflink is disabled if using progress bar, left unchanged otherwise. 50 | 51 | ### Alias 52 | 53 | You can install the binaries and create aliases for bash (or whatever you use) 54 | 55 | ``` 56 | sudo mv ./advcpmv/advcp /usr/local/bin/ 57 | sudo mv ./advcpmv/advmv /usr/local/bin/ 58 | ``` 59 | 60 | Bash: 61 | 62 | ``` 63 | echo -e 'alias cp=\x27/usr/local/bin/cpg -g\x27' >> ~/.bashrc 64 | echo -e 'alias mv=\x27/usr/local/bin/mvg -g\x27' >> ~/.bashrc 65 | ``` 66 | 67 | Fish: 68 | 69 | ``` 70 | echo alias cp '/usr/local/bin/advcp -g' >> ~/.config/fish/config.fish 71 | echo alias mv '/usr/local/bin/advmv -g' >> ~/.config/fish/config.fish 72 | ``` 73 | 74 | ``` 75 | ## Upstream merge 76 | 77 | The original author sent the patch to the team, that maintains the GNU CoreUtils. They won't merge this patch, because mv and cp are feature complete. 78 | ``` 79 | -------------------------------------------------------------------------------- /advcpmv-0.6-8.24.patch: -------------------------------------------------------------------------------- 1 | Only in coreutils-8.24-patch0.6: advcpmv-0.5-8.21.patch 2 | diff -crB coreutils-8.24/src/copy.c coreutils-8.24-patch0.6/src/copy.c 3 | *** coreutils-8.24/src/copy.c 2015-06-26 10:05:22.000000000 -0700 4 | --- coreutils-8.24-patch0.6/src/copy.c 2015-08-16 23:54:17.000000000 -0700 5 | *************** 6 | *** 196,201 **** 7 | --- 196,251 ---- 8 | } 9 | 10 | 11 | + /* BEGIN progress mod */ 12 | + static void file_progress_bar ( char * _cDest, int _iBarLength, int _iProgress, int _iTotal ) 13 | + { 14 | + // write number to progress bar 15 | + float fPercent = ( float ) _iProgress / ( float ) _iTotal * 100.f; 16 | + sprintf ( _cDest + ( _iBarLength - 6 ), "%4.1f", fPercent ); 17 | + // remove zero 18 | + _cDest[_iBarLength - 2] = ' '; 19 | + 20 | + // fill rest with '-' 21 | + int i; 22 | + for ( i = 1; i <= _iBarLength - 9; i++ ) 23 | + { 24 | + if ( fPercent > ( float ) ( i - 1 ) / ( _iBarLength - 10 ) * 100.f ) 25 | + _cDest[i] = '|'; 26 | + else 27 | + _cDest[i] = '-'; 28 | + } 29 | + } 30 | + 31 | + int file_size_format ( char * _cDst, int _iSize, int _iCounter ) 32 | + { 33 | + int iCounter = _iCounter; 34 | + double dSize = ( double ) _iSize; 35 | + while ( dSize >= 1000. ) 36 | + { 37 | + dSize /= 1024.; 38 | + iCounter++; 39 | + } 40 | + 41 | + /* get unit */ 42 | + char * sUnit; 43 | + if ( iCounter == 0 ) 44 | + sUnit = "B"; 45 | + else if ( iCounter == 1 ) 46 | + sUnit = "KiB"; 47 | + else if ( iCounter == 2 ) 48 | + sUnit = "MiB"; 49 | + else if ( iCounter == 3 ) 50 | + sUnit = "GiB"; 51 | + else if ( iCounter == 4 ) 52 | + sUnit = "TiB"; 53 | + else 54 | + sUnit = "N/A"; 55 | + 56 | + /* write number */ 57 | + return sprintf ( _cDst, "%5.1f %s", dSize, sUnit ); 58 | + } 59 | + /* END progress mod */ 60 | + 61 | /* Copy the regular file open on SRC_FD/SRC_NAME to DST_FD/DST_NAME, 62 | honoring the MAKE_HOLES setting and using the BUF_SIZE-byte buffer 63 | BUF for temporary storage. Copy no more than MAX_N_READ bytes. 64 | *************** 65 | *** 315,320 **** 66 | --- 365,383 ---- 67 | for each file. Unfortunately that doesn't work for 68 | certain files in /proc or /sys with linux kernels. */ 69 | } 70 | + 71 | + if (progress) { 72 | + /* BEGIN progress mod */ 73 | + /* update total size */ 74 | + g_iTotalWritten += *total_n_read / 1024; 75 | + g_iFilesCopied++; 76 | + 77 | + int i; 78 | + for ( i = 0; i < 6; i++ ) 79 | + free ( cProgressField[i] ); 80 | + free ( cProgressField ); 81 | + /* END progress mod */ 82 | + } 83 | 84 | /* Ensure a trailing hole is created, so that subsequent 85 | calls of sparse_copy() start at the correct offset. */ 86 | diff -crB coreutils-8.24/src/copy.h coreutils-8.24-patch0.6/src/copy.h 87 | *** coreutils-8.24/src/copy.h 2015-06-26 10:04:19.000000000 -0700 88 | --- coreutils-8.24-patch0.6/src/copy.h 2015-08-16 23:54:17.000000000 -0700 89 | *************** 90 | *** 230,235 **** 91 | --- 230,238 ---- 92 | /* If true, create symbolic links instead of copying files. 93 | Create destination directories as usual. */ 94 | bool symbolic_link; 95 | + 96 | + /* If true, draw a nice progress bar on screen */ 97 | + bool progress_bar; 98 | 99 | /* If true, do not copy a nondirectory that has an existing destination 100 | with the same or newer modification time. */ 101 | *************** 102 | *** 289,292 **** 103 | --- 292,306 ---- 104 | bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE; 105 | mode_t cached_umask (void); 106 | 107 | + /* BEGIN progress mod */ 108 | + int file_size_format ( char * _cDst, int _iSize, int _iCounter ); 109 | + 110 | + long g_iTotalSize; 111 | + long g_iTotalWritten; 112 | + int g_iFilesCopied; 113 | + struct timeval g_oStartTime; 114 | + int g_iTotalFiles; 115 | + bool progress; 116 | + /* END progress mod */ 117 | + 118 | #endif 119 | diff -crB coreutils-8.24/src/cp.c coreutils-8.24-patch0.6/src/cp.c 120 | *** coreutils-8.24/src/cp.c 2015-06-26 10:04:19.000000000 -0700 121 | --- coreutils-8.24-patch0.6/src/cp.c 2015-08-16 23:56:08.000000000 -0700 122 | *************** 123 | *** 141,146 **** 124 | --- 141,147 ---- 125 | {"target-directory", required_argument, NULL, 't'}, 126 | {"update", no_argument, NULL, 'u'}, 127 | {"verbose", no_argument, NULL, 'v'}, 128 | + {"progress-bar", no_argument, NULL, 'g'}, 129 | {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, 130 | {GETOPT_HELP_OPTION_DECL}, 131 | {GETOPT_VERSION_OPTION_DECL}, 132 | *************** 133 | *** 179,184 **** 134 | --- 180,186 ---- 135 | -f, --force if an existing destination file cannot be\n\ 136 | opened, remove it and try again (this option\n\ 137 | is ignored when the -n option is also used)\n\ 138 | + -g, --progress-bar add progress-bar\n\ 139 | -i, --interactive prompt before overwrite (overrides a previous -n\ 140 | \n\ 141 | option)\n\ 142 | *************** 143 | *** 624,629 **** 144 | --- 626,682 ---- 145 | error (EXIT_FAILURE, 0, _("target %s is not a directory"), 146 | quote (file[n_files - 1])); 147 | } 148 | + 149 | + /* BEGIN progress mod */ 150 | + struct timeval start_time; 151 | + if (progress) { 152 | + g_iTotalSize = 0; 153 | + g_iFilesCopied = 0; 154 | + g_iTotalWritten = 0; 155 | + 156 | + /* save time */ 157 | + gettimeofday ( & start_time, NULL ); 158 | + g_oStartTime = start_time; 159 | + 160 | + printf ( "Calculating total size... \r" ); 161 | + fflush ( stdout ); 162 | + long iTotalSize = 0; 163 | + int iFiles = n_files; 164 | + if ( ! target_directory ) 165 | + iFiles = n_files - 1; 166 | + int j; 167 | + for (j = 0; j < iFiles; j++) 168 | + { 169 | + /* call du -s for each file */ 170 | + /* create command */ 171 | + char command[1024]; 172 | + sprintf ( command, "du -s \"%s\"", file[j] ); 173 | + /* TODO: replace all quote signs in file[i] */ 174 | + 175 | + FILE *fp; 176 | + char output[1024]; 177 | + 178 | + /* run command */ 179 | + fp = popen(command, "r"); 180 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 181 | + printf("failed to run du.\n" ); 182 | + } 183 | + else 184 | + { 185 | + /* isolate size */ 186 | + strchr ( output, '\t' )[0] = '\0'; 187 | + iTotalSize += atol ( output ); 188 | + 189 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 190 | + fflush ( stdout ); 191 | + } 192 | + 193 | + /* close */ 194 | + pclose(fp); 195 | + } 196 | + g_iTotalSize = iTotalSize; 197 | + } 198 | + /* END progress mod */ 199 | 200 | if (target_directory) 201 | { 202 | *************** 203 | *** 766,771 **** 204 | --- 819,864 ---- 205 | 206 | ok = copy (source, new_dest, 0, x, &unused, NULL); 207 | } 208 | + 209 | + /* BEGIN progress mod */ 210 | + if (progress) { 211 | + /* remove everything */ 212 | + int i; 213 | + if ( g_iTotalSize ) 214 | + { 215 | + for ( i = 0; i < 6; i++ ) 216 | + printf ( "\033[K\n" ); 217 | + printf ( "\r\033[6A" ); 218 | + } 219 | + else 220 | + { 221 | + for ( i = 0; i < 3; i++ ) 222 | + printf ( "\033[K\n" ); 223 | + printf ( "\r\033[3A" ); 224 | + } 225 | + 226 | + /* save time */ 227 | + struct timeval end_time; 228 | + gettimeofday ( & end_time, NULL ); 229 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 230 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 231 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 232 | + 233 | + /* get total size */ 234 | + char sTotalWritten[20]; 235 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 236 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 237 | + 238 | + /* calculate speed */ 239 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 240 | + char s_copy_speed[20]; 241 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 242 | + 243 | + /* good-bye message */ 244 | + printf ( "%d files (%s) copied in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 245 | + sec_elapsed, s_copy_speed ); 246 | + } 247 | + /* END progress mod */ 248 | 249 | return ok; 250 | } 251 | *************** 252 | *** 801,806 **** 253 | --- 894,900 ---- 254 | x->recursive = false; 255 | x->sparse_mode = SPARSE_AUTO; 256 | x->symbolic_link = false; 257 | + x->progress_bar = false; 258 | x->set_mode = false; 259 | x->mode = 0; 260 | 261 | *************** 262 | *** 943,949 **** 263 | we'll actually use backup_suffix_string. */ 264 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 265 | 266 | ! while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", 267 | long_opts, NULL)) 268 | != -1) 269 | { 270 | --- 1037,1043 ---- 271 | we'll actually use backup_suffix_string. */ 272 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 273 | 274 | ! while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ", 275 | long_opts, NULL)) 276 | != -1) 277 | { 278 | *************** 279 | *** 1000,1005 **** 280 | --- 1094,1103 ---- 281 | x.unlink_dest_after_failed_open = true; 282 | break; 283 | 284 | + case 'g': 285 | + progress = true; 286 | + break; 287 | + 288 | case 'H': 289 | x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; 290 | break; 291 | diff -crB coreutils-8.24/src/mv.c coreutils-8.24-patch0.6/src/mv.c 292 | *** coreutils-8.24/src/mv.c 2015-06-26 10:04:19.000000000 -0700 293 | --- coreutils-8.24-patch0.6/src/mv.c 2015-08-16 23:56:44.000000000 -0700 294 | *************** 295 | *** 65,70 **** 296 | --- 65,71 ---- 297 | {"target-directory", required_argument, NULL, 't'}, 298 | {"update", no_argument, NULL, 'u'}, 299 | {"verbose", no_argument, NULL, 'v'}, 300 | + {"progress-bar", no_argument, NULL, 'g'}, 301 | {GETOPT_HELP_OPTION_DECL}, 302 | {GETOPT_VERSION_OPTION_DECL}, 303 | {NULL, 0, NULL, 0} 304 | *************** 305 | *** 167,173 **** 306 | bool copy_into_self; 307 | bool rename_succeeded; 308 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 309 | ! 310 | if (ok) 311 | { 312 | char const *dir_to_remove; 313 | --- 168,174 ---- 314 | bool copy_into_self; 315 | bool rename_succeeded; 316 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 317 | ! 318 | if (ok) 319 | { 320 | char const *dir_to_remove; 321 | *************** 322 | *** 302,307 **** 323 | --- 303,309 ---- 324 | \n\ 325 | -b like --backup but does not accept an argument\n\ 326 | -f, --force do not prompt before overwriting\n\ 327 | + -g, --progress-bar add progress-bar\n\ 328 | -i, --interactive prompt before overwrite\n\ 329 | -n, --no-clobber do not overwrite an existing file\n\ 330 | If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ 331 | *************** 332 | *** 373,379 **** 333 | we'll actually use backup_suffix_string. */ 334 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 335 | 336 | ! while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL)) 337 | != -1) 338 | { 339 | switch (c) 340 | --- 375,381 ---- 341 | we'll actually use backup_suffix_string. */ 342 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 343 | 344 | ! while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL)) 345 | != -1) 346 | { 347 | switch (c) 348 | *************** 349 | *** 419,424 **** 350 | --- 421,429 ---- 351 | case 'v': 352 | x.verbose = true; 353 | break; 354 | + case 'g': 355 | + progress = true; 356 | + break; 357 | case 'S': 358 | make_backups = true; 359 | backup_suffix_string = optarg; 360 | *************** 361 | *** 490,495 **** 362 | --- 495,551 ---- 363 | : no_backups); 364 | 365 | hash_init (); 366 | + 367 | + /* BEGIN progress mod */ 368 | + struct timeval start_time; 369 | + 370 | + if(progress) { 371 | + g_iTotalSize = 0; 372 | + g_iFilesCopied = 0; 373 | + g_iTotalWritten = 0; 374 | + 375 | + gettimeofday (& start_time, NULL); 376 | + g_oStartTime = start_time; 377 | + 378 | + printf ("Calculating total size... \r"); 379 | + fflush (stdout); 380 | + long iTotalSize = 0; 381 | + int iFiles = n_files; 382 | + if ( !target_directory ) 383 | + iFiles = 1; 384 | + int j; 385 | + for (j = 0; j < iFiles; j++) 386 | + { 387 | + /* call du -s for each file */ 388 | + /* create command */ 389 | + char command[1024]; 390 | + sprintf ( command, "du -s \"%s\"", file[j] ); 391 | + /* TODO: replace all quote signs in file[i] */ 392 | + 393 | + FILE *fp; 394 | + char output[1024]; 395 | + 396 | + /* run command */ 397 | + fp = popen(command, "r"); 398 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 399 | + printf("failed to run du.\n" ); 400 | + } 401 | + else 402 | + { 403 | + /* isolate size */ 404 | + strchr ( output, '\t' )[0] = '\0'; 405 | + iTotalSize += atol ( output ); 406 | + 407 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 408 | + fflush ( stdout ); 409 | + } 410 | + 411 | + /* close */ 412 | + pclose(fp); 413 | + } 414 | + g_iTotalSize = iTotalSize; 415 | + } 416 | + /* END progress mod */ 417 | 418 | if (target_directory) 419 | { 420 | *************** 421 | *** 507,512 **** 422 | --- 563,608 ---- 423 | } 424 | else 425 | ok = movefile (file[0], file[1], false, &x); 426 | + 427 | + /* BEGIN progress mod */ 428 | + if (progress) { 429 | + /* remove everything */ 430 | + int i; 431 | + if ( g_iTotalSize ) 432 | + { 433 | + for ( i = 0; i < 6; i++ ) 434 | + printf ( "\033[K\n" ); 435 | + printf ( "\r\033[6A" ); 436 | + } 437 | + else 438 | + { 439 | + for ( i = 0; i < 3; i++ ) 440 | + printf ( "\033[K\n" ); 441 | + printf ( "\r\033[3A" ); 442 | + } 443 | + 444 | + /* save time */ 445 | + struct timeval end_time; 446 | + gettimeofday ( & end_time, NULL ); 447 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 448 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 449 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 450 | + 451 | + /* get total size */ 452 | + char sTotalWritten[20]; 453 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 454 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 455 | + 456 | + /* calculate speed */ 457 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 458 | + char s_copy_speed[20]; 459 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 460 | + 461 | + /* good-bye message */ 462 | + printf ( "%d files (%s) moved in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 463 | + sec_elapsed, s_copy_speed ); 464 | + } 465 | + /* END progress mod */ 466 | 467 | return ok ? EXIT_SUCCESS : EXIT_FAILURE; 468 | } 469 | -------------------------------------------------------------------------------- /advcpmv-0.5-8.21.patch: -------------------------------------------------------------------------------- 1 | diff -crB coreutils-8.21/src/copy.c coreutils-8.21-patch0.5/src/copy.c 2 | *** coreutils-8.21/src/copy.c 2013-02-07 10:37:05.000000000 +0100 3 | --- coreutils-8.21-patch0.5/src/copy.c 2013-02-23 12:53:51.000000000 +0100 4 | *************** 5 | *** 135,140 **** 6 | --- 135,190 ---- 7 | return err; 8 | } 9 | 10 | + /* BEGIN progress mod */ 11 | + static void file_progress_bar ( char * _cDest, int _iBarLength, int _iProgress, int _iTotal ) 12 | + { 13 | + // write number to progress bar 14 | + float fPercent = ( float ) _iProgress / ( float ) _iTotal * 100.f; 15 | + sprintf ( _cDest + ( _iBarLength - 6 ), "%4.1f", fPercent ); 16 | + // remove zero 17 | + _cDest[_iBarLength - 2] = ' '; 18 | + 19 | + // fill rest with '-' 20 | + int i; 21 | + for ( i = 1; i <= _iBarLength - 9; i++ ) 22 | + { 23 | + if ( fPercent > ( float ) ( i - 1 ) / ( _iBarLength - 10 ) * 100.f ) 24 | + _cDest[i] = '|'; 25 | + else 26 | + _cDest[i] = '-'; 27 | + } 28 | + } 29 | + 30 | + int file_size_format ( char * _cDst, int _iSize, int _iCounter ) 31 | + { 32 | + int iCounter = _iCounter; 33 | + double dSize = ( double ) _iSize; 34 | + while ( dSize >= 1000. ) 35 | + { 36 | + dSize /= 1024.; 37 | + iCounter++; 38 | + } 39 | + 40 | + /* get unit */ 41 | + char * sUnit; 42 | + if ( iCounter == 0 ) 43 | + sUnit = "B"; 44 | + else if ( iCounter == 1 ) 45 | + sUnit = "KiB"; 46 | + else if ( iCounter == 2 ) 47 | + sUnit = "MiB"; 48 | + else if ( iCounter == 3 ) 49 | + sUnit = "GiB"; 50 | + else if ( iCounter == 4 ) 51 | + sUnit = "TiB"; 52 | + else 53 | + sUnit = "N/A"; 54 | + 55 | + /* write number */ 56 | + return sprintf ( _cDst, "%5.1f %s", dSize, sUnit ); 57 | + } 58 | + /* END progress mod */ 59 | + 60 | /* Copy the regular file open on SRC_FD/SRC_NAME to DST_FD/DST_NAME, 61 | honoring the MAKE_HOLES setting and using the BUF_SIZE-byte buffer 62 | BUF for temporary storage. Copy no more than MAX_N_READ bytes. 63 | *************** 64 | *** 151,163 **** 65 | bool make_holes, 66 | char const *src_name, char const *dst_name, 67 | uintmax_t max_n_read, off_t *total_n_read, 68 | ! bool *last_write_made_hole) 69 | { 70 | *last_write_made_hole = false; 71 | *total_n_read = 0; 72 | 73 | while (max_n_read) 74 | { 75 | bool make_hole = false; 76 | 77 | ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size)); 78 | --- 201,369 ---- 79 | bool make_holes, 80 | char const *src_name, char const *dst_name, 81 | uintmax_t max_n_read, off_t *total_n_read, 82 | ! bool *last_write_made_hole 83 | ! ) 84 | { 85 | + /* BEGIN progress mod */ 86 | + /* create a field of 6 lines */ 87 | + char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) ); 88 | + /* get console width */ 89 | + int iBarLength = 80; 90 | + struct winsize win; 91 | + if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 ) 92 | + iBarLength = win.ws_col; 93 | + /* create rows */ 94 | + int it; 95 | + for ( it = 0; it < 6; it++ ) 96 | + { 97 | + cProgressField[it] = ( char * ) malloc ( iBarLength + 1 ); 98 | + /* init with spaces */ 99 | + int j; 100 | + for ( j = 0; j < iBarLength; j++ ) 101 | + cProgressField[it][j] = ' '; 102 | + cProgressField[it][iBarLength] = '\0'; 103 | + } 104 | + 105 | + /* global progress bar? */ 106 | + if ( g_iTotalSize ) 107 | + { 108 | + /* init global progress bar */ 109 | + cProgressField[2][0] = '['; 110 | + cProgressField[2][iBarLength - 8] = ']'; 111 | + cProgressField[2][iBarLength - 7] = ' '; 112 | + cProgressField[2][iBarLength - 1] = '%'; 113 | + 114 | + /* total size */ 115 | + cProgressField[1][iBarLength - 11] = '/'; 116 | + file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 ); 117 | + 118 | + /* show how many files were written */ 119 | + int sum_length = sprintf ( cProgressField[1], "%d files copied so far...", g_iFilesCopied ); 120 | + cProgressField[1][sum_length] = ' '; 121 | + } 122 | + 123 | + /* truncate filename? */ 124 | + int fn_length; 125 | + if ( strlen ( src_name ) > iBarLength - 22 ) 126 | + fn_length = 127 | + sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) ); 128 | + else 129 | + fn_length = sprintf ( cProgressField[4], "%s", src_name ); 130 | + cProgressField[4][fn_length] = ' '; 131 | + 132 | + /* filesize */ 133 | + int file_size = max_n_read; 134 | + struct stat file_stat; 135 | + if (fstat(src_fd, & file_stat) == 0) 136 | + file_size = file_stat.st_size; 137 | + cProgressField[4][iBarLength - 11] = '/'; 138 | + file_size_format ( cProgressField[4] + iBarLength - 9, file_size, 0 ); 139 | + 140 | + int iCountDown = 1; 141 | + char * sProgressBar = cProgressField[5]; 142 | + sProgressBar[0] = '['; 143 | + sProgressBar[iBarLength - 8] = ']'; 144 | + sProgressBar[iBarLength - 7] = ' '; 145 | + sProgressBar[iBarLength - 1] = '%'; 146 | + 147 | + /* this will always save the time in between */ 148 | + struct timeval last_time; 149 | + gettimeofday ( & last_time, NULL ); 150 | + int last_size = g_iTotalWritten; 151 | + /* END progress mod */ 152 | + 153 | *last_write_made_hole = false; 154 | *total_n_read = 0; 155 | 156 | while (max_n_read) 157 | { 158 | + /* BEGIN progress mod */ 159 | + if (progress) { 160 | + /* update countdown */ 161 | + iCountDown--; 162 | + if ( iCountDown < 0 ) 163 | + { 164 | + /* average copy speed is assumed to be around 10 MiB/s, just to be safe. 165 | + * the status should be updated about 10 times per second, or approximately 166 | + * once per 1 MiB transferred. */ 167 | + iCountDown = 1024 * 1024 / buf_size; 168 | + /* must be greater than 0 */ 169 | + if (iCountDown < 1) 170 | + iCountDown = 1; 171 | + /* limit */ 172 | + if (iCountDown > 100) 173 | + iCountDown = 100; 174 | + } 175 | + 176 | + /* just print one line with the percentage, but not always */ 177 | + if ( iCountDown == 0 ) 178 | + { 179 | + /* calculate current speed */ 180 | + struct timeval cur_time; 181 | + gettimeofday ( & cur_time, NULL ); 182 | + int cur_size = g_iTotalWritten + *total_n_read / 1024; 183 | + int usec_elapsed = cur_time.tv_usec - last_time.tv_usec; 184 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 185 | + sec_elapsed += ( double ) ( cur_time.tv_sec - last_time.tv_sec ); 186 | + int copy_speed = ( int ) ( ( double ) ( cur_size - last_size ) 187 | + / sec_elapsed ); 188 | + if (copy_speed < 0) 189 | + copy_speed = 0; 190 | + char s_copy_speed[20]; 191 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 192 | + /* update vars */ 193 | + last_time = cur_time; 194 | + last_size = cur_size; 195 | + 196 | + /* how much time has passed since the start? */ 197 | + int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec; 198 | + int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size 199 | + * g_iTotalSize ) - isec_elapsed; 200 | + int min_remaining = sec_remaining / 60; 201 | + sec_remaining -= min_remaining * 60; 202 | + int hours_remaining = min_remaining / 60; 203 | + min_remaining -= hours_remaining * 60; 204 | + /* print out */ 205 | + sprintf ( cProgressField[3], 206 | + "Copying at %s/s (about %dh %dm %ds remaining)", s_copy_speed, 207 | + hours_remaining, min_remaining, sec_remaining ); 208 | + 209 | + int fs_len; 210 | + if ( g_iTotalSize ) 211 | + { 212 | + /* global progress bar */ 213 | + file_progress_bar ( cProgressField[2], iBarLength, 214 | + g_iTotalWritten + *total_n_read / 1024, g_iTotalSize ); 215 | + 216 | + /* print the global status */ 217 | + fs_len = file_size_format ( cProgressField[1] + iBarLength - 21, 218 | + g_iTotalWritten + *total_n_read / 1024, 1 ); 219 | + cProgressField[1][iBarLength - 21 + fs_len] = ' '; 220 | + } 221 | + 222 | + /* current progress bar */ 223 | + file_progress_bar ( sProgressBar, iBarLength, *total_n_read, file_size ); 224 | + 225 | + /* print the status */ 226 | + fs_len = file_size_format ( cProgressField[4] + iBarLength - 21, *total_n_read, 0 ); 227 | + cProgressField[4][iBarLength - 21 + fs_len] = ' '; 228 | + 229 | + /* print the field */ 230 | + for ( it = g_iTotalSize ? 0 : 3; it < 6; it++ ) 231 | + { 232 | + printf ( "\033[K%s\n", cProgressField[it] ); 233 | + if ( strlen ( cProgressField[it] ) < iBarLength ) 234 | + printf ( "" ); 235 | + } 236 | + if ( g_iTotalSize ) 237 | + printf ( "\r\033[6A" ); 238 | + else 239 | + printf ( "\r\033[3A" ); 240 | + fflush ( stdout ); 241 | + } 242 | + } 243 | + /* END progress mod */ 244 | + 245 | bool make_hole = false; 246 | 247 | ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size)); 248 | *************** 249 | *** 215,220 **** 250 | --- 421,439 ---- 251 | 252 | *last_write_made_hole = make_hole; 253 | } 254 | + 255 | + if (progress) { 256 | + /* BEGIN progress mod */ 257 | + /* update total size */ 258 | + g_iTotalWritten += *total_n_read / 1024; 259 | + g_iFilesCopied++; 260 | + 261 | + int i; 262 | + for ( i = 0; i < 6; i++ ) 263 | + free ( cProgressField[i] ); 264 | + free ( cProgressField ); 265 | + /* END progress mod */ 266 | + } 267 | 268 | return true; 269 | } 270 | diff -crB coreutils-8.21/src/copy.h coreutils-8.21-patch0.5/src/copy.h 271 | *** coreutils-8.21/src/copy.h 2013-01-31 01:46:24.000000000 +0100 272 | --- coreutils-8.21-patch0.5/src/copy.h 2013-02-23 12:53:51.000000000 +0100 273 | *************** 274 | *** 227,232 **** 275 | --- 227,235 ---- 276 | /* If true, create symbolic links instead of copying files. 277 | Create destination directories as usual. */ 278 | bool symbolic_link; 279 | + 280 | + /* If true, draw a nice progress bar on screen */ 281 | + bool progress_bar; 282 | 283 | /* If true, do not copy a nondirectory that has an existing destination 284 | with the same or newer modification time. */ 285 | *************** 286 | *** 286,289 **** 287 | --- 289,303 ---- 288 | bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE; 289 | mode_t cached_umask (void); 290 | 291 | + /* BEGIN progress mod */ 292 | + int file_size_format ( char * _cDst, int _iSize, int _iCounter ); 293 | + 294 | + long g_iTotalSize; 295 | + long g_iTotalWritten; 296 | + int g_iFilesCopied; 297 | + struct timeval g_oStartTime; 298 | + int g_iTotalFiles; 299 | + bool progress; 300 | + /* END progress mod */ 301 | + 302 | #endif 303 | diff -crB coreutils-8.21/src/cp.c coreutils-8.21-patch0.5/src/cp.c 304 | *** coreutils-8.21/src/cp.c 2013-02-07 10:37:05.000000000 +0100 305 | --- coreutils-8.21-patch0.5/src/cp.c 2013-02-23 12:53:51.000000000 +0100 306 | *************** 307 | *** 141,146 **** 308 | --- 141,147 ---- 309 | {"target-directory", required_argument, NULL, 't'}, 310 | {"update", no_argument, NULL, 'u'}, 311 | {"verbose", no_argument, NULL, 'v'}, 312 | + {"progress-bar", no_argument, NULL, 'g'}, 313 | {GETOPT_HELP_OPTION_DECL}, 314 | {GETOPT_VERSION_OPTION_DECL}, 315 | {NULL, 0, NULL, 0} 316 | *************** 317 | *** 178,183 **** 318 | --- 179,185 ---- 319 | -f, --force if an existing destination file cannot be\n\ 320 | opened, remove it and try again (this option\n\ 321 | is ignored when the -n option is also used)\n\ 322 | + -g, --progress-bar add progress-bar\n\ 323 | -i, --interactive prompt before overwrite (overrides a previous -n\ 324 | \n\ 325 | option)\n\ 326 | *************** 327 | *** 617,622 **** 328 | --- 619,675 ---- 329 | error (EXIT_FAILURE, 0, _("target %s is not a directory"), 330 | quote (file[n_files - 1])); 331 | } 332 | + 333 | + /* BEGIN progress mod */ 334 | + struct timeval start_time; 335 | + if (progress) { 336 | + g_iTotalSize = 0; 337 | + g_iFilesCopied = 0; 338 | + g_iTotalWritten = 0; 339 | + 340 | + /* save time */ 341 | + gettimeofday ( & start_time, NULL ); 342 | + g_oStartTime = start_time; 343 | + 344 | + printf ( "Calculating total size... \r" ); 345 | + fflush ( stdout ); 346 | + long iTotalSize = 0; 347 | + int iFiles = n_files; 348 | + if ( ! target_directory ) 349 | + iFiles = n_files - 1; 350 | + int j; 351 | + for (j = 0; j < iFiles; j++) 352 | + { 353 | + /* call du -s for each file */ 354 | + /* create command */ 355 | + char command[1024]; 356 | + sprintf ( command, "du -s \"%s\"", file[j] ); 357 | + /* TODO: replace all quote signs in file[i] */ 358 | + 359 | + FILE *fp; 360 | + char output[1024]; 361 | + 362 | + /* run command */ 363 | + fp = popen(command, "r"); 364 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 365 | + printf("failed to run du.\n" ); 366 | + } 367 | + else 368 | + { 369 | + /* isolate size */ 370 | + strchr ( output, '\t' )[0] = '\0'; 371 | + iTotalSize += atol ( output ); 372 | + 373 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 374 | + fflush ( stdout ); 375 | + } 376 | + 377 | + /* close */ 378 | + pclose(fp); 379 | + } 380 | + g_iTotalSize = iTotalSize; 381 | + } 382 | + /* END progress mod */ 383 | 384 | if (target_directory) 385 | { 386 | *************** 387 | *** 759,764 **** 388 | --- 812,857 ---- 389 | 390 | ok = copy (source, new_dest, 0, x, &unused, NULL); 391 | } 392 | + 393 | + /* BEGIN progress mod */ 394 | + if (progress) { 395 | + /* remove everything */ 396 | + int i; 397 | + if ( g_iTotalSize ) 398 | + { 399 | + for ( i = 0; i < 6; i++ ) 400 | + printf ( "\033[K\n" ); 401 | + printf ( "\r\033[6A" ); 402 | + } 403 | + else 404 | + { 405 | + for ( i = 0; i < 3; i++ ) 406 | + printf ( "\033[K\n" ); 407 | + printf ( "\r\033[3A" ); 408 | + } 409 | + 410 | + /* save time */ 411 | + struct timeval end_time; 412 | + gettimeofday ( & end_time, NULL ); 413 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 414 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 415 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 416 | + 417 | + /* get total size */ 418 | + char sTotalWritten[20]; 419 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 420 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 421 | + 422 | + /* calculate speed */ 423 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 424 | + char s_copy_speed[20]; 425 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 426 | + 427 | + /* good-bye message */ 428 | + printf ( "%d files (%s) copied in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 429 | + sec_elapsed, s_copy_speed ); 430 | + } 431 | + /* END progress mod */ 432 | 433 | return ok; 434 | } 435 | *************** 436 | *** 793,798 **** 437 | --- 886,892 ---- 438 | x->recursive = false; 439 | x->sparse_mode = SPARSE_AUTO; 440 | x->symbolic_link = false; 441 | + x->progress_bar = false; 442 | x->set_mode = false; 443 | x->mode = 0; 444 | 445 | *************** 446 | *** 933,939 **** 447 | we'll actually use backup_suffix_string. */ 448 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 449 | 450 | ! while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:T", 451 | long_opts, NULL)) 452 | != -1) 453 | { 454 | --- 1027,1033 ---- 455 | we'll actually use backup_suffix_string. */ 456 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 457 | 458 | ! while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:T", 459 | long_opts, NULL)) 460 | != -1) 461 | { 462 | *************** 463 | *** 990,995 **** 464 | --- 1084,1093 ---- 465 | x.unlink_dest_after_failed_open = true; 466 | break; 467 | 468 | + case 'g': 469 | + progress = true; 470 | + break; 471 | + 472 | case 'H': 473 | x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; 474 | break; 475 | diff -crB coreutils-8.21/src/mv.c coreutils-8.21-patch0.5/src/mv.c 476 | *** coreutils-8.21/src/mv.c 2013-02-07 10:37:05.000000000 +0100 477 | --- coreutils-8.21-patch0.5/src/mv.c 2013-03-12 21:23:58.000000000 +0100 478 | *************** 479 | *** 64,69 **** 480 | --- 64,70 ---- 481 | {"target-directory", required_argument, NULL, 't'}, 482 | {"update", no_argument, NULL, 'u'}, 483 | {"verbose", no_argument, NULL, 'v'}, 484 | + {"progress-bar", no_argument, NULL, 'g'}, 485 | {GETOPT_HELP_OPTION_DECL}, 486 | {GETOPT_VERSION_OPTION_DECL}, 487 | {NULL, 0, NULL, 0} 488 | *************** 489 | *** 165,171 **** 490 | bool copy_into_self; 491 | bool rename_succeeded; 492 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 493 | ! 494 | if (ok) 495 | { 496 | char const *dir_to_remove; 497 | --- 166,172 ---- 498 | bool copy_into_self; 499 | bool rename_succeeded; 500 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 501 | ! 502 | if (ok) 503 | { 504 | char const *dir_to_remove; 505 | *************** 506 | *** 300,305 **** 507 | --- 301,307 ---- 508 | \n\ 509 | -b like --backup but does not accept an argument\n\ 510 | -f, --force do not prompt before overwriting\n\ 511 | + -g, --progress-bar add progress-bar\n\ 512 | -i, --interactive prompt before overwrite\n\ 513 | -n, --no-clobber do not overwrite an existing file\n\ 514 | If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ 515 | *************** 516 | *** 368,374 **** 517 | we'll actually use backup_suffix_string. */ 518 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 519 | 520 | ! while ((c = getopt_long (argc, argv, "bfint:uvS:T", long_options, NULL)) 521 | != -1) 522 | { 523 | switch (c) 524 | --- 370,376 ---- 525 | we'll actually use backup_suffix_string. */ 526 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 527 | 528 | ! while ((c = getopt_long (argc, argv, "bfint:uvgS:T", long_options, NULL)) 529 | != -1) 530 | { 531 | switch (c) 532 | *************** 533 | *** 414,419 **** 534 | --- 416,424 ---- 535 | case 'v': 536 | x.verbose = true; 537 | break; 538 | + case 'g': 539 | + progress = true; 540 | + break; 541 | case 'S': 542 | make_backups = true; 543 | backup_suffix_string = optarg; 544 | *************** 545 | *** 476,481 **** 546 | --- 481,537 ---- 547 | : no_backups); 548 | 549 | hash_init (); 550 | + 551 | + /* BEGIN progress mod */ 552 | + struct timeval start_time; 553 | + 554 | + if(progress) { 555 | + g_iTotalSize = 0; 556 | + g_iFilesCopied = 0; 557 | + g_iTotalWritten = 0; 558 | + 559 | + gettimeofday (& start_time, NULL); 560 | + g_oStartTime = start_time; 561 | + 562 | + printf ("Calculating total size... \r"); 563 | + fflush (stdout); 564 | + long iTotalSize = 0; 565 | + int iFiles = n_files; 566 | + if ( !target_directory ) 567 | + iFiles = 1; 568 | + int j; 569 | + for (j = 0; j < iFiles; j++) 570 | + { 571 | + /* call du -s for each file */ 572 | + /* create command */ 573 | + char command[1024]; 574 | + sprintf ( command, "du -s \"%s\"", file[j] ); 575 | + /* TODO: replace all quote signs in file[i] */ 576 | + 577 | + FILE *fp; 578 | + char output[1024]; 579 | + 580 | + /* run command */ 581 | + fp = popen(command, "r"); 582 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 583 | + printf("failed to run du.\n" ); 584 | + } 585 | + else 586 | + { 587 | + /* isolate size */ 588 | + strchr ( output, '\t' )[0] = '\0'; 589 | + iTotalSize += atol ( output ); 590 | + 591 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 592 | + fflush ( stdout ); 593 | + } 594 | + 595 | + /* close */ 596 | + pclose(fp); 597 | + } 598 | + g_iTotalSize = iTotalSize; 599 | + } 600 | + /* END progress mod */ 601 | 602 | if (target_directory) 603 | { 604 | *************** 605 | *** 493,498 **** 606 | --- 549,594 ---- 607 | } 608 | else 609 | ok = movefile (file[0], file[1], false, &x); 610 | + 611 | + /* BEGIN progress mod */ 612 | + if (progress) { 613 | + /* remove everything */ 614 | + int i; 615 | + if ( g_iTotalSize ) 616 | + { 617 | + for ( i = 0; i < 6; i++ ) 618 | + printf ( "\033[K\n" ); 619 | + printf ( "\r\033[6A" ); 620 | + } 621 | + else 622 | + { 623 | + for ( i = 0; i < 3; i++ ) 624 | + printf ( "\033[K\n" ); 625 | + printf ( "\r\033[3A" ); 626 | + } 627 | + 628 | + /* save time */ 629 | + struct timeval end_time; 630 | + gettimeofday ( & end_time, NULL ); 631 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 632 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 633 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 634 | + 635 | + /* get total size */ 636 | + char sTotalWritten[20]; 637 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 638 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 639 | + 640 | + /* calculate speed */ 641 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 642 | + char s_copy_speed[20]; 643 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 644 | + 645 | + /* good-bye message */ 646 | + printf ( "%d files (%s) moved in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 647 | + sec_elapsed, s_copy_speed ); 648 | + } 649 | + /* END progress mod */ 650 | 651 | exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); 652 | } 653 | -------------------------------------------------------------------------------- /advcpmv-0.8-8.30.patch: -------------------------------------------------------------------------------- 1 | diff -u coreutils-8.30/src/copy.c coreutils-8.30-patched/src/copy.c 2 | --- coreutils-8.30/src/copy.c 2018-07-01 04:32:02.000000000 +0200 3 | +++ coreutils-8.30-patched/src/copy.c 2018-09-20 10:32:15.473398296 +0200 4 | @@ -129,6 +129,72 @@ 5 | dev_t dev; 6 | }; 7 | 8 | +struct progress_status { 9 | + int iCountDown; 10 | + char ** cProgressField; 11 | + struct timeval last_time; 12 | + int last_size, iBarLength; 13 | + struct stat src_open_sb; 14 | +}; 15 | + 16 | +/* Begin progress Mod*/ 17 | +static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal ) 18 | +{ 19 | + double dPercent = (double) _lProgress / (double) _lTotal * 100.f; 20 | + sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent ); 21 | + _cDest[_iBarLength - 2] = ' '; 22 | + 23 | + int i; 24 | + for ( i=1; i<=_iBarLength - 9; i++) 25 | + { 26 | + if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f ) 27 | + { 28 | + _cDest[i] = '='; 29 | + } 30 | + else 31 | + { 32 | + _cDest[i] = ' '; 33 | + } 34 | + } 35 | + for ( i=1; i<_iBarLength - 9; i++) 36 | + { 37 | + if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) ) 38 | + _cDest[i] = '>' ; 39 | + } 40 | +} 41 | + 42 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ) 43 | +{ 44 | + int iCounter = _iCounter; 45 | + double dSize = ( double ) _lSize; 46 | + while ( dSize >= 1000. ) 47 | + { 48 | + dSize /= 1024.; 49 | + iCounter++; 50 | + } 51 | + 52 | + /* get unit */ 53 | + char * sUnit; 54 | + if ( iCounter == 0 ) 55 | + sUnit = "B"; 56 | + else if ( iCounter == 1 ) 57 | + sUnit = "KiB"; 58 | + else if ( iCounter == 2 ) 59 | + sUnit = "MiB"; 60 | + else if ( iCounter == 3 ) 61 | + sUnit = "GiB"; 62 | + else if ( iCounter == 4 ) 63 | + sUnit = "TiB"; 64 | + else 65 | + sUnit = "N/A"; 66 | + 67 | + /* write number */ 68 | + return sprintf ( _cDst, "%5.1f %s", dSize, sUnit ); 69 | +} 70 | +/* END progress mod */ 71 | + 72 | + 73 | + 74 | /* Initial size of the cp.dest_info hash table. */ 75 | #define DEST_INFO_INITIAL_CAPACITY 61 76 | 77 | @@ -225,7 +291,8 @@ 78 | size_t hole_size, bool punch_holes, 79 | char const *src_name, char const *dst_name, 80 | uintmax_t max_n_read, off_t *total_n_read, 81 | - bool *last_write_made_hole) 82 | + bool *last_write_made_hole, 83 | + struct progress_status *s_progress) 84 | { 85 | *last_write_made_hole = false; 86 | *total_n_read = 0; 87 | @@ -234,6 +301,83 @@ 88 | 89 | while (max_n_read) 90 | { 91 | + 92 | + if (progress) { 93 | + /* BEGIN progress mod */ 94 | + /* update countdown */ 95 | + s_progress->iCountDown--; 96 | + char * sProgressBar = s_progress->cProgressField[5]; 97 | + if ( s_progress->iCountDown < 0 ) 98 | + s_progress->iCountDown = 100; 99 | + 100 | + /* just print one line with the percentage, but not always */ 101 | + if ( s_progress->iCountDown == 0 ) 102 | + { 103 | + /* calculate current speed */ 104 | + struct timeval cur_time; 105 | + gettimeofday ( & cur_time, NULL ); 106 | + int cur_size = g_iTotalWritten + *total_n_read / 1024; 107 | + int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec; 108 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 109 | + sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec ); 110 | + int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size ) 111 | + / sec_elapsed ); 112 | + char s_copy_speed[20]; 113 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 114 | + /* update vars */ 115 | + s_progress->last_time = cur_time; 116 | + s_progress->last_size = cur_size; 117 | + 118 | + /* how many time has passed since the start? */ 119 | + int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec; 120 | + int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size 121 | + * g_iTotalSize ) - isec_elapsed; 122 | + int min_remaining = sec_remaining / 60; 123 | + sec_remaining -= min_remaining * 60; 124 | + int hours_remaining = min_remaining / 60; 125 | + min_remaining -= hours_remaining * 60; 126 | + /* print out */ 127 | + sprintf ( s_progress->cProgressField[3], 128 | + "Copying at %s/s (about %uh %um %us remaining)", s_copy_speed, 129 | + hours_remaining, min_remaining, sec_remaining ); 130 | + 131 | + int fs_len; 132 | + if ( g_iTotalFiles > 1 ) 133 | + { 134 | + /* global progress bar */ 135 | + file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength, 136 | + g_iTotalWritten + *total_n_read / 1024, g_iTotalSize ); 137 | + 138 | + /* print the global status */ 139 | + fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21, 140 | + g_iTotalWritten + *total_n_read / 1024, 1 ); 141 | + s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' '; 142 | + } 143 | + 144 | + /* current progress bar */ 145 | + file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size ); 146 | + 147 | + /* print the status */ 148 | + fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 ); 149 | + s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' '; 150 | + 151 | + /* print the field */ 152 | + int it; 153 | + for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ ) 154 | + { 155 | + printf ( "\033[K%s\n", s_progress->cProgressField[it] ); 156 | + if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength ) 157 | + printf ( "" ); 158 | + } 159 | + if ( g_iTotalFiles > 1 ) 160 | + printf ( "\r\033[6A" ); 161 | + else 162 | + printf ( "\r\033[3A" ); 163 | + fflush ( stdout ); 164 | + } 165 | + /* END progress mod */ 166 | + } 167 | + 168 | ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size)); 169 | if (n_read < 0) 170 | { 171 | @@ -320,6 +464,14 @@ 172 | certain files in /proc or /sys with linux kernels. */ 173 | } 174 | 175 | + /* BEGIN progress mod */ 176 | + if (progress) { 177 | + /* update total size */ 178 | + g_iTotalWritten += *total_n_read / 1024; 179 | + g_iFilesCopied++; 180 | + } 181 | + /* END progress mod */ 182 | + 183 | /* Ensure a trailing hole is created, so that subsequent 184 | calls of sparse_copy() start at the correct offset. */ 185 | if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize)) 186 | @@ -388,7 +540,9 @@ 187 | size_t hole_size, off_t src_total_size, 188 | enum Sparse_type sparse_mode, 189 | char const *src_name, char const *dst_name, 190 | - bool *require_normal_copy) 191 | + bool *require_normal_copy, 192 | + int iCountDown, char ** cProgressField, struct timeval last_time, 193 | + int last_size, int iBarLength, struct stat src_open_sb) 194 | { 195 | struct extent_scan scan; 196 | off_t last_ext_start = 0; 197 | @@ -519,10 +673,16 @@ 198 | last_ext_len = ext_len; 199 | bool read_hole; 200 | 201 | + struct timeval a; 202 | + struct stat b; 203 | + 204 | + struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 205 | + 206 | + 207 | if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, 208 | sparse_mode == SPARSE_ALWAYS ? hole_size: 0, 209 | true, src_name, dst_name, ext_len, &n_read, 210 | - &read_hole)) 211 | + &read_hole,&s_progress)) 212 | goto fail; 213 | 214 | dest_pos = ext_start + n_read; 215 | @@ -1270,6 +1430,70 @@ 216 | 217 | buf_alloc = xmalloc (buf_size + buf_alignment); 218 | buf = ptr_align (buf_alloc, buf_alignment); 219 | + 220 | + /* BEGIN progress mod */ 221 | + /* create a field of 6 lines */ 222 | + char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) ); 223 | + /* get console width */ 224 | + int iBarLength = 80; 225 | + struct winsize win; 226 | + if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 ) 227 | + iBarLength = win.ws_col; 228 | + /* create rows */ 229 | + int it; 230 | + for ( it = 0; it < 6; it++ ) 231 | + { 232 | + cProgressField[it] = ( char * ) malloc ( iBarLength + 1 ); 233 | + /* init with spaces */ 234 | + int j; 235 | + for ( j = 0; j < iBarLength; j++ ) 236 | + cProgressField[it][j] = ' '; 237 | + cProgressField[it][iBarLength] = '\0'; 238 | + } 239 | + 240 | + /* global progress bar? */ 241 | + if ( g_iTotalFiles > 1 ) 242 | + { 243 | + /* init global progress bar */ 244 | + cProgressField[2][0] = '['; 245 | + cProgressField[2][iBarLength - 8] = ']'; 246 | + cProgressField[2][iBarLength - 7] = ' '; 247 | + cProgressField[2][iBarLength - 1] = '%'; 248 | + 249 | + /* total size */ 250 | + cProgressField[1][iBarLength - 11] = '/'; 251 | + file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 ); 252 | + 253 | + /* show how many files were written */ 254 | + int sum_length = sprintf ( cProgressField[1], "%d files copied so far...", g_iFilesCopied ); 255 | + cProgressField[1][sum_length] = ' '; 256 | + } 257 | + 258 | + /* truncate filename? */ 259 | + int fn_length; 260 | + if ( strlen ( src_name ) > iBarLength - 22 ) 261 | + fn_length = 262 | + sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) ); 263 | + else 264 | + fn_length = sprintf ( cProgressField[4], "%s", src_name ); 265 | + cProgressField[4][fn_length] = ' '; 266 | + 267 | + /* filesize */ 268 | + cProgressField[4][iBarLength - 11] = '/'; 269 | + file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 ); 270 | + 271 | + int iCountDown = 1; 272 | + char * sProgressBar = cProgressField[5]; 273 | + sProgressBar[0] = '['; 274 | + sProgressBar[iBarLength - 8] = ']'; 275 | + sProgressBar[iBarLength - 7] = ' '; 276 | + sProgressBar[iBarLength - 1] = '%'; 277 | + 278 | + /* this will always save the time in between */ 279 | + struct timeval last_time; 280 | + gettimeofday ( & last_time, NULL ); 281 | + int last_size = g_iTotalWritten; 282 | + /* END progress mod */ 283 | 284 | if (sparse_src) 285 | { 286 | @@ -1282,7 +1506,9 @@ 287 | if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, 288 | src_open_sb.st_size, 289 | make_holes ? x->sparse_mode : SPARSE_NEVER, 290 | - src_name, dst_name, &normal_copy_required)) 291 | + src_name, dst_name, &normal_copy_required, 292 | + iCountDown, cProgressField, last_time, last_size, 293 | + iBarLength, src_open_sb)) 294 | goto preserve_metadata; 295 | 296 | if (! normal_copy_required) 297 | @@ -1294,11 +1520,12 @@ 298 | 299 | off_t n_read; 300 | bool wrote_hole_at_eof; 301 | + struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 302 | if (! sparse_copy (source_desc, dest_desc, buf, buf_size, 303 | make_holes ? hole_size : 0, 304 | x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name, 305 | UINTMAX_MAX, &n_read, 306 | - &wrote_hole_at_eof)) 307 | + &wrote_hole_at_eof, &s_progress)) 308 | { 309 | return_val = false; 310 | goto close_src_and_dst_desc; 311 | @@ -1309,6 +1536,14 @@ 312 | return_val = false; 313 | goto close_src_and_dst_desc; 314 | } 315 | + /* BEGIN progress mod */ 316 | + if (progress) { 317 | + int i; 318 | + for ( i = 0; i < 6; i++ ) 319 | + free ( cProgressField[i] ); 320 | + free ( cProgressField ); 321 | + } 322 | + /* END progress mod */ 323 | } 324 | 325 | preserve_metadata: 326 | diff -u coreutils-8.30/src/copy.h coreutils-8.30-patched/src/copy.h 327 | --- coreutils-8.30/src/copy.h 2018-05-14 06:20:24.000000000 +0200 328 | +++ coreutils-8.30-patched/src/copy.h 2018-09-19 19:23:13.265541538 +0200 329 | @@ -234,6 +234,9 @@ 330 | Create destination directories as usual. */ 331 | bool symbolic_link; 332 | 333 | + /* If true, draw a nice progress bar on screen */ 334 | + bool progress_bar; 335 | + 336 | /* If true, do not copy a nondirectory that has an existing destination 337 | with the same or newer modification time. */ 338 | bool update; 339 | @@ -309,4 +312,15 @@ 340 | bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE; 341 | mode_t cached_umask (void); 342 | 343 | +/* BEGIN OF PROGRESS MOD */ 344 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ); 345 | + 346 | +long g_iTotalSize; 347 | +long g_iTotalWritten; 348 | +int g_iFilesCopied; 349 | +struct timeval g_oStartTime; 350 | +int g_iTotalFiles; 351 | +bool progress; 352 | +/* END OF PROGRESS MOD */ 353 | + 354 | #endif 355 | diff -u coreutils-8.30/src/cp.c coreutils-8.30-patched/src/cp.c 356 | --- coreutils-8.30/src/cp.c 2018-06-24 04:12:51.000000000 +0200 357 | +++ coreutils-8.30-patched/src/cp.c 2018-09-19 20:42:07.159168551 +0200 358 | @@ -131,6 +131,7 @@ 359 | {"symbolic-link", no_argument, NULL, 's'}, 360 | {"target-directory", required_argument, NULL, 't'}, 361 | {"update", no_argument, NULL, 'u'}, 362 | + {"progress-bar", no_argument, NULL, 'g'}, 363 | {"verbose", no_argument, NULL, 'v'}, 364 | {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, 365 | {GETOPT_HELP_OPTION_DECL}, 366 | @@ -170,6 +171,7 @@ 367 | -f, --force if an existing destination file cannot be\n\ 368 | opened, remove it and try again (this option\n\ 369 | is ignored when the -n option is also used)\n\ 370 | + -g, --progress-bar add a progress bar\n\ 371 | -i, --interactive prompt before overwrite (overrides a previous -n\ 372 | \n\ 373 | option)\n\ 374 | @@ -635,6 +637,70 @@ 375 | die (EXIT_FAILURE, 0, _("target %s is not a directory"), 376 | quoteaf (file[n_files - 1])); 377 | } 378 | + struct timeval start_time; 379 | + if (progress) { 380 | + /* BEGIN progress mod */ 381 | + g_iTotalSize = 0; 382 | + g_iTotalFiles = 0; 383 | + g_iFilesCopied = 0; 384 | + g_iTotalWritten = 0; 385 | + 386 | + /* save time */ 387 | + gettimeofday ( & start_time, NULL ); 388 | + g_oStartTime = start_time; 389 | + 390 | + printf ( "Calculating total size... \r" ); 391 | + fflush ( stdout ); 392 | + long iTotalSize = 0; 393 | + int iFiles = n_files; 394 | + if ( ! target_directory ) 395 | + iFiles = n_files - 1; 396 | + int j; 397 | + 398 | + /* how many files are we copying */ 399 | + char command[1024]; 400 | + sprintf( command, "find \"%s\" -type f | wc -l", file[0]); 401 | + FILE *fp ; 402 | + char output[1024]; 403 | + fp = popen(command,"r"); 404 | + if ( fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) 405 | + printf("failed to run find.\n"); 406 | + else 407 | + g_iTotalFiles = atoi( output ) ; 408 | + 409 | + for (j = 0; j < iFiles; j++) 410 | + { 411 | + /* call du -s for each file */ 412 | + /* create command */ 413 | + char command[1024]; 414 | + sprintf ( command, "du -s \"%s\"", file[j] ); 415 | + /* TODO: replace all quote signs in file[i] */ 416 | + 417 | + FILE *fp; 418 | + char output[1024]; 419 | + 420 | + /* run command */ 421 | + fp = popen(command, "r"); 422 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 423 | + printf("failed to run du.\n" ); 424 | + } 425 | + else 426 | + { 427 | + /* isolate size */ 428 | + strchr ( output, '\t' )[0] = '\0'; 429 | + iTotalSize += atol ( output ); 430 | + 431 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 432 | + fflush ( stdout ); 433 | + } 434 | + 435 | + /* close */ 436 | + pclose(fp); 437 | + } 438 | + g_iTotalSize = iTotalSize; 439 | + /* END progress mod */ 440 | + } 441 | + 442 | 443 | if (target_directory) 444 | { 445 | @@ -777,6 +843,46 @@ 446 | ok = copy (source, new_dest, 0, x, &unused, NULL); 447 | } 448 | 449 | + if (progress) { 450 | + /* BEGIN progress mod */ 451 | + /* remove everything */ 452 | + int i; 453 | + if ( g_iTotalFiles > 1 ) 454 | + { 455 | + for ( i = 0; i < 6; i++ ) 456 | + printf ( "\033[K\n" ); 457 | + printf ( "\r\033[6A" ); 458 | + } 459 | + else 460 | + { 461 | + for ( i = 0; i < 3; i++ ) 462 | + printf ( "\033[K\n" ); 463 | + printf ( "\r\033[3A" ); 464 | + } 465 | + 466 | + /* save time */ 467 | + struct timeval end_time; 468 | + gettimeofday ( & end_time, NULL ); 469 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 470 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 471 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 472 | + 473 | + /* get total size */ 474 | + char sTotalWritten[20]; 475 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 476 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 477 | + 478 | + /* calculate speed */ 479 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 480 | + char s_copy_speed[20]; 481 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 482 | + 483 | + /* good-bye message */ 484 | + printf ( "%d files (%s) copied in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 485 | + sec_elapsed, s_copy_speed ); 486 | + /* END progress mod */ 487 | + } 488 | + 489 | return ok; 490 | } 491 | 492 | @@ -812,6 +918,7 @@ 493 | x->recursive = false; 494 | x->sparse_mode = SPARSE_AUTO; 495 | x->symbolic_link = false; 496 | + x->progress_bar = false; 497 | x->set_mode = false; 498 | x->mode = 0; 499 | 500 | @@ -950,7 +1057,7 @@ 501 | selinux_enabled = (0 < is_selinux_enabled ()); 502 | cp_option_init (&x); 503 | 504 | - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", 505 | + while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ", 506 | long_opts, NULL)) 507 | != -1) 508 | { 509 | @@ -1007,6 +1114,10 @@ 510 | x.unlink_dest_after_failed_open = true; 511 | break; 512 | 513 | + case 'g': 514 | + progress = true; 515 | + break; 516 | + 517 | case 'H': 518 | x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; 519 | break; 520 | diff -u coreutils-8.30/src/mv.c coreutils-8.30-patched/src/mv.c 521 | --- coreutils-8.30/src/mv.c 2018-06-24 04:12:51.000000000 +0200 522 | +++ coreutils-8.30-patched/src/mv.c 2018-09-20 10:36:33.316124125 +0200 523 | @@ -66,6 +66,7 @@ 524 | {"target-directory", required_argument, NULL, 't'}, 525 | {"update", no_argument, NULL, 'u'}, 526 | {"verbose", no_argument, NULL, 'v'}, 527 | + {"progress-ar", no_argument, NULL, 'g'}, 528 | {GETOPT_HELP_OPTION_DECL}, 529 | {GETOPT_VERSION_OPTION_DECL}, 530 | {NULL, 0, NULL, 0} 531 | @@ -168,10 +169,86 @@ 532 | static bool 533 | do_move (const char *source, const char *dest, const struct cp_options *x) 534 | { 535 | + struct timeval start_time; 536 | + 537 | bool copy_into_self; 538 | bool rename_succeeded; 539 | + if(progress && x->rename_errno != 0) { 540 | + /* BEGIN progress mod */ 541 | + g_iTotalSize = 0; 542 | + g_iFilesCopied = 0; 543 | + g_iTotalWritten = 0; 544 | + 545 | + gettimeofday (& start_time, NULL); 546 | + g_oStartTime = start_time; 547 | + 548 | + printf ("Calculating total size... \r"); 549 | + fflush (stdout); 550 | + long iTotalSize = 0; 551 | + /* call du -s for each file */ 552 | + /* create command */ 553 | + char command[1024]; 554 | + sprintf ( command, "du -s '%s'", source ); 555 | + /* TODO: replace all quote signs in file[i] */ 556 | + 557 | + FILE *fp; 558 | + char output[1024]; 559 | + 560 | + /* run command */ 561 | + fp = popen(command, "r"); 562 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 563 | + //printf("failed to run du.\n" ); 564 | + } 565 | + else 566 | + { 567 | + /* isolate size */ 568 | + strchr ( output, '\t' )[0] = '\0'; 569 | + iTotalSize += atol ( output ); 570 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 571 | + fflush ( stdout ); 572 | + } 573 | + 574 | + /* close */ 575 | + pclose(fp); 576 | + g_iTotalSize = iTotalSize; 577 | + /* END progress mod */ 578 | + 579 | + } 580 | + 581 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 582 | 583 | + if (progress && (x->rename_errno != 0 && ok)) { 584 | + /* BEGIN progress mod */ 585 | + /* remove everything */ 586 | + int i; 587 | + int limit = (g_iTotalFiles > 1 ? 6 : 3); 588 | + for ( i = 0; i < limit; i++ ) 589 | + printf ( "\033[K\n" ); 590 | + printf ( "\r\033[3A" ); 591 | + 592 | + /* save time */ 593 | + struct timeval end_time; 594 | + gettimeofday ( & end_time, NULL ); 595 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 596 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 597 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 598 | + 599 | + /* get total size */ 600 | + char sTotalWritten[20]; 601 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 602 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 603 | + 604 | + /* calculate speed */ 605 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 606 | + char s_copy_speed[20]; 607 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 608 | + 609 | + /* good-bye message */ 610 | + printf ( "%d files (%s) moved in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 611 | + sec_elapsed, s_copy_speed ); 612 | + /* END progress mod */ 613 | + } 614 | + 615 | if (ok) 616 | { 617 | char const *dir_to_remove; 618 | @@ -306,6 +383,7 @@ 619 | \n\ 620 | -b like --backup but does not accept an argument\n\ 621 | -f, --force do not prompt before overwriting\n\ 622 | + -g, --progress-bar add progress-bar\n\ 623 | -i, --interactive prompt before overwrite\n\ 624 | -n, --no-clobber do not overwrite an existing file\n\ 625 | If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ 626 | @@ -361,7 +439,7 @@ 627 | /* Try to disable the ability to unlink a directory. */ 628 | priv_set_remove_linkdir (); 629 | 630 | - while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL)) 631 | + while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL)) 632 | != -1) 633 | { 634 | switch (c) 635 | @@ -407,6 +485,11 @@ 636 | case 'v': 637 | x.verbose = true; 638 | break; 639 | + 640 | + case 'g': 641 | + progress = true; 642 | + break; 643 | + 644 | case 'S': 645 | make_backups = true; 646 | backup_suffix = optarg; 647 | -------------------------------------------------------------------------------- /advcpmv-0.8-8.31.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/copy.c b/src/copy.c 2 | index dc1f6d0fa..53ff5ba87 100644 3 | --- a/src/copy.c 4 | +++ b/src/copy.c 5 | @@ -129,6 +129,72 @@ struct dir_list 6 | dev_t dev; 7 | }; 8 | 9 | +struct progress_status { 10 | + int iCountDown; 11 | + char ** cProgressField; 12 | + struct timeval last_time; 13 | + int last_size, iBarLength; 14 | + struct stat src_open_sb; 15 | +}; 16 | + 17 | +/* Begin progress Mod*/ 18 | +static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal ) 19 | +{ 20 | + double dPercent = (double) _lProgress / (double) _lTotal * 100.f; 21 | + sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent ); 22 | + _cDest[_iBarLength - 2] = ' '; 23 | + 24 | + int i; 25 | + for ( i=1; i<=_iBarLength - 9; i++) 26 | + { 27 | + if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f ) 28 | + { 29 | + _cDest[i] = '='; 30 | + } 31 | + else 32 | + { 33 | + _cDest[i] = ' '; 34 | + } 35 | + } 36 | + for ( i=1; i<_iBarLength - 9; i++) 37 | + { 38 | + if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) ) 39 | + _cDest[i] = '>' ; 40 | + } 41 | +} 42 | + 43 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ) 44 | +{ 45 | + int iCounter = _iCounter; 46 | + double dSize = ( double ) _lSize; 47 | + while ( dSize >= 1000. ) 48 | + { 49 | + dSize /= 1024.; 50 | + iCounter++; 51 | + } 52 | + 53 | + /* get unit */ 54 | + char * sUnit; 55 | + if ( iCounter == 0 ) 56 | + sUnit = "B"; 57 | + else if ( iCounter == 1 ) 58 | + sUnit = "KiB"; 59 | + else if ( iCounter == 2 ) 60 | + sUnit = "MiB"; 61 | + else if ( iCounter == 3 ) 62 | + sUnit = "GiB"; 63 | + else if ( iCounter == 4 ) 64 | + sUnit = "TiB"; 65 | + else 66 | + sUnit = "N/A"; 67 | + 68 | + /* write number */ 69 | + return sprintf ( _cDst, "%5.1f %s", dSize, sUnit ); 70 | +} 71 | +/* END progress mod */ 72 | + 73 | + 74 | + 75 | /* Initial size of the cp.dest_info hash table. */ 76 | #define DEST_INFO_INITIAL_CAPACITY 61 77 | 78 | @@ -225,7 +291,8 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, 79 | size_t hole_size, bool punch_holes, 80 | char const *src_name, char const *dst_name, 81 | uintmax_t max_n_read, off_t *total_n_read, 82 | - bool *last_write_made_hole) 83 | + bool *last_write_made_hole, 84 | + struct progress_status *s_progress) 85 | { 86 | *last_write_made_hole = false; 87 | *total_n_read = 0; 88 | @@ -234,6 +301,83 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, 89 | 90 | while (max_n_read) 91 | { 92 | + 93 | + if (progress) { 94 | + /* BEGIN progress mod */ 95 | + /* update countdown */ 96 | + s_progress->iCountDown--; 97 | + char * sProgressBar = s_progress->cProgressField[5]; 98 | + if ( s_progress->iCountDown < 0 ) 99 | + s_progress->iCountDown = 100; 100 | + 101 | + /* just print one line with the percentage, but not always */ 102 | + if ( s_progress->iCountDown == 0 ) 103 | + { 104 | + /* calculate current speed */ 105 | + struct timeval cur_time; 106 | + gettimeofday ( & cur_time, NULL ); 107 | + int cur_size = g_iTotalWritten + *total_n_read / 1024; 108 | + int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec; 109 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 110 | + sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec ); 111 | + int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size ) 112 | + / sec_elapsed ); 113 | + char s_copy_speed[20]; 114 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 115 | + /* update vars */ 116 | + s_progress->last_time = cur_time; 117 | + s_progress->last_size = cur_size; 118 | + 119 | + /* how many time has passed since the start? */ 120 | + int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec; 121 | + int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size 122 | + * g_iTotalSize ) - isec_elapsed; 123 | + int min_remaining = sec_remaining / 60; 124 | + sec_remaining -= min_remaining * 60; 125 | + int hours_remaining = min_remaining / 60; 126 | + min_remaining -= hours_remaining * 60; 127 | + /* print out */ 128 | + sprintf ( s_progress->cProgressField[3], 129 | + "Copying at %s/s (about %uh %um %us remaining)", s_copy_speed, 130 | + hours_remaining, min_remaining, sec_remaining ); 131 | + 132 | + int fs_len; 133 | + if ( g_iTotalFiles > 1 ) 134 | + { 135 | + /* global progress bar */ 136 | + file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength, 137 | + g_iTotalWritten + *total_n_read / 1024, g_iTotalSize ); 138 | + 139 | + /* print the global status */ 140 | + fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21, 141 | + g_iTotalWritten + *total_n_read / 1024, 1 ); 142 | + s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' '; 143 | + } 144 | + 145 | + /* current progress bar */ 146 | + file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size ); 147 | + 148 | + /* print the status */ 149 | + fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 ); 150 | + s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' '; 151 | + 152 | + /* print the field */ 153 | + int it; 154 | + for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ ) 155 | + { 156 | + printf ( "\033[K%s\n", s_progress->cProgressField[it] ); 157 | + if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength ) 158 | + printf ( "" ); 159 | + } 160 | + if ( g_iTotalFiles > 1 ) 161 | + printf ( "\r\033[6A" ); 162 | + else 163 | + printf ( "\r\033[3A" ); 164 | + fflush ( stdout ); 165 | + } 166 | + /* END progress mod */ 167 | + } 168 | + 169 | ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size)); 170 | if (n_read < 0) 171 | { 172 | @@ -320,6 +464,14 @@ sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, 173 | certain files in /proc or /sys with linux kernels. */ 174 | } 175 | 176 | + /* BEGIN progress mod */ 177 | + if (progress) { 178 | + /* update total size */ 179 | + g_iTotalWritten += *total_n_read / 1024; 180 | + g_iFilesCopied++; 181 | + } 182 | + /* END progress mod */ 183 | + 184 | /* Ensure a trailing hole is created, so that subsequent 185 | calls of sparse_copy() start at the correct offset. */ 186 | if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize)) 187 | @@ -388,7 +540,9 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, 188 | size_t hole_size, off_t src_total_size, 189 | enum Sparse_type sparse_mode, 190 | char const *src_name, char const *dst_name, 191 | - bool *require_normal_copy) 192 | + bool *require_normal_copy, 193 | + int iCountDown, char ** cProgressField, struct timeval last_time, 194 | + int last_size, int iBarLength, struct stat src_open_sb) 195 | { 196 | struct extent_scan scan; 197 | off_t last_ext_start = 0; 198 | @@ -519,10 +673,16 @@ extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, 199 | last_ext_len = ext_len; 200 | bool read_hole; 201 | 202 | + struct timeval a; 203 | + struct stat b; 204 | + 205 | + struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 206 | + 207 | + 208 | if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, 209 | sparse_mode == SPARSE_ALWAYS ? hole_size: 0, 210 | true, src_name, dst_name, ext_len, &n_read, 211 | - &read_hole)) 212 | + &read_hole,&s_progress)) 213 | goto fail; 214 | 215 | dest_pos = ext_start + n_read; 216 | @@ -1271,6 +1431,70 @@ copy_reg (char const *src_name, char const *dst_name, 217 | buf_alloc = xmalloc (buf_size + buf_alignment); 218 | buf = ptr_align (buf_alloc, buf_alignment); 219 | 220 | + /* BEGIN progress mod */ 221 | + /* create a field of 6 lines */ 222 | + char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) ); 223 | + /* get console width */ 224 | + int iBarLength = 80; 225 | + struct winsize win; 226 | + if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 ) 227 | + iBarLength = win.ws_col; 228 | + /* create rows */ 229 | + int it; 230 | + for ( it = 0; it < 6; it++ ) 231 | + { 232 | + cProgressField[it] = ( char * ) malloc ( iBarLength + 1 ); 233 | + /* init with spaces */ 234 | + int j; 235 | + for ( j = 0; j < iBarLength; j++ ) 236 | + cProgressField[it][j] = ' '; 237 | + cProgressField[it][iBarLength] = '\0'; 238 | + } 239 | + 240 | + /* global progress bar? */ 241 | + if ( g_iTotalFiles > 1 ) 242 | + { 243 | + /* init global progress bar */ 244 | + cProgressField[2][0] = '['; 245 | + cProgressField[2][iBarLength - 8] = ']'; 246 | + cProgressField[2][iBarLength - 7] = ' '; 247 | + cProgressField[2][iBarLength - 1] = '%'; 248 | + 249 | + /* total size */ 250 | + cProgressField[1][iBarLength - 11] = '/'; 251 | + file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 ); 252 | + 253 | + /* show how many files were written */ 254 | + int sum_length = sprintf ( cProgressField[1], "%d files copied so far...", g_iFilesCopied ); 255 | + cProgressField[1][sum_length] = ' '; 256 | + } 257 | + 258 | + /* truncate filename? */ 259 | + int fn_length; 260 | + if ( strlen ( src_name ) > iBarLength - 22 ) 261 | + fn_length = 262 | + sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) ); 263 | + else 264 | + fn_length = sprintf ( cProgressField[4], "%s", src_name ); 265 | + cProgressField[4][fn_length] = ' '; 266 | + 267 | + /* filesize */ 268 | + cProgressField[4][iBarLength - 11] = '/'; 269 | + file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 ); 270 | + 271 | + int iCountDown = 1; 272 | + char * sProgressBar = cProgressField[5]; 273 | + sProgressBar[0] = '['; 274 | + sProgressBar[iBarLength - 8] = ']'; 275 | + sProgressBar[iBarLength - 7] = ' '; 276 | + sProgressBar[iBarLength - 1] = '%'; 277 | + 278 | + /* this will always save the time in between */ 279 | + struct timeval last_time; 280 | + gettimeofday ( & last_time, NULL ); 281 | + int last_size = g_iTotalWritten; 282 | + /* END progress mod */ 283 | + 284 | if (sparse_src) 285 | { 286 | bool normal_copy_required; 287 | @@ -1282,7 +1506,9 @@ copy_reg (char const *src_name, char const *dst_name, 288 | if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, 289 | src_open_sb.st_size, 290 | make_holes ? x->sparse_mode : SPARSE_NEVER, 291 | - src_name, dst_name, &normal_copy_required)) 292 | + src_name, dst_name, &normal_copy_required, 293 | + iCountDown, cProgressField, last_time, last_size, 294 | + iBarLength, src_open_sb)) 295 | goto preserve_metadata; 296 | 297 | if (! normal_copy_required) 298 | @@ -1294,11 +1520,12 @@ copy_reg (char const *src_name, char const *dst_name, 299 | 300 | off_t n_read; 301 | bool wrote_hole_at_eof; 302 | + struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 303 | if (! sparse_copy (source_desc, dest_desc, buf, buf_size, 304 | make_holes ? hole_size : 0, 305 | x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name, 306 | UINTMAX_MAX, &n_read, 307 | - &wrote_hole_at_eof)) 308 | + &wrote_hole_at_eof, &s_progress)) 309 | { 310 | return_val = false; 311 | goto close_src_and_dst_desc; 312 | @@ -1309,6 +1536,14 @@ copy_reg (char const *src_name, char const *dst_name, 313 | return_val = false; 314 | goto close_src_and_dst_desc; 315 | } 316 | + /* BEGIN progress mod */ 317 | + if (progress) { 318 | + int i; 319 | + for ( i = 0; i < 6; i++ ) 320 | + free ( cProgressField[i] ); 321 | + free ( cProgressField ); 322 | + } 323 | + /* END progress mod */ 324 | } 325 | 326 | preserve_metadata: 327 | diff --git a/src/copy.h b/src/copy.h 328 | index 7d2303c54..d2bb8b02c 100644 329 | --- a/src/copy.h 330 | +++ b/src/copy.h 331 | @@ -234,6 +234,9 @@ struct cp_options 332 | Create destination directories as usual. */ 333 | bool symbolic_link; 334 | 335 | + /* If true, draw a nice progress bar on screen */ 336 | + bool progress_bar; 337 | + 338 | /* If true, do not copy a nondirectory that has an existing destination 339 | with the same or newer modification time. */ 340 | bool update; 341 | @@ -309,4 +312,15 @@ void cp_options_default (struct cp_options *); 342 | bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE; 343 | mode_t cached_umask (void); 344 | 345 | +/* BEGIN OF PROGRESS MOD */ 346 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ); 347 | + 348 | +__attribute__((__common__)) long g_iTotalSize; 349 | +__attribute__((__common__)) long g_iTotalWritten; 350 | +__attribute__((__common__)) int g_iFilesCopied; 351 | +__attribute__((__common__)) struct timeval g_oStartTime; 352 | +__attribute__((__common__)) int g_iTotalFiles; 353 | +__attribute__((__common__)) bool progress; 354 | +/* END OF PROGRESS MOD */ 355 | + 356 | #endif 357 | diff --git a/src/cp.c b/src/cp.c 358 | index 707f3984a..dd85cf6d4 100644 359 | --- a/src/cp.c 360 | +++ b/src/cp.c 361 | @@ -131,6 +131,7 @@ static struct option const long_opts[] = 362 | {"symbolic-link", no_argument, NULL, 's'}, 363 | {"target-directory", required_argument, NULL, 't'}, 364 | {"update", no_argument, NULL, 'u'}, 365 | + {"progress-bar", no_argument, NULL, 'g'}, 366 | {"verbose", no_argument, NULL, 'v'}, 367 | {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, 368 | {GETOPT_HELP_OPTION_DECL}, 369 | @@ -170,6 +171,7 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\ 370 | -f, --force if an existing destination file cannot be\n\ 371 | opened, remove it and try again (this option\n\ 372 | is ignored when the -n option is also used)\n\ 373 | + -g, --progress-bar add a progress bar\n\ 374 | -i, --interactive prompt before overwrite (overrides a previous -n\ 375 | \n\ 376 | option)\n\ 377 | @@ -635,6 +637,70 @@ do_copy (int n_files, char **file, const char *target_directory, 378 | die (EXIT_FAILURE, 0, _("target %s is not a directory"), 379 | quoteaf (file[n_files - 1])); 380 | } 381 | + struct timeval start_time; 382 | + if (progress) { 383 | + /* BEGIN progress mod */ 384 | + g_iTotalSize = 0; 385 | + g_iTotalFiles = 0; 386 | + g_iFilesCopied = 0; 387 | + g_iTotalWritten = 0; 388 | + 389 | + /* save time */ 390 | + gettimeofday ( & start_time, NULL ); 391 | + g_oStartTime = start_time; 392 | + 393 | + printf ( "Calculating total size... \r" ); 394 | + fflush ( stdout ); 395 | + long iTotalSize = 0; 396 | + int iFiles = n_files; 397 | + if ( ! target_directory ) 398 | + iFiles = n_files - 1; 399 | + int j; 400 | + 401 | + /* how many files are we copying */ 402 | + char command[1024]; 403 | + sprintf( command, "find \"%s\" -type f | wc -l", file[0]); 404 | + FILE *fp ; 405 | + char output[1024]; 406 | + fp = popen(command,"r"); 407 | + if ( fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) 408 | + printf("failed to run find.\n"); 409 | + else 410 | + g_iTotalFiles = atoi( output ) ; 411 | + 412 | + for (j = 0; j < iFiles; j++) 413 | + { 414 | + /* call du -s for each file */ 415 | + /* create command */ 416 | + char command[1024]; 417 | + sprintf ( command, "du -s \"%s\"", file[j] ); 418 | + /* TODO: replace all quote signs in file[i] */ 419 | + 420 | + FILE *fp; 421 | + char output[1024]; 422 | + 423 | + /* run command */ 424 | + fp = popen(command, "r"); 425 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 426 | + printf("failed to run du.\n" ); 427 | + } 428 | + else 429 | + { 430 | + /* isolate size */ 431 | + strchr ( output, '\t' )[0] = '\0'; 432 | + iTotalSize += atol ( output ); 433 | + 434 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 435 | + fflush ( stdout ); 436 | + } 437 | + 438 | + /* close */ 439 | + pclose(fp); 440 | + } 441 | + g_iTotalSize = iTotalSize; 442 | + /* END progress mod */ 443 | + } 444 | + 445 | 446 | if (target_directory) 447 | { 448 | @@ -777,6 +843,46 @@ do_copy (int n_files, char **file, const char *target_directory, 449 | ok = copy (source, new_dest, 0, x, &unused, NULL); 450 | } 451 | 452 | + if (progress) { 453 | + /* BEGIN progress mod */ 454 | + /* remove everything */ 455 | + int i; 456 | + if ( g_iTotalFiles > 1 ) 457 | + { 458 | + for ( i = 0; i < 6; i++ ) 459 | + printf ( "\033[K\n" ); 460 | + printf ( "\r\033[6A" ); 461 | + } 462 | + else 463 | + { 464 | + for ( i = 0; i < 3; i++ ) 465 | + printf ( "\033[K\n" ); 466 | + printf ( "\r\033[3A" ); 467 | + } 468 | + 469 | + /* save time */ 470 | + struct timeval end_time; 471 | + gettimeofday ( & end_time, NULL ); 472 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 473 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 474 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 475 | + 476 | + /* get total size */ 477 | + char sTotalWritten[20]; 478 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 479 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 480 | + 481 | + /* calculate speed */ 482 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 483 | + char s_copy_speed[20]; 484 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 485 | + 486 | + /* good-bye message */ 487 | + printf ( "%d files (%s) copied in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 488 | + sec_elapsed, s_copy_speed ); 489 | + /* END progress mod */ 490 | + } 491 | + 492 | return ok; 493 | } 494 | 495 | @@ -812,6 +918,7 @@ cp_option_init (struct cp_options *x) 496 | x->recursive = false; 497 | x->sparse_mode = SPARSE_AUTO; 498 | x->symbolic_link = false; 499 | + x->progress_bar = false; 500 | x->set_mode = false; 501 | x->mode = 0; 502 | 503 | @@ -950,7 +1057,7 @@ main (int argc, char **argv) 504 | selinux_enabled = (0 < is_selinux_enabled ()); 505 | cp_option_init (&x); 506 | 507 | - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", 508 | + while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ", 509 | long_opts, NULL)) 510 | != -1) 511 | { 512 | @@ -1007,6 +1114,10 @@ main (int argc, char **argv) 513 | x.unlink_dest_after_failed_open = true; 514 | break; 515 | 516 | + case 'g': 517 | + progress = true; 518 | + break; 519 | + 520 | case 'H': 521 | x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; 522 | break; 523 | diff --git a/src/mv.c b/src/mv.c 524 | index d1dd1d224..944505e1e 100644 525 | --- a/src/mv.c 526 | +++ b/src/mv.c 527 | @@ -66,6 +66,7 @@ static struct option const long_options[] = 528 | {"target-directory", required_argument, NULL, 't'}, 529 | {"update", no_argument, NULL, 'u'}, 530 | {"verbose", no_argument, NULL, 'v'}, 531 | + {"progress-ar", no_argument, NULL, 'g'}, 532 | {GETOPT_HELP_OPTION_DECL}, 533 | {GETOPT_VERSION_OPTION_DECL}, 534 | {NULL, 0, NULL, 0} 535 | @@ -168,10 +169,86 @@ target_directory_operand (char const *file) 536 | static bool 537 | do_move (const char *source, const char *dest, const struct cp_options *x) 538 | { 539 | + struct timeval start_time; 540 | + 541 | bool copy_into_self; 542 | bool rename_succeeded; 543 | + if(progress && x->rename_errno != 0) { 544 | + /* BEGIN progress mod */ 545 | + g_iTotalSize = 0; 546 | + g_iFilesCopied = 0; 547 | + g_iTotalWritten = 0; 548 | + 549 | + gettimeofday (& start_time, NULL); 550 | + g_oStartTime = start_time; 551 | + 552 | + printf ("Calculating total size... \r"); 553 | + fflush (stdout); 554 | + long iTotalSize = 0; 555 | + /* call du -s for each file */ 556 | + /* create command */ 557 | + char command[1024]; 558 | + sprintf ( command, "du -s '%s'", source ); 559 | + /* TODO: replace all quote signs in file[i] */ 560 | + 561 | + FILE *fp; 562 | + char output[1024]; 563 | + 564 | + /* run command */ 565 | + fp = popen(command, "r"); 566 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 567 | + //printf("failed to run du.\n" ); 568 | + } 569 | + else 570 | + { 571 | + /* isolate size */ 572 | + strchr ( output, '\t' )[0] = '\0'; 573 | + iTotalSize += atol ( output ); 574 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 575 | + fflush ( stdout ); 576 | + } 577 | + 578 | + /* close */ 579 | + pclose(fp); 580 | + g_iTotalSize = iTotalSize; 581 | + /* END progress mod */ 582 | + 583 | + } 584 | + 585 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 586 | 587 | + if (progress && (x->rename_errno != 0 && ok)) { 588 | + /* BEGIN progress mod */ 589 | + /* remove everything */ 590 | + int i; 591 | + int limit = (g_iTotalFiles > 1 ? 6 : 3); 592 | + for ( i = 0; i < limit; i++ ) 593 | + printf ( "\033[K\n" ); 594 | + printf ( "\r\033[3A" ); 595 | + 596 | + /* save time */ 597 | + struct timeval end_time; 598 | + gettimeofday ( & end_time, NULL ); 599 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 600 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 601 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 602 | + 603 | + /* get total size */ 604 | + char sTotalWritten[20]; 605 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 606 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 607 | + 608 | + /* calculate speed */ 609 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 610 | + char s_copy_speed[20]; 611 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 612 | + 613 | + /* good-bye message */ 614 | + printf ( "%d files (%s) moved in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 615 | + sec_elapsed, s_copy_speed ); 616 | + /* END progress mod */ 617 | + } 618 | + 619 | if (ok) 620 | { 621 | char const *dir_to_remove; 622 | @@ -306,6 +383,7 @@ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\ 623 | \n\ 624 | -b like --backup but does not accept an argument\n\ 625 | -f, --force do not prompt before overwriting\n\ 626 | + -g, --progress-bar add progress-bar\n\ 627 | -i, --interactive prompt before overwrite\n\ 628 | -n, --no-clobber do not overwrite an existing file\n\ 629 | If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ 630 | @@ -361,7 +439,7 @@ main (int argc, char **argv) 631 | /* Try to disable the ability to unlink a directory. */ 632 | priv_set_remove_linkdir (); 633 | 634 | - while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL)) 635 | + while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL)) 636 | != -1) 637 | { 638 | switch (c) 639 | @@ -407,6 +485,11 @@ main (int argc, char **argv) 640 | case 'v': 641 | x.verbose = true; 642 | break; 643 | + 644 | + case 'g': 645 | + progress = true; 646 | + break; 647 | + 648 | case 'S': 649 | make_backups = true; 650 | backup_suffix = optarg; 651 | -------------------------------------------------------------------------------- /advcpmv-0.7-8.25.patch: -------------------------------------------------------------------------------- 1 | diff -rup coreutils-8.25/src/copy.c coreutils-8.25-patched/src/copy.c 2 | --- coreutils-8.25/src/copy.c 2016-01-16 20:09:33.000000000 +0100 3 | +++ coreutils-8.25-patched/src/copy.c 2016-08-26 00:56:59.728580294 +0200 4 | @@ -117,6 +117,71 @@ struct dir_list 5 | dev_t dev; 6 | }; 7 | 8 | +struct progress_status { 9 | + int iCountDown; 10 | + char ** cProgressField; 11 | + struct timeval last_time; 12 | + int last_size, iBarLength; 13 | + struct stat src_open_sb; 14 | +}; 15 | + 16 | +/* Begin progress Mod*/ 17 | +static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal ) 18 | +{ 19 | + double dPercent = (double) _lProgress / (double) _lTotal * 100.f; 20 | + sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent ); 21 | + _cDest[_iBarLength - 2] = ' '; 22 | + 23 | + int i; 24 | + for ( i=1; i<=_iBarLength - 9; i++) 25 | + { 26 | + if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f ) 27 | + { 28 | + _cDest[i] = '='; 29 | + } 30 | + else 31 | + { 32 | + _cDest[i] = ' '; 33 | + } 34 | + } 35 | + for ( i=1; i<_iBarLength - 9; i++) 36 | + { 37 | + if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) ) 38 | + _cDest[i] = '>' ; 39 | + } 40 | +} 41 | + 42 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ) 43 | +{ 44 | + int iCounter = _iCounter; 45 | + double dSize = ( double ) _lSize; 46 | + while ( dSize >= 1000. ) 47 | + { 48 | + dSize /= 1024.; 49 | + iCounter++; 50 | + } 51 | + 52 | + /* get unit */ 53 | + char * sUnit; 54 | + if ( iCounter == 0 ) 55 | + sUnit = "B"; 56 | + else if ( iCounter == 1 ) 57 | + sUnit = "KiB"; 58 | + else if ( iCounter == 2 ) 59 | + sUnit = "MiB"; 60 | + else if ( iCounter == 3 ) 61 | + sUnit = "GiB"; 62 | + else if ( iCounter == 4 ) 63 | + sUnit = "TiB"; 64 | + else 65 | + sUnit = "N/A"; 66 | + 67 | + /* write number */ 68 | + return sprintf ( _cDst, "%5.1f %s", dSize, sUnit ); 69 | +} 70 | +/* END progress mod */ 71 | + 72 | + 73 | /* Initial size of the cp.dest_info hash table. */ 74 | #define DEST_INFO_INITIAL_CAPACITY 61 75 | 76 | @@ -212,7 +277,8 @@ sparse_copy (int src_fd, int dest_fd, ch 77 | size_t hole_size, bool punch_holes, 78 | char const *src_name, char const *dst_name, 79 | uintmax_t max_n_read, off_t *total_n_read, 80 | - bool *last_write_made_hole) 81 | + bool *last_write_made_hole, 82 | + struct progress_status *s_progress) 83 | { 84 | *last_write_made_hole = false; 85 | *total_n_read = 0; 86 | @@ -221,6 +287,83 @@ sparse_copy (int src_fd, int dest_fd, ch 87 | 88 | while (max_n_read) 89 | { 90 | + 91 | + if (progress) { 92 | + /* BEGIN progress mod */ 93 | + /* update countdown */ 94 | + s_progress->iCountDown--; 95 | + char * sProgressBar = s_progress->cProgressField[5]; 96 | + if ( s_progress->iCountDown < 0 ) 97 | + s_progress->iCountDown = 100; 98 | + 99 | + /* just print one line with the percentage, but not always */ 100 | + if ( s_progress->iCountDown == 0 ) 101 | + { 102 | + /* calculate current speed */ 103 | + struct timeval cur_time; 104 | + gettimeofday ( & cur_time, NULL ); 105 | + int cur_size = g_iTotalWritten + *total_n_read / 1024; 106 | + int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec; 107 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 108 | + sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec ); 109 | + int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size ) 110 | + / sec_elapsed ); 111 | + char s_copy_speed[20]; 112 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 113 | + /* update vars */ 114 | + s_progress->last_time = cur_time; 115 | + s_progress->last_size = cur_size; 116 | + 117 | + /* how many time has passed since the start? */ 118 | + int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec; 119 | + int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size 120 | + * g_iTotalSize ) - isec_elapsed; 121 | + int min_remaining = sec_remaining / 60; 122 | + sec_remaining -= min_remaining * 60; 123 | + int hours_remaining = min_remaining / 60; 124 | + min_remaining -= hours_remaining * 60; 125 | + /* print out */ 126 | + sprintf ( s_progress->cProgressField[3], 127 | + "Copying at %s/s (about %uh %um %us remaining)", s_copy_speed, 128 | + hours_remaining, min_remaining, sec_remaining ); 129 | + 130 | + int fs_len; 131 | + if ( g_iTotalFiles > 1 ) 132 | + { 133 | + /* global progress bar */ 134 | + file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength, 135 | + g_iTotalWritten + *total_n_read / 1024, g_iTotalSize ); 136 | + 137 | + /* print the global status */ 138 | + fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21, 139 | + g_iTotalWritten + *total_n_read / 1024, 1 ); 140 | + s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' '; 141 | + } 142 | + 143 | + /* current progress bar */ 144 | + file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size ); 145 | + 146 | + /* print the status */ 147 | + fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 ); 148 | + s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' '; 149 | + 150 | + /* print the field */ 151 | + int it; 152 | + for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ ) 153 | + { 154 | + printf ( "\033[K%s\n", s_progress->cProgressField[it] ); 155 | + if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength ) 156 | + printf ( "" ); 157 | + } 158 | + if ( g_iTotalFiles > 1 ) 159 | + printf ( "\r\033[6A" ); 160 | + else 161 | + printf ( "\r\033[3A" ); 162 | + fflush ( stdout ); 163 | + } 164 | + /* END progress mod */ 165 | + } 166 | + 167 | ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size)); 168 | if (n_read < 0) 169 | { 170 | @@ -307,6 +450,14 @@ sparse_copy (int src_fd, int dest_fd, ch 171 | certain files in /proc or /sys with linux kernels. */ 172 | } 173 | 174 | + /* BEGIN progress mod */ 175 | + if (progress) { 176 | + /* update total size */ 177 | + g_iTotalWritten += *total_n_read / 1024; 178 | + g_iFilesCopied++; 179 | + } 180 | + /* END progress mod */ 181 | + 182 | /* Ensure a trailing hole is created, so that subsequent 183 | calls of sparse_copy() start at the correct offset. */ 184 | if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize)) 185 | @@ -379,7 +530,9 @@ extent_copy (int src_fd, int dest_fd, ch 186 | size_t hole_size, off_t src_total_size, 187 | enum Sparse_type sparse_mode, 188 | char const *src_name, char const *dst_name, 189 | - bool *require_normal_copy) 190 | + bool *require_normal_copy, 191 | + int iCountDown, char ** cProgressField, struct timeval last_time, 192 | + int last_size, int iBarLength, struct stat src_open_sb) 193 | { 194 | struct extent_scan scan; 195 | off_t last_ext_start = 0; 196 | @@ -511,10 +664,16 @@ extent_copy (int src_fd, int dest_fd, ch 197 | last_ext_len = ext_len; 198 | bool read_hole; 199 | 200 | + struct timeval a; 201 | + struct stat b; 202 | + 203 | + struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 204 | + 205 | + 206 | if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, 207 | sparse_mode == SPARSE_ALWAYS ? hole_size: 0, 208 | true, src_name, dst_name, ext_len, &n_read, 209 | - &read_hole)) 210 | + &read_hole,&s_progress)) 211 | goto fail; 212 | 213 | dest_pos = ext_start + n_read; 214 | @@ -1263,6 +1422,70 @@ copy_reg (char const *src_name, char con 215 | buf_alloc = xmalloc (buf_size + buf_alignment); 216 | buf = ptr_align (buf_alloc, buf_alignment); 217 | 218 | + /* BEGIN progress mod */ 219 | + /* create a field of 6 lines */ 220 | + char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) ); 221 | + /* get console width */ 222 | + int iBarLength = 80; 223 | + struct winsize win; 224 | + if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 ) 225 | + iBarLength = win.ws_col; 226 | + /* create rows */ 227 | + int it; 228 | + for ( it = 0; it < 6; it++ ) 229 | + { 230 | + cProgressField[it] = ( char * ) malloc ( iBarLength + 1 ); 231 | + /* init with spaces */ 232 | + int j; 233 | + for ( j = 0; j < iBarLength; j++ ) 234 | + cProgressField[it][j] = ' '; 235 | + cProgressField[it][iBarLength] = '\0'; 236 | + } 237 | + 238 | + /* global progress bar? */ 239 | + if ( g_iTotalFiles > 1 ) 240 | + { 241 | + /* init global progress bar */ 242 | + cProgressField[2][0] = '['; 243 | + cProgressField[2][iBarLength - 8] = ']'; 244 | + cProgressField[2][iBarLength - 7] = ' '; 245 | + cProgressField[2][iBarLength - 1] = '%'; 246 | + 247 | + /* total size */ 248 | + cProgressField[1][iBarLength - 11] = '/'; 249 | + file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 ); 250 | + 251 | + /* show how many files were written */ 252 | + int sum_length = sprintf ( cProgressField[1], "%d files copied so far...", g_iFilesCopied ); 253 | + cProgressField[1][sum_length] = ' '; 254 | + } 255 | + 256 | + /* truncate filename? */ 257 | + int fn_length; 258 | + if ( strlen ( src_name ) > iBarLength - 22 ) 259 | + fn_length = 260 | + sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) ); 261 | + else 262 | + fn_length = sprintf ( cProgressField[4], "%s", src_name ); 263 | + cProgressField[4][fn_length] = ' '; 264 | + 265 | + /* filesize */ 266 | + cProgressField[4][iBarLength - 11] = '/'; 267 | + file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 ); 268 | + 269 | + int iCountDown = 1; 270 | + char * sProgressBar = cProgressField[5]; 271 | + sProgressBar[0] = '['; 272 | + sProgressBar[iBarLength - 8] = ']'; 273 | + sProgressBar[iBarLength - 7] = ' '; 274 | + sProgressBar[iBarLength - 1] = '%'; 275 | + 276 | + /* this will always save the time in between */ 277 | + struct timeval last_time; 278 | + gettimeofday ( & last_time, NULL ); 279 | + int last_size = g_iTotalWritten; 280 | + /* END progress mod */ 281 | + 282 | if (sparse_src) 283 | { 284 | bool normal_copy_required; 285 | @@ -1274,7 +1497,9 @@ copy_reg (char const *src_name, char con 286 | if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, 287 | src_open_sb.st_size, 288 | make_holes ? x->sparse_mode : SPARSE_NEVER, 289 | - src_name, dst_name, &normal_copy_required)) 290 | + src_name, dst_name, &normal_copy_required, 291 | + iCountDown, cProgressField, last_time, last_size, 292 | + iBarLength, src_open_sb)) 293 | goto preserve_metadata; 294 | 295 | if (! normal_copy_required) 296 | @@ -1286,11 +1511,12 @@ copy_reg (char const *src_name, char con 297 | 298 | off_t n_read; 299 | bool wrote_hole_at_eof; 300 | + struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 301 | if (! sparse_copy (source_desc, dest_desc, buf, buf_size, 302 | make_holes ? hole_size : 0, 303 | x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name, 304 | UINTMAX_MAX, &n_read, 305 | - &wrote_hole_at_eof)) 306 | + &wrote_hole_at_eof, &s_progress)) 307 | { 308 | return_val = false; 309 | goto close_src_and_dst_desc; 310 | @@ -1301,6 +1527,14 @@ copy_reg (char const *src_name, char con 311 | return_val = false; 312 | goto close_src_and_dst_desc; 313 | } 314 | + /* BEGIN progress mod */ 315 | + if (progress) { 316 | + int i; 317 | + for ( i = 0; i < 6; i++ ) 318 | + free ( cProgressField[i] ); 319 | + free ( cProgressField ); 320 | + } 321 | + /* END progress mod */ 322 | } 323 | 324 | preserve_metadata: 325 | diff -rup coreutils-8.25/src/copy.h coreutils-8.25-patched/src/copy.h 326 | --- coreutils-8.25/src/copy.h 2016-01-01 14:48:50.000000000 +0100 327 | +++ coreutils-8.25-patched/src/copy.h 2016-08-26 01:00:00.677989663 +0200 328 | @@ -231,6 +231,9 @@ struct cp_options 329 | Create destination directories as usual. */ 330 | bool symbolic_link; 331 | 332 | + /* If true, draw a nice progress bar on screen */ 333 | + bool progress_bar; 334 | + 335 | /* If true, do not copy a nondirectory that has an existing destination 336 | with the same or newer modification time. */ 337 | bool update; 338 | @@ -289,4 +292,15 @@ void cp_options_default (struct cp_optio 339 | bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE; 340 | mode_t cached_umask (void); 341 | 342 | +/* BEGIN OF PROGRESS MOD */ 343 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ); 344 | + 345 | +long g_iTotalSize; 346 | +long g_iTotalWritten; 347 | +int g_iFilesCopied; 348 | +struct timeval g_oStartTime; 349 | +int g_iTotalFiles; 350 | +bool progress; 351 | +/* END OF PROGRESS MOD */ 352 | + 353 | #endif 354 | Nur in coreutils-8.25-patched/src: coreutils.h. 355 | diff -rup coreutils-8.25/src/cp.c coreutils-8.25-patched/src/cp.c 356 | --- coreutils-8.25/src/cp.c 2016-01-01 14:48:50.000000000 +0100 357 | +++ coreutils-8.25-patched/src/cp.c 2016-08-26 01:06:48.546794770 +0200 358 | @@ -140,6 +140,7 @@ static struct option const long_opts[] = 359 | {"symbolic-link", no_argument, NULL, 's'}, 360 | {"target-directory", required_argument, NULL, 't'}, 361 | {"update", no_argument, NULL, 'u'}, 362 | + {"progress-bar", no_argument, NULL, 'g'}, 363 | {"verbose", no_argument, NULL, 'v'}, 364 | {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, 365 | {GETOPT_HELP_OPTION_DECL}, 366 | @@ -179,6 +180,7 @@ Copy SOURCE to DEST, or multiple SOURCE( 367 | -f, --force if an existing destination file cannot be\n\ 368 | opened, remove it and try again (this option\n\ 369 | is ignored when the -n option is also used)\n\ 370 | + -g, --progress-bar add a progress bar\n\ 371 | -i, --interactive prompt before overwrite (overrides a previous -n\ 372 | \n\ 373 | option)\n\ 374 | @@ -625,6 +627,71 @@ do_copy (int n_files, char **file, const 375 | quoteaf (file[n_files - 1])); 376 | } 377 | 378 | + struct timeval start_time; 379 | + if (progress) { 380 | + /* BEGIN progress mod */ 381 | + g_iTotalSize = 0; 382 | + g_iTotalFiles = 0; 383 | + g_iFilesCopied = 0; 384 | + g_iTotalWritten = 0; 385 | + 386 | + /* save time */ 387 | + gettimeofday ( & start_time, NULL ); 388 | + g_oStartTime = start_time; 389 | + 390 | + printf ( "Calculating total size... \r" ); 391 | + fflush ( stdout ); 392 | + long iTotalSize = 0; 393 | + int iFiles = n_files; 394 | + if ( ! target_directory ) 395 | + iFiles = n_files - 1; 396 | + int j; 397 | + 398 | + /* how many files are we copying */ 399 | + char command[1024]; 400 | + sprintf( command, "find \"%s\" -type f | wc -l", file[0]); 401 | + FILE *fp ; 402 | + char output[1024]; 403 | + fp = popen(command,"r"); 404 | + if ( fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) 405 | + printf("failed to run find.\n"); 406 | + else 407 | + g_iTotalFiles = atoi( output ) ; 408 | + 409 | + for (j = 0; j < iFiles; j++) 410 | + { 411 | + /* call du -s for each file */ 412 | + /* create command */ 413 | + char command[1024]; 414 | + sprintf ( command, "du -s \"%s\"", file[j] ); 415 | + /* TODO: replace all quote signs in file[i] */ 416 | + 417 | + FILE *fp; 418 | + char output[1024]; 419 | + 420 | + /* run command */ 421 | + fp = popen(command, "r"); 422 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 423 | + printf("failed to run du.\n" ); 424 | + } 425 | + else 426 | + { 427 | + /* isolate size */ 428 | + strchr ( output, '\t' )[0] = '\0'; 429 | + iTotalSize += atol ( output ); 430 | + 431 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 432 | + fflush ( stdout ); 433 | + } 434 | + 435 | + /* close */ 436 | + pclose(fp); 437 | + } 438 | + g_iTotalSize = iTotalSize; 439 | + /* END progress mod */ 440 | + } 441 | + 442 | + 443 | if (target_directory) 444 | { 445 | /* cp file1...filen edir 446 | @@ -767,6 +834,46 @@ do_copy (int n_files, char **file, const 447 | ok = copy (source, new_dest, 0, x, &unused, NULL); 448 | } 449 | 450 | + if (progress) { 451 | + /* BEGIN progress mod */ 452 | + /* remove everything */ 453 | + int i; 454 | + if ( g_iTotalFiles > 1 ) 455 | + { 456 | + for ( i = 0; i < 6; i++ ) 457 | + printf ( "\033[K\n" ); 458 | + printf ( "\r\033[6A" ); 459 | + } 460 | + else 461 | + { 462 | + for ( i = 0; i < 3; i++ ) 463 | + printf ( "\033[K\n" ); 464 | + printf ( "\r\033[3A" ); 465 | + } 466 | + 467 | + /* save time */ 468 | + struct timeval end_time; 469 | + gettimeofday ( & end_time, NULL ); 470 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 471 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 472 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 473 | + 474 | + /* get total size */ 475 | + char sTotalWritten[20]; 476 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 477 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 478 | + 479 | + /* calculate speed */ 480 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 481 | + char s_copy_speed[20]; 482 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 483 | + 484 | + /* good-bye message */ 485 | + printf ( "%d files (%s) copied in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 486 | + sec_elapsed, s_copy_speed ); 487 | + /* END progress mod */ 488 | + } 489 | + 490 | return ok; 491 | } 492 | 493 | @@ -801,6 +908,7 @@ cp_option_init (struct cp_options *x) 494 | x->recursive = false; 495 | x->sparse_mode = SPARSE_AUTO; 496 | x->symbolic_link = false; 497 | + x->progress_bar = false; 498 | x->set_mode = false; 499 | x->mode = 0; 500 | 501 | @@ -943,7 +1051,7 @@ main (int argc, char **argv) 502 | we'll actually use backup_suffix_string. */ 503 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 504 | 505 | - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", 506 | + while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ", 507 | long_opts, NULL)) 508 | != -1) 509 | { 510 | @@ -1000,6 +1108,10 @@ main (int argc, char **argv) 511 | x.unlink_dest_after_failed_open = true; 512 | break; 513 | 514 | + case 'g': 515 | + progress = true; 516 | + break; 517 | + 518 | case 'H': 519 | x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; 520 | break; 521 | Nur in coreutils-8.25-patched/src: .deps. 522 | Nur in coreutils-8.25-patched/src: .dirstamp. 523 | diff -rup coreutils-8.25/src/mv.c coreutils-8.25-patched/src/mv.c 524 | --- coreutils-8.25/src/mv.c 2016-01-12 12:41:44.000000000 +0100 525 | +++ coreutils-8.25-patched/src/mv.c 2016-08-26 01:11:58.525641138 +0200 526 | @@ -64,6 +64,7 @@ static struct option const long_options[ 527 | {"target-directory", required_argument, NULL, 't'}, 528 | {"update", no_argument, NULL, 'u'}, 529 | {"verbose", no_argument, NULL, 'v'}, 530 | + {"progress-ar", no_argument, NULL, 'g'}, 531 | {GETOPT_HELP_OPTION_DECL}, 532 | {GETOPT_VERSION_OPTION_DECL}, 533 | {NULL, 0, NULL, 0} 534 | @@ -163,10 +164,96 @@ target_directory_operand (char const *fi 535 | static bool 536 | do_move (const char *source, const char *dest, const struct cp_options *x) 537 | { 538 | + struct timeval start_time; 539 | + 540 | + if(progress) { 541 | + /* BEGIN progress mod */ 542 | + g_iTotalSize = 0; 543 | + g_iFilesCopied = 0; 544 | + g_iTotalWritten = 0; 545 | + 546 | + gettimeofday (& start_time, NULL); 547 | + g_oStartTime = start_time; 548 | + 549 | + printf ("Calculating total size... \r"); 550 | + fflush (stdout); 551 | + long iTotalSize = 0; 552 | + /* call du -s for each file */ 553 | + /* create command */ 554 | + char command[1024]; 555 | + sprintf ( command, "du -s \"%s\"", source ); 556 | + /* TODO: replace all quote signs in file[i] */ 557 | + 558 | + FILE *fp; 559 | + char output[1024]; 560 | + 561 | + /* run command */ 562 | + fp = popen(command, "r"); 563 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 564 | + printf("failed to run du.\n" ); 565 | + } 566 | + else 567 | + { 568 | + /* isolate size */ 569 | + strchr ( output, '\t' )[0] = '\0'; 570 | + iTotalSize += atol ( output ); 571 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 572 | + fflush ( stdout ); 573 | + } 574 | + 575 | + /* close */ 576 | + pclose(fp); 577 | + g_iTotalSize = iTotalSize; 578 | + /* END progress mod */ 579 | + 580 | + } 581 | + 582 | + 583 | + 584 | bool copy_into_self; 585 | bool rename_succeeded; 586 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 587 | 588 | + if (progress) { 589 | + /* BEGIN progress mod */ 590 | + /* remove everything */ 591 | + int i; 592 | + if ( g_iTotalFiles > 1 ) 593 | + { 594 | + for ( i = 0; i < 6; i++ ) 595 | + printf ( "\033[K\n" ); 596 | + printf ( "\r\033[6A" ); 597 | + } 598 | + else 599 | + { 600 | + for ( i = 0; i < 3; i++ ) 601 | + printf ( "\033[K\n" ); 602 | + printf ( "\r\033[3A" ); 603 | + } 604 | + 605 | + /* save time */ 606 | + struct timeval end_time; 607 | + gettimeofday ( & end_time, NULL ); 608 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 609 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 610 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 611 | + 612 | + /* get total size */ 613 | + char sTotalWritten[20]; 614 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 615 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 616 | + 617 | + /* calculate speed */ 618 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 619 | + char s_copy_speed[20]; 620 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 621 | + 622 | + /* good-bye message */ 623 | + printf ( "%d files (%s) moved in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 624 | + sec_elapsed, s_copy_speed ); 625 | + /* END progress mod */ 626 | + } 627 | + 628 | if (ok) 629 | { 630 | char const *dir_to_remove; 631 | @@ -301,6 +388,7 @@ Rename SOURCE to DEST, or move SOURCE(s) 632 | \n\ 633 | -b like --backup but does not accept an argument\n\ 634 | -f, --force do not prompt before overwriting\n\ 635 | + -g, --progress-bar add progress-bar\n\ 636 | -i, --interactive prompt before overwrite\n\ 637 | -n, --no-clobber do not overwrite an existing file\n\ 638 | If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ 639 | @@ -372,7 +460,7 @@ main (int argc, char **argv) 640 | we'll actually use backup_suffix_string. */ 641 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); 642 | 643 | - while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL)) 644 | + while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL)) 645 | != -1) 646 | { 647 | switch (c) 648 | @@ -418,6 +506,11 @@ main (int argc, char **argv) 649 | case 'v': 650 | x.verbose = true; 651 | break; 652 | + 653 | + case 'g': 654 | + progress = true; 655 | + break; 656 | + 657 | case 'S': 658 | make_backups = true; 659 | backup_suffix_string = optarg; 660 | 661 | -------------------------------------------------------------------------------- /advcpmv-0.8-8.28.patch: -------------------------------------------------------------------------------- 1 | diff -rup coreutils-8.28/src/copy.c coreutils-8.28-patched/src/copy.c 2 | --- coreutils-8.28/src/copy.c 2017-09-01 10:11:03.000000000 +0300 3 | +++ coreutils-8.28-patched/src/copy.c 2018-09-20 09:51:41.000000000 +0300 4 | @@ -128,6 +128,72 @@ struct dir_list 5 | dev_t dev; 6 | }; 7 | 8 | +struct progress_status { 9 | + int iCountDown; 10 | + char ** cProgressField; 11 | + struct timeval last_time; 12 | + int last_size, iBarLength; 13 | + struct stat src_open_sb; 14 | +}; 15 | + 16 | +/* Begin progress Mod*/ 17 | +static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal ) 18 | +{ 19 | + double dPercent = (double) _lProgress / (double) _lTotal * 100.f; 20 | + sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent ); 21 | + _cDest[_iBarLength - 2] = ' '; 22 | + 23 | + int i; 24 | + for ( i=1; i<=_iBarLength - 9; i++) 25 | + { 26 | + if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f ) 27 | + { 28 | + _cDest[i] = '='; 29 | + } 30 | + else 31 | + { 32 | + _cDest[i] = ' '; 33 | + } 34 | + } 35 | + for ( i=1; i<_iBarLength - 9; i++) 36 | + { 37 | + if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) ) 38 | + _cDest[i] = '>' ; 39 | + } 40 | +} 41 | + 42 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ) 43 | +{ 44 | + int iCounter = _iCounter; 45 | + double dSize = ( double ) _lSize; 46 | + while ( dSize >= 1000. ) 47 | + { 48 | + dSize /= 1024.; 49 | + iCounter++; 50 | + } 51 | + 52 | + /* get unit */ 53 | + char * sUnit; 54 | + if ( iCounter == 0 ) 55 | + sUnit = "B"; 56 | + else if ( iCounter == 1 ) 57 | + sUnit = "KiB"; 58 | + else if ( iCounter == 2 ) 59 | + sUnit = "MiB"; 60 | + else if ( iCounter == 3 ) 61 | + sUnit = "GiB"; 62 | + else if ( iCounter == 4 ) 63 | + sUnit = "TiB"; 64 | + else 65 | + sUnit = "N/A"; 66 | + 67 | + /* write number */ 68 | + return sprintf ( _cDst, "%5.1f %s", dSize, sUnit ); 69 | +} 70 | +/* END progress mod */ 71 | + 72 | + 73 | + 74 | /* Initial size of the cp.dest_info hash table. */ 75 | #define DEST_INFO_INITIAL_CAPACITY 61 76 | 77 | @@ -224,7 +290,8 @@ sparse_copy (int src_fd, int dest_fd, ch 78 | size_t hole_size, bool punch_holes, 79 | char const *src_name, char const *dst_name, 80 | uintmax_t max_n_read, off_t *total_n_read, 81 | - bool *last_write_made_hole) 82 | + bool *last_write_made_hole, 83 | + struct progress_status *s_progress) 84 | { 85 | *last_write_made_hole = false; 86 | *total_n_read = 0; 87 | @@ -233,6 +300,83 @@ sparse_copy (int src_fd, int dest_fd, ch 88 | 89 | while (max_n_read) 90 | { 91 | + 92 | + if (progress) { 93 | + /* BEGIN progress mod */ 94 | + /* update countdown */ 95 | + s_progress->iCountDown--; 96 | + char * sProgressBar = s_progress->cProgressField[5]; 97 | + if ( s_progress->iCountDown < 0 ) 98 | + s_progress->iCountDown = 100; 99 | + 100 | + /* just print one line with the percentage, but not always */ 101 | + if ( s_progress->iCountDown == 0 ) 102 | + { 103 | + /* calculate current speed */ 104 | + struct timeval cur_time; 105 | + gettimeofday ( & cur_time, NULL ); 106 | + int cur_size = g_iTotalWritten + *total_n_read / 1024; 107 | + int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec; 108 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 109 | + sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec ); 110 | + int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size ) 111 | + / sec_elapsed ); 112 | + char s_copy_speed[20]; 113 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 114 | + /* update vars */ 115 | + s_progress->last_time = cur_time; 116 | + s_progress->last_size = cur_size; 117 | + 118 | + /* how many time has passed since the start? */ 119 | + int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec; 120 | + int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size 121 | + * g_iTotalSize ) - isec_elapsed; 122 | + int min_remaining = sec_remaining / 60; 123 | + sec_remaining -= min_remaining * 60; 124 | + int hours_remaining = min_remaining / 60; 125 | + min_remaining -= hours_remaining * 60; 126 | + /* print out */ 127 | + sprintf ( s_progress->cProgressField[3], 128 | + "Copying at %s/s (about %uh %um %us remaining)", s_copy_speed, 129 | + hours_remaining, min_remaining, sec_remaining ); 130 | + 131 | + int fs_len; 132 | + if ( g_iTotalFiles > 1 ) 133 | + { 134 | + /* global progress bar */ 135 | + file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength, 136 | + g_iTotalWritten + *total_n_read / 1024, g_iTotalSize ); 137 | + 138 | + /* print the global status */ 139 | + fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21, 140 | + g_iTotalWritten + *total_n_read / 1024, 1 ); 141 | + s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' '; 142 | + } 143 | + 144 | + /* current progress bar */ 145 | + file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size ); 146 | + 147 | + /* print the status */ 148 | + fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 ); 149 | + s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' '; 150 | + 151 | + /* print the field */ 152 | + int it; 153 | + for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ ) 154 | + { 155 | + printf ( "\033[K%s\n", s_progress->cProgressField[it] ); 156 | + if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength ) 157 | + printf ( "" ); 158 | + } 159 | + if ( g_iTotalFiles > 1 ) 160 | + printf ( "\r\033[6A" ); 161 | + else 162 | + printf ( "\r\033[3A" ); 163 | + fflush ( stdout ); 164 | + } 165 | + /* END progress mod */ 166 | + } 167 | + 168 | ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size)); 169 | if (n_read < 0) 170 | { 171 | @@ -318,7 +462,16 @@ sparse_copy (int src_fd, int dest_fd, ch 172 | for each file. Unfortunately that doesn't work for 173 | certain files in /proc or /sys with linux kernels. */ 174 | } 175 | - 176 | + 177 | + /* BEGIN progress mod */ 178 | + if (progress) { 179 | + /* update total size */ 180 | + g_iTotalWritten += *total_n_read / 1024; 181 | + g_iFilesCopied++; 182 | + } 183 | + /* END progress mod */ 184 | + 185 | + 186 | /* Ensure a trailing hole is created, so that subsequent 187 | calls of sparse_copy() start at the correct offset. */ 188 | if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize)) 189 | @@ -387,7 +540,9 @@ extent_copy (int src_fd, int dest_fd, ch 190 | size_t hole_size, off_t src_total_size, 191 | enum Sparse_type sparse_mode, 192 | char const *src_name, char const *dst_name, 193 | - bool *require_normal_copy) 194 | + bool *require_normal_copy, 195 | + int iCountDown, char ** cProgressField, struct timeval last_time, 196 | + int last_size, int iBarLength, struct stat src_open_sb) 197 | { 198 | struct extent_scan scan; 199 | off_t last_ext_start = 0; 200 | @@ -518,10 +673,15 @@ extent_copy (int src_fd, int dest_fd, ch 201 | last_ext_len = ext_len; 202 | bool read_hole; 203 | 204 | + struct timeval a; 205 | + struct stat b; 206 | + 207 | + struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 208 | + 209 | if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, 210 | sparse_mode == SPARSE_ALWAYS ? hole_size: 0, 211 | true, src_name, dst_name, ext_len, &n_read, 212 | - &read_hole)) 213 | + &read_hole,&s_progress)) 214 | goto fail; 215 | 216 | dest_pos = ext_start + n_read; 217 | @@ -1269,7 +1429,71 @@ copy_reg (char const *src_name, char con 218 | 219 | buf_alloc = xmalloc (buf_size + buf_alignment); 220 | buf = ptr_align (buf_alloc, buf_alignment); 221 | + 222 | + /* BEGIN progress mod */ 223 | + /* create a field of 6 lines */ 224 | + char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) ); 225 | + /* get console width */ 226 | + int iBarLength = 80; 227 | + struct winsize win; 228 | + if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 ) 229 | + iBarLength = win.ws_col; 230 | + /* create rows */ 231 | + int it; 232 | + for ( it = 0; it < 6; it++ ) 233 | + { 234 | + cProgressField[it] = ( char * ) malloc ( iBarLength + 1 ); 235 | + /* init with spaces */ 236 | + int j; 237 | + for ( j = 0; j < iBarLength; j++ ) 238 | + cProgressField[it][j] = ' '; 239 | + cProgressField[it][iBarLength] = '\0'; 240 | + } 241 | + 242 | + /* global progress bar? */ 243 | + if ( g_iTotalFiles > 1 ) 244 | + { 245 | + /* init global progress bar */ 246 | + cProgressField[2][0] = '['; 247 | + cProgressField[2][iBarLength - 8] = ']'; 248 | + cProgressField[2][iBarLength - 7] = ' '; 249 | + cProgressField[2][iBarLength - 1] = '%'; 250 | + 251 | + /* total size */ 252 | + cProgressField[1][iBarLength - 11] = '/'; 253 | + file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 ); 254 | + 255 | + /* show how many files were written */ 256 | + int sum_length = sprintf ( cProgressField[1], "%d files copied so far...", g_iFilesCopied ); 257 | + cProgressField[1][sum_length] = ' '; 258 | + } 259 | + 260 | + /* truncate filename? */ 261 | + int fn_length; 262 | + if ( strlen ( src_name ) > iBarLength - 22 ) 263 | + fn_length = 264 | + sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) ); 265 | + else 266 | + fn_length = sprintf ( cProgressField[4], "%s", src_name ); 267 | + cProgressField[4][fn_length] = ' '; 268 | 269 | + /* filesize */ 270 | + cProgressField[4][iBarLength - 11] = '/'; 271 | + file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 ); 272 | + 273 | + int iCountDown = 1; 274 | + char * sProgressBar = cProgressField[5]; 275 | + sProgressBar[0] = '['; 276 | + sProgressBar[iBarLength - 8] = ']'; 277 | + sProgressBar[iBarLength - 7] = ' '; 278 | + sProgressBar[iBarLength - 1] = '%'; 279 | + 280 | + /* this will always save the time in between */ 281 | + struct timeval last_time; 282 | + gettimeofday ( & last_time, NULL ); 283 | + int last_size = g_iTotalWritten; 284 | + /* END progress mod */ 285 | + 286 | if (sparse_src) 287 | { 288 | bool normal_copy_required; 289 | @@ -1281,7 +1505,9 @@ copy_reg (char const *src_name, char con 290 | if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, 291 | src_open_sb.st_size, 292 | make_holes ? x->sparse_mode : SPARSE_NEVER, 293 | - src_name, dst_name, &normal_copy_required)) 294 | + src_name, dst_name, &normal_copy_required, 295 | + iCountDown, cProgressField, last_time, last_size, 296 | + iBarLength, src_open_sb)) 297 | goto preserve_metadata; 298 | 299 | if (! normal_copy_required) 300 | @@ -1293,11 +1519,12 @@ copy_reg (char const *src_name, char con 301 | 302 | off_t n_read; 303 | bool wrote_hole_at_eof; 304 | + struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 305 | if (! sparse_copy (source_desc, dest_desc, buf, buf_size, 306 | make_holes ? hole_size : 0, 307 | x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name, 308 | UINTMAX_MAX, &n_read, 309 | - &wrote_hole_at_eof)) 310 | + &wrote_hole_at_eof, &s_progress)) 311 | { 312 | return_val = false; 313 | goto close_src_and_dst_desc; 314 | @@ -1308,6 +1535,14 @@ copy_reg (char const *src_name, char con 315 | return_val = false; 316 | goto close_src_and_dst_desc; 317 | } 318 | + /* BEGIN progress mod */ 319 | + if (progress) { 320 | + int i; 321 | + for ( i = 0; i < 6; i++ ) 322 | + free ( cProgressField[i] ); 323 | + free ( cProgressField ); 324 | + } 325 | + /* END progress mod */ 326 | } 327 | 328 | preserve_metadata: 329 | diff -rup coreutils-8.28/src/copy.h coreutils-8.28-patched/src/copy.h 330 | --- coreutils-8.28/src/copy.h 2017-09-01 10:11:03.000000000 +0300 331 | +++ coreutils-8.28-patched/src/copy.h 2018-09-20 09:32:47.000000000 +0300 332 | @@ -233,7 +233,10 @@ struct cp_options 333 | /* If true, create symbolic links instead of copying files. 334 | Create destination directories as usual. */ 335 | bool symbolic_link; 336 | - 337 | + 338 | + /* If true, draw a nice progress bar on screen */ 339 | + bool progress_bar; 340 | + 341 | /* If true, do not copy a nondirectory that has an existing destination 342 | with the same or newer modification time. */ 343 | bool update; 344 | @@ -300,4 +303,15 @@ void cp_options_default (struct cp_optio 345 | bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE; 346 | mode_t cached_umask (void); 347 | 348 | +/* BEGIN OF PROGRESS MOD */ 349 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ); 350 | + 351 | +long g_iTotalSize; 352 | +long g_iTotalWritten; 353 | +int g_iFilesCopied; 354 | +struct timeval g_oStartTime; 355 | +int g_iTotalFiles; 356 | +bool progress; 357 | +/* END OF PROGRESS MOD */ 358 | + 359 | #endif 360 | diff -rup coreutils-8.28/src/cp.c coreutils-8.28-patched/src/cp.c 361 | --- coreutils-8.28/src/cp.c 2017-09-01 10:11:03.000000000 +0300 362 | +++ coreutils-8.28-patched/src/cp.c 2018-09-20 09:40:15.000000000 +0300 363 | @@ -141,6 +141,7 @@ static struct option const long_opts[] = 364 | {"symbolic-link", no_argument, NULL, 's'}, 365 | {"target-directory", required_argument, NULL, 't'}, 366 | {"update", no_argument, NULL, 'u'}, 367 | + {"progress-bar", no_argument, NULL, 'g'}, 368 | {"verbose", no_argument, NULL, 'v'}, 369 | {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, 370 | {GETOPT_HELP_OPTION_DECL}, 371 | @@ -180,6 +181,7 @@ Copy SOURCE to DEST, or multiple SOURCE( 372 | -f, --force if an existing destination file cannot be\n\ 373 | opened, remove it and try again (this option\n\ 374 | is ignored when the -n option is also used)\n\ 375 | + -g, --progress-bar add a progress bar\n\ 376 | -i, --interactive prompt before overwrite (overrides a previous -n\ 377 | \n\ 378 | option)\n\ 379 | @@ -634,7 +636,71 @@ do_copy (int n_files, char **file, const 380 | die (EXIT_FAILURE, 0, _("target %s is not a directory"), 381 | quoteaf (file[n_files - 1])); 382 | } 383 | + 384 | + struct timeval start_time; 385 | + if (progress) { 386 | + /* BEGIN progress mod */ 387 | + g_iTotalSize = 0; 388 | + g_iTotalFiles = 0; 389 | + g_iFilesCopied = 0; 390 | + g_iTotalWritten = 0; 391 | + 392 | + /* save time */ 393 | + gettimeofday ( & start_time, NULL ); 394 | + g_oStartTime = start_time; 395 | + 396 | + printf ( "Calculating total size... \r" ); 397 | + fflush ( stdout ); 398 | + long iTotalSize = 0; 399 | + int iFiles = n_files; 400 | + if ( ! target_directory ) 401 | + iFiles = n_files - 1; 402 | + int j; 403 | + 404 | + /* how many files are we copying */ 405 | + char command[1024]; 406 | + sprintf( command, "find \"%s\" -type f | wc -l", file[0]); 407 | + FILE *fp ; 408 | + char output[1024]; 409 | + fp = popen(command,"r"); 410 | + if ( fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) 411 | + printf("failed to run find.\n"); 412 | + else 413 | + g_iTotalFiles = atoi( output ) ; 414 | 415 | + for (j = 0; j < iFiles; j++) 416 | + { 417 | + /* call du -s for each file */ 418 | + /* create command */ 419 | + char command[1024]; 420 | + sprintf ( command, "du -s \"%s\"", file[j] ); 421 | + /* TODO: replace all quote signs in file[i] */ 422 | + 423 | + FILE *fp; 424 | + char output[1024]; 425 | + 426 | + /* run command */ 427 | + fp = popen(command, "r"); 428 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 429 | + printf("failed to run du.\n" ); 430 | + } 431 | + else 432 | + { 433 | + /* isolate size */ 434 | + strchr ( output, '\t' )[0] = '\0'; 435 | + iTotalSize += atol ( output ); 436 | + 437 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 438 | + fflush ( stdout ); 439 | + } 440 | + 441 | + /* close */ 442 | + pclose(fp); 443 | + } 444 | + g_iTotalSize = iTotalSize; 445 | + /* END progress mod */ 446 | + } 447 | + 448 | if (target_directory) 449 | { 450 | /* cp file1...filen edir 451 | @@ -775,6 +841,46 @@ do_copy (int n_files, char **file, const 452 | 453 | ok = copy (source, new_dest, 0, x, &unused, NULL); 454 | } 455 | + 456 | + if (progress) { 457 | + /* BEGIN progress mod */ 458 | + /* remove everything */ 459 | + int i; 460 | + if ( g_iTotalFiles > 1 ) 461 | + { 462 | + for ( i = 0; i < 6; i++ ) 463 | + printf ( "\033[K\n" ); 464 | + printf ( "\r\033[6A" ); 465 | + } 466 | + else 467 | + { 468 | + for ( i = 0; i < 3; i++ ) 469 | + printf ( "\033[K\n" ); 470 | + printf ( "\r\033[3A" ); 471 | + } 472 | + 473 | + /* save time */ 474 | + struct timeval end_time; 475 | + gettimeofday ( & end_time, NULL ); 476 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 477 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 478 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 479 | + 480 | + /* get total size */ 481 | + char sTotalWritten[20]; 482 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 483 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 484 | + 485 | + /* calculate speed */ 486 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 487 | + char s_copy_speed[20]; 488 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 489 | + 490 | + /* good-bye message */ 491 | + printf ( "%d files (%s) copied in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 492 | + sec_elapsed, s_copy_speed ); 493 | + /* END progress mod */ 494 | + } 495 | 496 | return ok; 497 | } 498 | @@ -811,6 +917,7 @@ cp_option_init (struct cp_options *x) 499 | x->recursive = false; 500 | x->sparse_mode = SPARSE_AUTO; 501 | x->symbolic_link = false; 502 | + x->progress_bar = false; 503 | x->set_mode = false; 504 | x->mode = 0; 505 | 506 | @@ -949,7 +1056,7 @@ main (int argc, char **argv) 507 | selinux_enabled = (0 < is_selinux_enabled ()); 508 | cp_option_init (&x); 509 | 510 | - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", 511 | + while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ", 512 | long_opts, NULL)) 513 | != -1) 514 | { 515 | @@ -1006,6 +1113,10 @@ main (int argc, char **argv) 516 | x.unlink_dest_after_failed_open = true; 517 | break; 518 | 519 | + case 'g': 520 | + progress = true; 521 | + break; 522 | + 523 | case 'H': 524 | x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; 525 | break; 526 | diff -rup coreutils-8.28/src/mv.c coreutils-8.28-patched/src/mv.c 527 | --- coreutils-8.28/src/mv.c 2017-09-01 10:11:03.000000000 +0300 528 | +++ coreutils-8.28-patched/src/mv.c 2018-09-20 09:43:35.000000000 +0300 529 | @@ -65,6 +65,7 @@ static struct option const long_options[ 530 | {"target-directory", required_argument, NULL, 't'}, 531 | {"update", no_argument, NULL, 'u'}, 532 | {"verbose", no_argument, NULL, 'v'}, 533 | + {"progress-ar", no_argument, NULL, 'g'}, 534 | {GETOPT_HELP_OPTION_DECL}, 535 | {GETOPT_VERSION_OPTION_DECL}, 536 | {NULL, 0, NULL, 0} 537 | @@ -165,10 +166,97 @@ target_directory_operand (char const *fi 538 | static bool 539 | do_move (const char *source, const char *dest, const struct cp_options *x) 540 | { 541 | + 542 | + struct timeval start_time; 543 | + 544 | + if(progress) { 545 | + /* BEGIN progress mod */ 546 | + g_iTotalSize = 0; 547 | + g_iFilesCopied = 0; 548 | + g_iTotalWritten = 0; 549 | + 550 | + gettimeofday (& start_time, NULL); 551 | + g_oStartTime = start_time; 552 | + 553 | + printf ("Calculating total size... \r"); 554 | + fflush (stdout); 555 | + long iTotalSize = 0; 556 | + /* call du -s for each file */ 557 | + /* create command */ 558 | + char command[1024]; 559 | + sprintf ( command, "du -s \"%s\"", source ); 560 | + /* TODO: replace all quote signs in file[i] */ 561 | + 562 | + FILE *fp; 563 | + char output[1024]; 564 | + 565 | + /* run command */ 566 | + fp = popen(command, "r"); 567 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 568 | + printf("failed to run du.\n" ); 569 | + } 570 | + else 571 | + { 572 | + /* isolate size */ 573 | + strchr ( output, '\t' )[0] = '\0'; 574 | + iTotalSize += atol ( output ); 575 | + printf ( "Calculating total size... %ld\r", iTotalSize ); 576 | + fflush ( stdout ); 577 | + } 578 | + 579 | + /* close */ 580 | + pclose(fp); 581 | + g_iTotalSize = iTotalSize; 582 | + /* END progress mod */ 583 | + 584 | + } 585 | + 586 | + 587 | + 588 | bool copy_into_self; 589 | bool rename_succeeded; 590 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 591 | + 592 | + if (progress) { 593 | + /* BEGIN progress mod */ 594 | + /* remove everything */ 595 | + int i; 596 | + if ( g_iTotalFiles > 1 ) 597 | + { 598 | + for ( i = 0; i < 6; i++ ) 599 | + printf ( "\033[K\n" ); 600 | + printf ( "\r\033[6A" ); 601 | + } 602 | + else 603 | + { 604 | + for ( i = 0; i < 3; i++ ) 605 | + printf ( "\033[K\n" ); 606 | + printf ( "\r\033[3A" ); 607 | + } 608 | 609 | + /* save time */ 610 | + struct timeval end_time; 611 | + gettimeofday ( & end_time, NULL ); 612 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 613 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 614 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 615 | + 616 | + /* get total size */ 617 | + char sTotalWritten[20]; 618 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 619 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 620 | + 621 | + /* calculate speed */ 622 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 623 | + char s_copy_speed[20]; 624 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 625 | + 626 | + /* good-bye message */ 627 | + printf ( "%d files (%s) moved in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten, 628 | + sec_elapsed, s_copy_speed ); 629 | + /* END progress mod */ 630 | + } 631 | + 632 | if (ok) 633 | { 634 | char const *dir_to_remove; 635 | @@ -303,6 +391,7 @@ Rename SOURCE to DEST, or move SOURCE(s) 636 | \n\ 637 | -b like --backup but does not accept an argument\n\ 638 | -f, --force do not prompt before overwriting\n\ 639 | + -g, --progress-bar add progress-bar\n\ 640 | -i, --interactive prompt before overwrite\n\ 641 | -n, --no-clobber do not overwrite an existing file\n\ 642 | If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ 643 | @@ -358,7 +447,7 @@ main (int argc, char **argv) 644 | /* Try to disable the ability to unlink a directory. */ 645 | priv_set_remove_linkdir (); 646 | 647 | - while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL)) 648 | + while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL)) 649 | != -1) 650 | { 651 | switch (c) 652 | @@ -404,6 +493,9 @@ main (int argc, char **argv) 653 | case 'v': 654 | x.verbose = true; 655 | break; 656 | + case 'g': 657 | + progress = true; 658 | + break; 659 | case 'S': 660 | make_backups = true; 661 | backup_suffix = optarg; 662 | -------------------------------------------------------------------------------- /advcpmv-0.8-8.32.patch: -------------------------------------------------------------------------------- 1 | diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c 2 | --- coreutils-8.32/src/copy.c 2020-01-01 15:13:12.000000000 +0100 3 | +++ coreutils-8.32-patched/src/copy.c 2022-04-03 21:03:04.869415222 +0200 4 | @@ -129,6 +129,133 @@ 5 | dev_t dev; 6 | }; 7 | 8 | +/* BEGIN progress mod */ 9 | +struct progress_status { 10 | + int iCountDown; 11 | + char ** cProgressField; 12 | + struct timeval last_time; 13 | + int last_size, iBarLength; 14 | + struct stat src_open_sb; 15 | +}; 16 | + 17 | +FILE * spawn( const char *cmd, char *const argv[] ) 18 | +{ 19 | + FILE *ret = NULL; 20 | + int pfd_read[2]; 21 | + pid_t pid; 22 | + 23 | + if (cmd == NULL || argv == NULL) 24 | + return ret; 25 | + 26 | + if (pipe(pfd_read) < 0) { 27 | + error(0, errno, "pipe: %s", cmd); 28 | + return ret; 29 | + } 30 | + 31 | + if ((pid = fork()) == 0) { 32 | + int err = dup2(pfd_read[1], 1) < 0; 33 | + close(pfd_read[0]); 34 | + close(pfd_read[1]); 35 | + 36 | + if (err) 37 | + error(EXIT_FAILURE, errno, "dup2: %s", cmd); 38 | + execvp(cmd, argv); 39 | + error(EXIT_FAILURE, errno, "exec: %s", cmd); 40 | + } 41 | + 42 | + close(pfd_read[1]); 43 | + 44 | + if (pid < 0) { 45 | + close(pfd_read[0]); 46 | + error(0, errno, "fork: %s", cmd); 47 | + return ret; 48 | + } 49 | + 50 | + ret = fdopen(pfd_read[0], "r"); 51 | + return ret; 52 | +} 53 | + 54 | +void format_time ( char * _cDest, double seconds, bool showall ) 55 | +{ 56 | + // hours 57 | + int hr = ( (int) seconds / (60 * 60)) % 24; 58 | + // minutes 59 | + int min = ( (int) seconds / 60) % 60; 60 | + // seconds 61 | + double sec = seconds - (hr * (60 * 60)) - (min * 60); 62 | + if ( showall ) 63 | + { 64 | + if ( seconds < 0 ) 65 | + sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?'); 66 | + else 67 | + sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec); 68 | + } else if ( seconds >= 3600 ) 69 | + { 70 | + sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec); 71 | + } else if ( seconds >= 60 ) 72 | + { 73 | + sprintf(_cDest, "%2dm %4.1fs", min, sec); 74 | + } else 75 | + { 76 | + sprintf(_cDest, "%4.1fs", sec); 77 | + } 78 | +} 79 | + 80 | +static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal ) 81 | +{ 82 | + double dPercent = (double) _lProgress / (double) _lTotal * 100.f; 83 | + sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent ); 84 | + _cDest[_iBarLength - 2] = ' '; 85 | + 86 | + int i; 87 | + for ( i=1; i<=_iBarLength - 9; i++) 88 | + { 89 | + if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f ) 90 | + { 91 | + _cDest[i] = '='; 92 | + } 93 | + else 94 | + { 95 | + _cDest[i] = ' '; 96 | + } 97 | + } 98 | + for ( i=1; i<_iBarLength - 9; i++) 99 | + { 100 | + if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) ) 101 | + _cDest[i] = '>' ; 102 | + } 103 | +} 104 | + 105 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ) 106 | +{ 107 | + int iCounter = _iCounter; 108 | + double dSize = ( double ) _lSize; 109 | + while ( dSize >= 1000. ) 110 | + { 111 | + dSize /= 1024.; 112 | + iCounter++; 113 | + } 114 | + 115 | + /* get unit */ 116 | + char * sUnit; 117 | + if ( iCounter == 0 ) 118 | + sUnit = "B"; 119 | + else if ( iCounter == 1 ) 120 | + sUnit = "KiB"; 121 | + else if ( iCounter == 2 ) 122 | + sUnit = "MiB"; 123 | + else if ( iCounter == 3 ) 124 | + sUnit = "GiB"; 125 | + else if ( iCounter == 4 ) 126 | + sUnit = "TiB"; 127 | + else 128 | + sUnit = "N/A"; 129 | + 130 | + /* write number */ 131 | + return sprintf ( _cDst, "%5.1f %s", dSize, sUnit ); 132 | +} 133 | +/* END progress mod */ 134 | + 135 | /* Initial size of the cp.dest_info hash table. */ 136 | #define DEST_INFO_INITIAL_CAPACITY 61 137 | 138 | @@ -258,18 +385,125 @@ 139 | bytes read. */ 140 | static bool 141 | sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, 142 | - size_t hole_size, bool punch_holes, 143 | + size_t hole_size, bool punch_holes, bool move_mode, 144 | char const *src_name, char const *dst_name, 145 | uintmax_t max_n_read, off_t *total_n_read, 146 | - bool *last_write_made_hole) 147 | + bool *last_write_made_hole, 148 | + struct progress_status *s_progress) 149 | { 150 | *last_write_made_hole = false; 151 | *total_n_read = 0; 152 | bool make_hole = false; 153 | off_t psize = 0; 154 | 155 | + /* BEGIN progress mod */ 156 | + gettimeofday ( & g_oFStartTime, NULL ); 157 | + g_iFTotalWritten = 0; 158 | + struct stat st; 159 | + stat(src_name, &st); 160 | + g_iFTotalSize = st.st_size/1024; 161 | + /* END progress mod */ 162 | + 163 | while (max_n_read) 164 | { 165 | + 166 | + /* BEGIN progress mod */ 167 | + if (progress) { 168 | + /* update countdown */ 169 | + s_progress->iCountDown--; 170 | + char * sProgressBar = s_progress->cProgressField[5]; 171 | + if ( s_progress->iCountDown < 0 ) 172 | + s_progress->iCountDown = 100; 173 | + 174 | + /* just print one line with the percentage, but not always */ 175 | + if ( s_progress->iCountDown == 0 ) 176 | + { 177 | + /* calculate current speed */ 178 | + struct timeval cur_time; 179 | + gettimeofday ( & cur_time, NULL ); 180 | + int cur_size = g_iTotalWritten + *total_n_read / 1024; 181 | + int cur_fsize = g_iFTotalWritten + *total_n_read / 1024; 182 | + int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec; 183 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 184 | + sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec ); 185 | + int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size ) 186 | + / sec_elapsed ); 187 | + char s_copy_speed[20]; 188 | + file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 ); 189 | + /* update vars */ 190 | + s_progress->last_time = cur_time; 191 | + s_progress->last_size = cur_size; 192 | + 193 | + /* how many time has passed since the start? */ 194 | + int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec; 195 | + int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec; 196 | + int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size 197 | + * g_iTotalSize ) - isec_elapsed; 198 | + int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize 199 | + * g_iFTotalSize ) - isec_felapsed; 200 | + /* print out */ 201 | + 202 | + char f_ttime[20]; 203 | + char f_ftime[20]; 204 | + format_time(f_ttime, sec_remaining, true); 205 | + format_time(f_ftime, sec_fremaining, true); 206 | + 207 | + int fs_len; 208 | + fs_len = sprintf ( s_progress->cProgressField[1], 209 | + move_mode 210 | + ? "%d of %d files moved (about %s remaining)" 211 | + : "%d of %d files copied (about %s remaining)", 212 | + g_iFilesCopied, g_iTotalFiles, f_ttime ); 213 | + s_progress->cProgressField[1][fs_len] = ' '; 214 | + 215 | + char s_ftime[40] = ""; 216 | + 217 | + if (g_iTotalFiles > 1) 218 | + sprintf ( s_ftime, "(about %s remaining)", f_ftime ); 219 | + else 220 | + sprintf ( s_ftime, "(about %s remaining)", f_ttime ); 221 | + 222 | + sprintf ( s_progress->cProgressField[3], 223 | + move_mode 224 | + ? "moving at %s/s %s" 225 | + : "copying at %s/s %s", s_copy_speed, s_ftime ); 226 | + 227 | + if ( g_iTotalFiles > 1 ) 228 | + { 229 | + /* global progress bar */ 230 | + file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength, 231 | + g_iTotalWritten + *total_n_read / 1024, g_iTotalSize ); 232 | + 233 | + /* print the global status */ 234 | + fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21, 235 | + g_iTotalWritten + *total_n_read / 1024, 1 ); 236 | + s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' '; 237 | + } 238 | + 239 | + /* current progress bar */ 240 | + file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size ); 241 | + 242 | + /* print the status */ 243 | + fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 ); 244 | + s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' '; 245 | + 246 | + /* print the field */ 247 | + int it; 248 | + for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ ) 249 | + { 250 | + printf ( "\033[K%s\n", s_progress->cProgressField[it] ); 251 | + if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength ) 252 | + printf ( "%s", "" ); 253 | + } 254 | + if ( g_iTotalFiles > 1 ) 255 | + printf ( "\r\033[6A" ); 256 | + else 257 | + printf ( "\r\033[3A" ); 258 | + fflush ( stdout ); 259 | + } 260 | + } 261 | + /* END progress mod */ 262 | + 263 | ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size)); 264 | if (n_read < 0) 265 | { 266 | @@ -354,6 +588,14 @@ 267 | certain files in /proc or /sys with linux kernels. */ 268 | } 269 | 270 | + /* BEGIN progress mod */ 271 | + if (progress) { 272 | + /* update total size */ 273 | + g_iTotalWritten += *total_n_read / 1024; 274 | + g_iFilesCopied++; 275 | + } 276 | + /* END progress mod */ 277 | + 278 | /* Ensure a trailing hole is created, so that subsequent 279 | calls of sparse_copy() start at the correct offset. */ 280 | if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize)) 281 | @@ -420,9 +662,11 @@ 282 | static bool 283 | extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size, 284 | size_t hole_size, off_t src_total_size, 285 | - enum Sparse_type sparse_mode, 286 | + enum Sparse_type sparse_mode, bool move_mode, 287 | char const *src_name, char const *dst_name, 288 | - bool *require_normal_copy) 289 | + bool *require_normal_copy, 290 | + int iCountDown, char ** cProgressField, struct timeval last_time, 291 | + int last_size, int iBarLength, struct stat src_open_sb) 292 | { 293 | struct extent_scan scan; 294 | off_t last_ext_start = 0; 295 | @@ -553,10 +797,16 @@ 296 | last_ext_len = ext_len; 297 | bool read_hole; 298 | 299 | + struct timeval a; 300 | + struct stat b; 301 | + 302 | + struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 303 | + 304 | + 305 | if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size, 306 | sparse_mode == SPARSE_ALWAYS ? hole_size: 0, 307 | - true, src_name, dst_name, ext_len, &n_read, 308 | - &read_hole)) 309 | + true, move_mode, src_name, dst_name, ext_len, 310 | + &n_read, &read_hole, &s_progress)) 311 | goto fail; 312 | 313 | dest_pos = ext_start + n_read; 314 | @@ -1305,6 +1555,75 @@ 315 | buf_alloc = xmalloc (buf_size + buf_alignment); 316 | buf = ptr_align (buf_alloc, buf_alignment); 317 | 318 | + /* BEGIN progress mod */ 319 | + /* create a field of 6 lines */ 320 | + char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) ); 321 | + /* get console width */ 322 | + int iBarLength = 80; 323 | + struct winsize win; 324 | + if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 ) 325 | + if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */ 326 | + iBarLength = win.ws_col; 327 | + /* create rows */ 328 | + int it; 329 | + for ( it = 0; it < 6; it++ ) 330 | + { 331 | + cProgressField[it] = ( char * ) malloc ( iBarLength + 1 ); 332 | + /* init with spaces */ 333 | + int j; 334 | + for ( j = 0; j < iBarLength; j++ ) 335 | + cProgressField[it][j] = ' '; 336 | + cProgressField[it][iBarLength] = '\0'; 337 | + } 338 | + 339 | + /* global progress bar? */ 340 | + if ( g_iTotalFiles > 1 ) 341 | + { 342 | + /* init global progress bar */ 343 | + cProgressField[2][0] = '['; 344 | + cProgressField[2][iBarLength - 8] = ']'; 345 | + cProgressField[2][iBarLength - 7] = ' '; 346 | + cProgressField[2][iBarLength - 1] = '%'; 347 | + 348 | + /* total size */ 349 | + cProgressField[1][iBarLength - 11] = '/'; 350 | + file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 ); 351 | + 352 | + /* show how many files were written */ 353 | + int sum_length = 0; 354 | + sum_length = sprintf ( cProgressField[1], 355 | + x->move_mode 356 | + ? "%d of %d files moved so far" 357 | + : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles ); 358 | + cProgressField[1][sum_length] = ' '; 359 | + } 360 | + 361 | + /* truncate filename? */ 362 | + int fn_length; 363 | + if ( strlen ( src_name ) > iBarLength - 22 ) 364 | + fn_length = 365 | + sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) ); 366 | + else 367 | + fn_length = sprintf ( cProgressField[4], "%s", src_name ); 368 | + cProgressField[4][fn_length] = ' '; 369 | + 370 | + /* filesize */ 371 | + cProgressField[4][iBarLength - 11] = '/'; 372 | + file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 ); 373 | + 374 | + int iCountDown = 1; 375 | + char * sProgressBar = cProgressField[5]; 376 | + sProgressBar[0] = '['; 377 | + sProgressBar[iBarLength - 8] = ']'; 378 | + sProgressBar[iBarLength - 7] = ' '; 379 | + sProgressBar[iBarLength - 1] = '%'; 380 | + 381 | + /* this will always save the time in between */ 382 | + struct timeval last_time; 383 | + gettimeofday ( & last_time, NULL ); 384 | + int last_size = g_iTotalWritten; 385 | + /* END progress mod */ 386 | + 387 | if (sparse_src) 388 | { 389 | bool normal_copy_required; 390 | @@ -1316,7 +1635,9 @@ 391 | if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size, 392 | src_open_sb.st_size, 393 | make_holes ? x->sparse_mode : SPARSE_NEVER, 394 | - src_name, dst_name, &normal_copy_required)) 395 | + x->move_mode, src_name, dst_name, &normal_copy_required, 396 | + iCountDown, cProgressField, last_time, last_size, 397 | + iBarLength, src_open_sb)) 398 | goto preserve_metadata; 399 | 400 | if (! normal_copy_required) 401 | @@ -1328,11 +1649,14 @@ 402 | 403 | off_t n_read; 404 | bool wrote_hole_at_eof; 405 | + 406 | + struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 407 | + 408 | if (! sparse_copy (source_desc, dest_desc, buf, buf_size, 409 | make_holes ? hole_size : 0, 410 | - x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name, 411 | - UINTMAX_MAX, &n_read, 412 | - &wrote_hole_at_eof)) 413 | + x->sparse_mode == SPARSE_ALWAYS, x->move_mode, 414 | + src_name, dst_name, UINTMAX_MAX, &n_read, 415 | + &wrote_hole_at_eof, &s_progress)) 416 | { 417 | return_val = false; 418 | goto close_src_and_dst_desc; 419 | @@ -1343,6 +1667,12 @@ 420 | return_val = false; 421 | goto close_src_and_dst_desc; 422 | } 423 | + if (progress) { 424 | + int i; 425 | + for ( i = 0; i < 6; i++ ) 426 | + free ( cProgressField[i] ); 427 | + free ( cProgressField ); 428 | + } 429 | } 430 | 431 | preserve_metadata: 432 | @@ -1716,15 +2046,15 @@ 433 | fprintf (stderr, 434 | (x->move_mode || x->unlink_dest_before_opening 435 | || x->unlink_dest_after_failed_open) 436 | - ? _("%s: replace %s, overriding mode %04lo (%s)? ") 437 | - : _("%s: unwritable %s (mode %04lo, %s); try anyway? "), 438 | + ? _("\n\n%s: replace %s, overriding mode %04lo (%s)? ") 439 | + : _("\n\n%s: unwritable %s (mode %04lo, %s); try anyway? "), 440 | program_name, quoteaf (dst_name), 441 | (unsigned long int) (dst_sb->st_mode & CHMOD_MODE_BITS), 442 | &perms[1]); 443 | } 444 | else 445 | { 446 | - fprintf (stderr, _("%s: overwrite %s? "), 447 | + fprintf (stderr, _("\n\n%s: overwrite %s? "), 448 | program_name, quoteaf (dst_name)); 449 | } 450 | 451 | diff -aur coreutils-8.32/src/copy.h coreutils-8.32-patched/src/copy.h 452 | --- coreutils-8.32/src/copy.h 2020-01-01 15:13:12.000000000 +0100 453 | +++ coreutils-8.32-patched/src/copy.h 2022-04-03 21:03:04.873415280 +0200 454 | @@ -234,6 +234,9 @@ 455 | Create destination directories as usual. */ 456 | bool symbolic_link; 457 | 458 | + /* If true, draw a nice progress bar on screen */ 459 | + bool progress_bar; 460 | + 461 | /* If true, do not copy a nondirectory that has an existing destination 462 | with the same or newer modification time. */ 463 | bool update; 464 | @@ -304,4 +307,22 @@ 465 | bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE; 466 | mode_t cached_umask (void); 467 | 468 | +/* BEGIN progress mod */ 469 | +FILE * spawn( const char *cmd, char *const argv[] ); 470 | +void format_time ( char * _cDst, double seconds, bool showall ); 471 | + 472 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ); 473 | + 474 | +__attribute__((__common__)) long g_iTotalSize; 475 | +__attribute__((__common__)) long g_iFTotalSize; 476 | +__attribute__((__common__)) long g_iTotalWritten; 477 | +__attribute__((__common__)) long g_iFTotalWritten; 478 | +__attribute__((__common__)) int g_iFilesCopied; 479 | +__attribute__((__common__)) int g_iDirectoriesCopied; 480 | +__attribute__((__common__)) struct timeval g_oStartTime; 481 | +__attribute__((__common__)) struct timeval g_oFStartTime; 482 | +__attribute__((__common__)) int g_iTotalFiles; 483 | +__attribute__((__common__)) bool progress; 484 | +/* END progress mod */ 485 | + 486 | #endif 487 | diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c 488 | --- coreutils-8.32/src/cp.c 2020-01-01 15:13:12.000000000 +0100 489 | +++ coreutils-8.32-patched/src/cp.c 2022-04-03 21:03:04.873415280 +0200 490 | @@ -131,6 +131,7 @@ 491 | {"symbolic-link", no_argument, NULL, 's'}, 492 | {"target-directory", required_argument, NULL, 't'}, 493 | {"update", no_argument, NULL, 'u'}, 494 | + {"progress-bar", no_argument, NULL, 'g'}, 495 | {"verbose", no_argument, NULL, 'v'}, 496 | {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, 497 | {GETOPT_HELP_OPTION_DECL}, 498 | @@ -170,6 +171,9 @@ 499 | -f, --force if an existing destination file cannot be\n\ 500 | opened, remove it and try again (this option\n\ 501 | is ignored when the -n option is also used)\n\ 502 | + -g, --progress-bar add a progress bar.\n\ 503 | + Note that this doesn't work with reflink,\n\ 504 | + reflink will be automatically disabled\n\ 505 | -i, --interactive prompt before overwrite (overrides a previous -n\ 506 | \n\ 507 | option)\n\ 508 | @@ -635,6 +639,84 @@ 509 | die (EXIT_FAILURE, 0, _("target %s is not a directory"), 510 | quoteaf (file[n_files - 1])); 511 | } 512 | + /* BEGIN progress mod */ 513 | + struct timeval start_time; 514 | + if (progress) { 515 | + if (g_iTotalSize == 0) 516 | + g_iTotalSize = 0; 517 | + if (g_iTotalFiles == 0) 518 | + g_iTotalFiles = n_files; 519 | + if (g_iFilesCopied == 0) 520 | + g_iFilesCopied = 0; 521 | + if (g_iDirectoriesCopied == 0) 522 | + g_iDirectoriesCopied = 0; 523 | + if (g_iTotalWritten == 0) 524 | + g_iTotalWritten = 0; 525 | + 526 | + if (target_directory_operand (file[0], &sb, &new_dst, forcing)) 527 | + g_iDirectoriesCopied++; 528 | + 529 | + /* save time */ 530 | + gettimeofday ( & start_time, NULL ); 531 | + g_oStartTime = start_time; 532 | + 533 | + printf ( "calculating total size... \r" ); 534 | + fflush ( stdout ); 535 | + long iTotalSize = 0; 536 | + int iFiles = n_files; 537 | + if ( ! target_directory ) 538 | + iFiles = n_files - 1; 539 | + int j; 540 | + 541 | + /* how many files are we copying */ 542 | + FILE *fp ; 543 | + char output[1024]; 544 | + char fcmd[] = "find"; 545 | + fp = spawn(fcmd, (char *[]){ fcmd, file[0], "-type", "f", NULL }); 546 | + if ( fp == NULL) 547 | + printf("failed to run find\r"); 548 | + else 549 | + { 550 | + char *line_buf = NULL; 551 | + size_t line_buf_size = 0; 552 | + int line_count = 0; 553 | + ssize_t line_size; 554 | + line_size = getline(&line_buf, &line_buf_size, fp); 555 | + while (line_size > 0) 556 | + { 557 | + line_count++; 558 | + line_size = getline(&line_buf, &line_buf_size, fp); 559 | + } 560 | + free (line_buf); 561 | + if ( line_count > n_files ) 562 | + g_iTotalFiles = line_count; 563 | + } 564 | + 565 | + for (j = 0; j < iFiles; j++) 566 | + { 567 | + /* call du -s for each file */ 568 | + char dcmd[] = "du"; 569 | + fp = spawn(dcmd, (char *[]){ dcmd, "-s", file[j], NULL }); 570 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 571 | + printf("failed to run du\r" ); 572 | + } 573 | + else 574 | + { 575 | + /* isolate size */ 576 | + strchr ( output, '\t' )[0] = '\0'; 577 | + iTotalSize += atol ( output ); 578 | + 579 | + printf ( "calculating total size... %ld\r", iTotalSize ); 580 | + fflush ( stdout ); 581 | + } 582 | + 583 | + /* close */ 584 | + pclose(fp); 585 | + } 586 | + g_iTotalSize += iTotalSize; 587 | + } 588 | + /* END progress mod */ 589 | + 590 | 591 | if (target_directory) 592 | { 593 | @@ -777,6 +859,56 @@ 594 | ok = copy (source, new_dest, 0, x, &unused, NULL); 595 | } 596 | 597 | + /* BEGIN progress mod */ 598 | + if (progress) { 599 | + /* remove everything */ 600 | + int i; 601 | + if ( g_iTotalFiles > 1 ) 602 | + { 603 | + for ( i = 0; i < 6; i++ ) 604 | + printf ( "\033[K\n" ); 605 | + printf ( "\r\033[6A" ); 606 | + } 607 | + else 608 | + { 609 | + for ( i = 0; i < 3; i++ ) 610 | + printf ( "\033[K\n" ); 611 | + printf ( "\r\033[3A" ); 612 | + } 613 | + 614 | + /* save time */ 615 | + struct timeval end_time; 616 | + gettimeofday ( & end_time, NULL ); 617 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 618 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 619 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 620 | + 621 | + /* get total size */ 622 | + char sTotalWritten[20]; 623 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 624 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 625 | + 626 | + /* calculate speed */ 627 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 628 | + char s_copy_speed[20]; 629 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 630 | + 631 | + /* good-bye message */ 632 | + char sFType[20]; 633 | + if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied ) 634 | + sprintf ( sFType, "%s", "folder(s)" ); 635 | + else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied ) 636 | + sprintf ( sFType, "%s", "folder(s)/file(s)" ); 637 | + else 638 | + sprintf ( sFType, "%s", "file(s)" ); 639 | + 640 | + char f_time[20]; 641 | + format_time(f_time, sec_elapsed, false); 642 | + printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType, 643 | + sTotalWritten, f_time, s_copy_speed ); 644 | + } 645 | + /* END progress mod */ 646 | + 647 | return ok; 648 | } 649 | 650 | @@ -812,6 +944,7 @@ 651 | x->recursive = false; 652 | x->sparse_mode = SPARSE_AUTO; 653 | x->symbolic_link = false; 654 | + x->progress_bar = false; 655 | x->set_mode = false; 656 | x->mode = 0; 657 | 658 | @@ -950,7 +1083,7 @@ 659 | selinux_enabled = (0 < is_selinux_enabled ()); 660 | cp_option_init (&x); 661 | 662 | - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", 663 | + while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ", 664 | long_opts, NULL)) 665 | != -1) 666 | { 667 | @@ -1007,6 +1140,10 @@ 668 | x.unlink_dest_after_failed_open = true; 669 | break; 670 | 671 | + case 'g': 672 | + progress = true; 673 | + break; 674 | + 675 | case 'H': 676 | x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; 677 | break; 678 | diff -aur coreutils-8.32/src/mv.c coreutils-8.32-patched/src/mv.c 679 | --- coreutils-8.32/src/mv.c 2020-01-01 15:13:12.000000000 +0100 680 | +++ coreutils-8.32-patched/src/mv.c 2022-04-03 21:03:04.873415280 +0200 681 | @@ -66,6 +66,7 @@ 682 | {"target-directory", required_argument, NULL, 't'}, 683 | {"update", no_argument, NULL, 'u'}, 684 | {"verbose", no_argument, NULL, 'v'}, 685 | + {"progress-bar", no_argument, NULL, 'g'}, 686 | {GETOPT_HELP_OPTION_DECL}, 687 | {GETOPT_VERSION_OPTION_DECL}, 688 | {NULL, 0, NULL, 0} 689 | @@ -170,8 +171,130 @@ 690 | { 691 | bool copy_into_self; 692 | bool rename_succeeded; 693 | + 694 | + /* BEGIN progress mod */ 695 | + struct timeval start_time; 696 | + 697 | + if(progress && x->rename_errno != 0) { 698 | + if (g_iTotalSize == 0) 699 | + g_iTotalSize = 0; 700 | + if (g_iTotalFiles == 0) 701 | + g_iTotalFiles = 0; 702 | + if (g_iFilesCopied == 0) 703 | + g_iFilesCopied = 0; 704 | + if (g_iDirectoriesCopied == 0) 705 | + g_iDirectoriesCopied = 0; 706 | + if (g_iTotalWritten == 0) 707 | + g_iTotalWritten = 0; 708 | + 709 | + if (target_directory_operand (source)) 710 | + g_iDirectoriesCopied++; 711 | + 712 | + gettimeofday (& start_time, NULL); 713 | + g_oStartTime = start_time; 714 | + 715 | + /* how many files are we copying */ 716 | + FILE *fp ; 717 | + char output[1024]; 718 | + char fcmd[] = "find"; 719 | + fp = spawn(fcmd, (char *[]){ fcmd, (char *)source, "-type", "f", NULL }); 720 | + if ( fp == NULL) 721 | + printf("failed to run find\r"); 722 | + else 723 | + { 724 | + char *line_buf = NULL; 725 | + size_t line_buf_size = 0; 726 | + int line_count = 0; 727 | + ssize_t line_size; 728 | + line_size = getline(&line_buf, &line_buf_size, fp); 729 | + while (line_size > 0) 730 | + { 731 | + line_count++; 732 | + line_size = getline(&line_buf, &line_buf_size, fp); 733 | + } 734 | + free (line_buf); 735 | + g_iTotalFiles = line_count; 736 | + } 737 | + /* close */ 738 | + pclose(fp); 739 | + 740 | + printf ("calculating total size... \r"); 741 | + fflush (stdout); 742 | + long iTotalSize = 0; 743 | + /* call du -s for each file */ 744 | + char dcmd[] = "du"; 745 | + fp = spawn(dcmd, (char *[]){ dcmd, "-s", (unsigned char *)(size_t)source, NULL }); 746 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 747 | + printf("failed to run du\r" ); 748 | + } 749 | + else 750 | + { 751 | + /* isolate size */ 752 | + strchr ( output, '\t' )[0] = '\0'; 753 | + iTotalSize += atol ( output ); 754 | + printf ( "calculating total size... %ld\r", iTotalSize ); 755 | + fflush ( stdout ); 756 | + } 757 | + 758 | + /* close */ 759 | + pclose(fp); 760 | + g_iTotalSize += iTotalSize; 761 | + } 762 | + /* END progress mod */ 763 | + 764 | bool ok = copy (source, dest, false, x, ©_into_self, &rename_succeeded); 765 | 766 | + /* BEGIN progress mod */ 767 | + if (progress && (x->rename_errno != 0 && ok)) { 768 | + /* remove everything */ 769 | + int i; 770 | + int limit = (g_iTotalFiles > 1 ? 6 : 3); 771 | + if (!rename_succeeded) 772 | + { 773 | + for ( i = 0; i < limit; i++ ) 774 | + printf ( "\033[K\n" ); 775 | + printf ( "\r\033[3A" ); 776 | + } 777 | + 778 | + /* save time */ 779 | + struct timeval end_time; 780 | + gettimeofday ( & end_time, NULL ); 781 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 782 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 783 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 784 | + 785 | + /* get total size */ 786 | + char sTotalWritten[20]; 787 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 788 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 789 | + 790 | + /* calculate speed */ 791 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 792 | + char s_copy_speed[20]; 793 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 794 | + 795 | + /* increase counter */ 796 | + g_iFilesCopied++; 797 | + 798 | + /* good-bye message */ 799 | + if ( x->last_file ) 800 | + { 801 | + char sFType[20]; 802 | + if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied ) 803 | + sprintf ( sFType, "%s", "folder(s)" ); 804 | + else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied ) 805 | + sprintf ( sFType, "%s", "folder(s)/file(s)" ); 806 | + else 807 | + sprintf ( sFType, "%s", "file(s)" ); 808 | + 809 | + char f_time[20]; 810 | + format_time(f_time, sec_elapsed, false); 811 | + printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType, 812 | + sTotalWritten, f_time, s_copy_speed ); 813 | + } 814 | + } 815 | + /* END progress mod */ 816 | + 817 | if (ok) 818 | { 819 | char const *dir_to_remove; 820 | @@ -306,6 +429,7 @@ 821 | \n\ 822 | -b like --backup but does not accept an argument\n\ 823 | -f, --force do not prompt before overwriting\n\ 824 | + -g, --progress-bar add progress-bar\n\ 825 | -i, --interactive prompt before overwrite\n\ 826 | -n, --no-clobber do not overwrite an existing file\n\ 827 | If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ 828 | @@ -361,7 +485,7 @@ 829 | /* Try to disable the ability to unlink a directory. */ 830 | priv_set_remove_linkdir (); 831 | 832 | - while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL)) 833 | + while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL)) 834 | != -1) 835 | { 836 | switch (c) 837 | @@ -407,6 +531,9 @@ 838 | case 'v': 839 | x.verbose = true; 840 | break; 841 | + case 'g': 842 | + progress = true; 843 | + break; 844 | case 'S': 845 | make_backups = true; 846 | backup_suffix = optarg; 847 | -------------------------------------------------------------------------------- /advcpmv-0.9-9.5.patch: -------------------------------------------------------------------------------- 1 | --- coreutils-9.5/src/copy.c 2023-04-17 16:43:34.000000000 -0400 2 | +++ coreutils-9.5/src/copy.c 2023-07-02 16:25:44.187962600 -0400 3 | @@ -121,6 +121,133 @@ 4 | dev_t dev; 5 | }; 6 | 7 | +/* BEGIN progress mod */ 8 | +struct progress_status { 9 | + int iCountDown; 10 | + char ** cProgressField; 11 | + struct timeval last_time; 12 | + int last_size, iBarLength; 13 | + struct stat src_open_sb; 14 | +}; 15 | + 16 | +FILE * spawn( const char *cmd, char *const argv[] ) 17 | +{ 18 | + FILE *ret = NULL; 19 | + int pfd_read[2]; 20 | + pid_t pid; 21 | + 22 | + if (cmd == NULL || argv == NULL) 23 | + return ret; 24 | + 25 | + if (pipe(pfd_read) < 0) { 26 | + error(0, errno, "pipe: %s", cmd); 27 | + return ret; 28 | + } 29 | + 30 | + if ((pid = fork()) == 0) { 31 | + int err = dup2(pfd_read[1], 1) < 0; 32 | + close(pfd_read[0]); 33 | + close(pfd_read[1]); 34 | + 35 | + if (err) 36 | + error(EXIT_FAILURE, errno, "dup2: %s", cmd); 37 | + execvp(cmd, argv); 38 | + error(EXIT_FAILURE, errno, "exec: %s", cmd); 39 | + } 40 | + 41 | + close(pfd_read[1]); 42 | + 43 | + if (pid < 0) { 44 | + close(pfd_read[0]); 45 | + error(0, errno, "fork: %s", cmd); 46 | + return ret; 47 | + } 48 | + 49 | + ret = fdopen(pfd_read[0], "r"); 50 | + return ret; 51 | +} 52 | + 53 | +void format_time ( char * _cDest, double seconds, bool showall ) 54 | +{ 55 | + // hours 56 | + int hr = ( (int) seconds / (60 * 60)) % 24; 57 | + // minutes 58 | + int min = ( (int) seconds / 60) % 60; 59 | + // seconds 60 | + double sec = seconds - (hr * (60 * 60)) - (min * 60); 61 | + if ( showall ) 62 | + { 63 | + if ( seconds < 0 ) 64 | + sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?'); 65 | + else 66 | + sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec); 67 | + } else if ( seconds >= 3600 ) 68 | + { 69 | + sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec); 70 | + } else if ( seconds >= 60 ) 71 | + { 72 | + sprintf(_cDest, "%2dm %4.1fs", min, sec); 73 | + } else 74 | + { 75 | + sprintf(_cDest, "%4.1fs", sec); 76 | + } 77 | +} 78 | + 79 | +static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal ) 80 | +{ 81 | + double dPercent = (double) _lProgress / (double) _lTotal * 100.f; 82 | + sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent ); 83 | + _cDest[_iBarLength - 2] = ' '; 84 | + 85 | + int i; 86 | + for ( i=1; i<=_iBarLength - 9; i++) 87 | + { 88 | + if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f ) 89 | + { 90 | + _cDest[i] = '='; 91 | + } 92 | + else 93 | + { 94 | + _cDest[i] = ' '; 95 | + } 96 | + } 97 | + for ( i=1; i<_iBarLength - 9; i++) 98 | + { 99 | + if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) ) 100 | + _cDest[i] = '>' ; 101 | + } 102 | +} 103 | + 104 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ) 105 | +{ 106 | + int iCounter = _iCounter; 107 | + double dSize = ( double ) _lSize; 108 | + while ( dSize >= 1000. ) 109 | + { 110 | + dSize /= 1024.; 111 | + iCounter++; 112 | + } 113 | + 114 | + /* get unit */ 115 | + char * sUnit; 116 | + if ( iCounter == 0 ) 117 | + sUnit = "B"; 118 | + else if ( iCounter == 1 ) 119 | + sUnit = "KiB"; 120 | + else if ( iCounter == 2 ) 121 | + sUnit = "MiB"; 122 | + else if ( iCounter == 3 ) 123 | + sUnit = "GiB"; 124 | + else if ( iCounter == 4 ) 125 | + sUnit = "TiB"; 126 | + else 127 | + sUnit = "N/A"; 128 | + 129 | + /* write number */ 130 | + return sprintf ( _cDst, "%5.1f %s", dSize, sUnit ); 131 | +} 132 | +/* END progress mod */ 133 | + 134 | /* Initial size of the cp.dest_info hash table. */ 135 | #define DEST_INFO_INITIAL_CAPACITY 61 136 | 137 | @@ -317,14 +444,23 @@ 138 | bytes read. */ 139 | static bool 140 | sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size, 141 | - size_t hole_size, bool punch_holes, bool allow_reflink, 142 | + size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode, 143 | char const *src_name, char const *dst_name, 144 | uintmax_t max_n_read, off_t *total_n_read, 145 | - bool *last_write_made_hole) 146 | + bool *last_write_made_hole, 147 | + struct progress_status *s_progress) 148 | { 149 | *last_write_made_hole = false; 150 | *total_n_read = 0; 151 | 152 | + /* BEGIN progress mod */ 153 | + gettimeofday ( & g_oFStartTime, NULL ); 154 | + g_iFTotalWritten = 0; 155 | + struct stat st; 156 | + stat(src_name, &st); 157 | + g_iFTotalSize = st.st_size/1024; 158 | + /* END progress mod */ 159 | + 160 | if (copy_debug.sparse_detection == COPY_DEBUG_UNKNOWN) 161 | copy_debug.sparse_detection = hole_size ? COPY_DEBUG_YES : COPY_DEBUG_NO; 162 | else if (hole_size && copy_debug.sparse_detection == COPY_DEBUG_EXTERNAL) 163 | @@ -339,6 +475,102 @@ 164 | (SSIZE_MAX, SIZE_MAX) truncated to a value that is 165 | surely aligned well. */ 166 | ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30; 167 | + 168 | + /* BEGIN progress mod */ 169 | + if (progress) { 170 | + /* update countdown */ 171 | + s_progress->iCountDown--; 172 | + char * sProgressBar = s_progress->cProgressField[5]; 173 | + if ( s_progress->iCountDown < 0 ) 174 | + s_progress->iCountDown = 100; 175 | + 176 | + /* just print one line with the percentage, but not always */ 177 | + if ( s_progress->iCountDown == 0 ) 178 | + { 179 | + /* calculate current speed */ 180 | + struct timeval cur_time; 181 | + gettimeofday ( & cur_time, NULL ); 182 | + int cur_size = g_iTotalWritten + *total_n_read / 1024; 183 | + int cur_fsize = g_iFTotalWritten + *total_n_read / 1024; 184 | + int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec; 185 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 186 | + sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec ); 187 | + int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size ) 188 | + / sec_elapsed ); 189 | + char s_copy_speed[20]; 190 | + file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 ); 191 | + /* update vars */ 192 | + s_progress->last_time = cur_time; 193 | + s_progress->last_size = cur_size; 194 | + 195 | + /* how many time has passed since the start? */ 196 | + int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec; 197 | + int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec; 198 | + int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size 199 | + * g_iTotalSize ) - isec_elapsed; 200 | + int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize 201 | + * g_iFTotalSize ) - isec_felapsed; 202 | + /* print out */ 203 | + 204 | + char f_ttime[20]; 205 | + char f_ftime[20]; 206 | + format_time(f_ttime, sec_remaining, true); 207 | + format_time(f_ftime, sec_fremaining, true); 208 | + 209 | + int fs_len; 210 | + fs_len = sprintf ( s_progress->cProgressField[1], 211 | + move_mode 212 | + ? "%d of %d files moved (about %s remaining)" 213 | + : "%d of %d files copied (about %s remaining)", 214 | + g_iFilesCopied, g_iTotalFiles, f_ttime ); 215 | + s_progress->cProgressField[1][fs_len] = ' '; 216 | + 217 | + char s_ftime[40] = ""; 218 | + 219 | + if (g_iTotalFiles > 1) 220 | + sprintf ( s_ftime, "(about %s remaining)", f_ftime ); 221 | + else 222 | + sprintf ( s_ftime, "(about %s remaining)", f_ttime ); 223 | + 224 | + if ( g_iTotalFiles > 1 ) 225 | + { 226 | + /* global progress bar */ 227 | + file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength, 228 | + g_iTotalWritten + *total_n_read / 1024, g_iTotalSize ); 229 | + 230 | + /* print the global status */ 231 | + fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21, 232 | + g_iTotalWritten + *total_n_read / 1024, 1 ); 233 | + s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' '; 234 | + } 235 | + 236 | + /* set current progress bar to be always 100 % */ 237 | + file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size ); 238 | + s_progress->cProgressField[5][s_progress->iBarLength-2] = '0'; 239 | + s_progress->cProgressField[5][s_progress->iBarLength-1] = '%'; 240 | + s_progress->cProgressField[5][s_progress->iBarLength] = '\0'; 241 | + 242 | + /* print the status as always full filesize*/ 243 | + fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 ); 244 | + s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' '; 245 | + 246 | + /* print the field */ 247 | + int it; 248 | + for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ ) 249 | + { 250 | + printf ( "\033[K%s\n", s_progress->cProgressField[it] ); 251 | + if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength ) 252 | + printf ( "%s", "" ); 253 | + } 254 | + if ( g_iTotalFiles > 1 ) 255 | + printf ( "\r\033[6A" ); 256 | + else 257 | + printf ( "\r\033[3A" ); 258 | + fflush ( stdout ); 259 | + } 260 | + } 261 | + /* END progress mod */ 262 | + 263 | ssize_t n_copied = copy_file_range (src_fd, nullptr, dest_fd, nullptr, 264 | MIN (max_n_read, copy_max), 0); 265 | if (n_copied == 0) 266 | @@ -382,6 +614,14 @@ 267 | copy_debug.offload = COPY_DEBUG_YES; 268 | max_n_read -= n_copied; 269 | *total_n_read += n_copied; 270 | + 271 | + /* BEGIN progress mod */ 272 | + if (progress) { 273 | + /* update total size */ 274 | + g_iTotalWritten += *total_n_read / 1024; 275 | + g_iFilesCopied++; 276 | + } 277 | + /* END progress mod */ 278 | } 279 | else 280 | copy_debug.offload = COPY_DEBUG_AVOIDED; 281 | @@ -392,6 +632,103 @@ 282 | 283 | while (max_n_read) 284 | { 285 | + 286 | + /* BEGIN progress mod */ 287 | + if (progress) { 288 | + /* update countdown */ 289 | + s_progress->iCountDown--; 290 | + char * sProgressBar = s_progress->cProgressField[5]; 291 | + if ( s_progress->iCountDown < 0 ) 292 | + s_progress->iCountDown = 100; 293 | + 294 | + /* just print one line with the percentage, but not always */ 295 | + if ( s_progress->iCountDown == 0 ) 296 | + { 297 | + /* calculate current speed */ 298 | + struct timeval cur_time; 299 | + gettimeofday ( & cur_time, NULL ); 300 | + int cur_size = g_iTotalWritten + *total_n_read / 1024; 301 | + int cur_fsize = g_iFTotalWritten + *total_n_read / 1024; 302 | + int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec; 303 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 304 | + sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec ); 305 | + int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size ) 306 | + / sec_elapsed ); 307 | + char s_copy_speed[20]; 308 | + file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 ); 309 | + /* update vars */ 310 | + s_progress->last_time = cur_time; 311 | + s_progress->last_size = cur_size; 312 | + 313 | + /* how many time has passed since the start? */ 314 | + int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec; 315 | + int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec; 316 | + int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size 317 | + * g_iTotalSize ) - isec_elapsed; 318 | + int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize 319 | + * g_iFTotalSize ) - isec_felapsed; 320 | + /* print out */ 321 | + 322 | + char f_ttime[20]; 323 | + char f_ftime[20]; 324 | + format_time(f_ttime, sec_remaining, true); 325 | + format_time(f_ftime, sec_fremaining, true); 326 | + 327 | + int fs_len; 328 | + fs_len = sprintf ( s_progress->cProgressField[1], 329 | + move_mode 330 | + ? "%d of %d files moved (about %s remaining)" 331 | + : "%d of %d files copied (about %s remaining)", 332 | + g_iFilesCopied, g_iTotalFiles, f_ttime ); 333 | + s_progress->cProgressField[1][fs_len] = ' '; 334 | + 335 | + char s_ftime[40] = ""; 336 | + 337 | + if (g_iTotalFiles > 1) 338 | + sprintf ( s_ftime, "(about %s remaining)", f_ftime ); 339 | + else 340 | + sprintf ( s_ftime, "(about %s remaining)", f_ttime ); 341 | + 342 | + sprintf ( s_progress->cProgressField[3], 343 | + move_mode 344 | + ? "moving at %s/s %s" 345 | + : "copying at %s/s %s", s_copy_speed, s_ftime ); 346 | + if ( g_iTotalFiles > 1 ) 347 | + { 348 | + /* global progress bar */ 349 | + file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength, 350 | + g_iTotalWritten + *total_n_read / 1024, g_iTotalSize ); 351 | + 352 | + /* print the global status */ 353 | + fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21, 354 | + g_iTotalWritten + *total_n_read / 1024, 1 ); 355 | + s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' '; 356 | + } 357 | + 358 | + /* current progress bar */ 359 | + file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size ); 360 | + 361 | + /* print the status */ 362 | + fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 ); 363 | + s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' '; 364 | + 365 | + /* print the field */ 366 | + int it; 367 | + for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ ) 368 | + { 369 | + printf ( "\033[K%s\n", s_progress->cProgressField[it] ); 370 | + if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength ) 371 | + printf ( "%s", "" ); 372 | + } 373 | + if ( g_iTotalFiles > 1 ) 374 | + printf ( "\r\033[6A" ); 375 | + else 376 | + printf ( "\r\033[3A" ); 377 | + fflush ( stdout ); 378 | + } 379 | + /* END progress mod */ 380 | + } 381 | + 382 | if (!*abuf) 383 | *abuf = xalignalloc (getpagesize (), buf_size); 384 | char *buf = *abuf; 385 | @@ -479,6 +816,14 @@ 386 | certain files in /proc or /sys with linux kernels. */ 387 | } 388 | 389 | + /* BEGIN progress mod */ 390 | + if (progress) { 391 | + /* update total size */ 392 | + g_iTotalWritten += *total_n_read / 1024; 393 | + g_iFilesCopied++; 394 | + } 395 | + /* END progress mod */ 396 | + 397 | /* Ensure a trailing hole is created, so that subsequent 398 | calls of sparse_copy() start at the correct offset. */ 399 | if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize)) 400 | @@ -550,9 +895,11 @@ 401 | static bool 402 | lseek_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size, 403 | size_t hole_size, off_t ext_start, off_t src_total_size, 404 | - enum Sparse_type sparse_mode, 405 | + enum Sparse_type sparse_mode, bool move_mode, 406 | bool allow_reflink, 407 | - char const *src_name, char const *dst_name) 408 | + char const *src_name, char const *dst_name, 409 | + int iCountDown, char ** cProgressField, struct timeval last_time, 410 | + int last_size, int iBarLength, struct stat src_open_sb) 411 | { 412 | off_t last_ext_start = 0; 413 | off_t last_ext_len = 0; 414 | @@ -626,10 +973,16 @@ 415 | is conservative and may miss some holes. */ 416 | off_t n_read; 417 | bool read_hole; 418 | + 419 | + struct timeval a; 420 | + struct stat b; 421 | + 422 | + struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 423 | + 424 | if ( ! sparse_copy (src_fd, dest_fd, abuf, buf_size, 425 | sparse_mode != SPARSE_ALWAYS ? 0 : hole_size, 426 | - true, allow_reflink, src_name, dst_name, 427 | - ext_len, &n_read, &read_hole)) 428 | + true, allow_reflink, move_mode, src_name, 429 | + dst_name, ext_len, &n_read, &read_hole, &s_progress)) 430 | return false; 431 | 432 | dest_pos = ext_start + n_read; 433 | @@ -1592,8 +1945,80 @@ 434 | buf_size = blcm; 435 | } 436 | 437 | + /* BEGIN progress mod */ 438 | + /* create a field of 6 lines */ 439 | + char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) ); 440 | + /* get console width */ 441 | + int iBarLength = 80; 442 | + struct winsize win; 443 | + if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 ) 444 | + if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */ 445 | + iBarLength = win.ws_col; 446 | + /* create rows */ 447 | + int it; 448 | + for ( it = 0; it < 6; it++ ) 449 | + { 450 | + cProgressField[it] = ( char * ) malloc ( iBarLength + 1 ); 451 | + /* init with spaces */ 452 | + int j; 453 | + for ( j = 0; j < iBarLength; j++ ) 454 | + cProgressField[it][j] = ' '; 455 | + cProgressField[it][iBarLength] = '\0'; 456 | + } 457 | + 458 | + /* global progress bar? */ 459 | + if ( g_iTotalFiles > 1 ) 460 | + { 461 | + /* init global progress bar */ 462 | + cProgressField[2][0] = '['; 463 | + cProgressField[2][iBarLength - 8] = ']'; 464 | + cProgressField[2][iBarLength - 7] = ' '; 465 | + cProgressField[2][iBarLength - 1] = '%'; 466 | + 467 | + /* total size */ 468 | + cProgressField[1][iBarLength - 11] = '/'; 469 | + file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 ); 470 | + 471 | + /* show how many files were written */ 472 | + int sum_length = 0; 473 | + sum_length = sprintf ( cProgressField[1], 474 | + x->move_mode 475 | + ? "%d of %d files moved so far" 476 | + : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles ); 477 | + cProgressField[1][sum_length] = ' '; 478 | + } 479 | + 480 | + /* truncate filename? */ 481 | + int fn_length; 482 | + if ( strlen ( src_name ) > iBarLength - 22 ) 483 | + fn_length = 484 | + sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) ); 485 | + else 486 | + fn_length = sprintf ( cProgressField[4], "%s", src_name ); 487 | + cProgressField[4][fn_length] = ' '; 488 | + 489 | + /* filesize */ 490 | + cProgressField[4][iBarLength - 11] = '/'; 491 | + file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 ); 492 | + 493 | + int iCountDown = 1; 494 | + char * sProgressBar = cProgressField[5]; 495 | + sProgressBar[0] = '['; 496 | + sProgressBar[iBarLength - 8] = ']'; 497 | + sProgressBar[iBarLength - 7] = ' '; 498 | + sProgressBar[iBarLength - 1] = '%'; 499 | + 500 | + /* this will always save the time in between */ 501 | + struct timeval last_time; 502 | + gettimeofday ( & last_time, NULL ); 503 | + int last_size = g_iTotalWritten; 504 | + /* END progress mod */ 505 | + 506 | off_t n_read; 507 | bool wrote_hole_at_eof = false; 508 | + 509 | + struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb}; 510 | + 511 | if (! ( 512 | #ifdef SEEK_HOLE 513 | scantype == LSEEK_SCANTYPE 514 | @@ -1601,15 +2026,17 @@ 515 | scan_inference.ext_start, src_open_sb.st_size, 516 | make_holes ? x->sparse_mode : SPARSE_NEVER, 517 | x->reflink_mode != REFLINK_NEVER, 518 | - src_name, dst_name) 519 | + x->move_mode, src_name, dst_name, 520 | + iCountDown, cProgressField, last_time, last_size, 521 | + iBarLength, src_open_sb) 522 | : 523 | #endif 524 | sparse_copy (source_desc, dest_desc, &buf, buf_size, 525 | make_holes ? hole_size : 0, 526 | x->sparse_mode == SPARSE_ALWAYS, 527 | x->reflink_mode != REFLINK_NEVER, 528 | - src_name, dst_name, UINTMAX_MAX, &n_read, 529 | - &wrote_hole_at_eof))) 530 | + x->move_mode, src_name, dst_name, UINTMAX_MAX, 531 | + &n_read, &wrote_hole_at_eof, &s_progress))) 532 | { 533 | return_val = false; 534 | goto close_src_and_dst_desc; 535 | @@ -1620,6 +2047,14 @@ 536 | return_val = false; 537 | goto close_src_and_dst_desc; 538 | } 539 | + /* BEGIN progress mod */ 540 | + if (progress) { 541 | + int i; 542 | + for ( i = 0; i < 6; i++ ) 543 | + free ( cProgressField[i] ); 544 | + free ( cProgressField ); 545 | + } 546 | + /* END progress mod */ 547 | } 548 | 549 | if (x->preserve_timestamps) 550 | --- coreutils-9.5/src/copy.h 2023-04-10 06:14:08.000000000 -0400 551 | +++ coreutils-9.5/src/copy.h 2023-07-02 16:15:50.875580500 -0400 552 | @@ -249,6 +249,9 @@ 553 | Create destination directories as usual. */ 554 | bool symbolic_link; 555 | 556 | + /* If true, draw a nice progress bar on screen */ 557 | + bool progress_bar; 558 | + 559 | /* If true, do not copy a nondirectory that has an existing destination 560 | with the same or newer modification time. */ 561 | bool update; 562 | @@ -329,4 +332,22 @@ 563 | _GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE; 564 | mode_t cached_umask (void); 565 | 566 | +/* BEGIN progress mod */ 567 | +FILE * spawn( const char *cmd, char *const argv[] ); 568 | +void format_time ( char * _cDst, double seconds, bool showall ); 569 | + 570 | +int file_size_format ( char * _cDst, long _lSize, int _iCounter ); 571 | + 572 | +__attribute__((__common__)) long g_iTotalSize; 573 | +__attribute__((__common__)) long g_iFTotalSize; 574 | +__attribute__((__common__)) long g_iTotalWritten; 575 | +__attribute__((__common__)) long g_iFTotalWritten; 576 | +__attribute__((__common__)) int g_iFilesCopied; 577 | +__attribute__((__common__)) int g_iDirectoriesCopied; 578 | +__attribute__((__common__)) struct timeval g_oStartTime; 579 | +__attribute__((__common__)) struct timeval g_oFStartTime; 580 | +__attribute__((__common__)) int g_iTotalFiles; 581 | +__attribute__((__common__)) bool progress; 582 | +/* END progress mod */ 583 | + 584 | #endif 585 | --- coreutils-9.5/src/cp.cg 2023-04-10 06:14:08.000000000 -0400 586 | +++ coreutils-9.5/src/cp.c 2023-07-02 16:26:48.895801600 -0400 587 | @@ -140,6 +140,7 @@ 588 | {"symbolic-link", no_argument, nullptr, 's'}, 589 | {"target-directory", required_argument, nullptr, 't'}, 590 | {"update", optional_argument, nullptr, 'u'}, 591 | + {"progress-bar", no_argument, nullptr, 'g'}, 592 | {"verbose", no_argument, nullptr, 'v'}, 593 | {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, 594 | {GETOPT_HELP_OPTION_DECL}, 595 | @@ -182,6 +183,9 @@ 596 | -f, --force if an existing destination file cannot be\n\ 597 | opened, remove it and try again (this option\n\ 598 | is ignored when the -n option is also used)\n\ 599 | + -g, --progress-bar add a progress bar.\n\ 600 | + Note that this doesn't work with reflink,\n\ 601 | + reflink will be automatically disabled\n\ 602 | -i, --interactive prompt before overwrite (overrides a previous -n\ 603 | \n\ 604 | option)\n\ 605 | @@ -666,6 +670,82 @@ 606 | } 607 | } 608 | 609 | + /* BEGIN progress mod */ 610 | + struct timeval start_time; 611 | + if (progress) { 612 | + if (g_iTotalSize == 0) 613 | + g_iTotalSize = 0; 614 | + if (g_iTotalFiles == 0) 615 | + g_iTotalFiles = n_files; 616 | + if (g_iFilesCopied == 0) 617 | + g_iFilesCopied = 0; 618 | + if (g_iDirectoriesCopied == 0) 619 | + g_iDirectoriesCopied = 0; 620 | + if (g_iTotalWritten == 0) 621 | + g_iTotalWritten = 0; 622 | + 623 | + if (target_directory_operand (file[0], &sb)) 624 | + g_iDirectoriesCopied++; 625 | + 626 | + /* save time */ 627 | + gettimeofday ( & start_time, NULL ); 628 | + g_oStartTime = start_time; 629 | + 630 | + printf ( "calculating total size... \r" ); 631 | + fflush ( stdout ); 632 | + long iTotalSize = 0; 633 | + int iFiles = n_files; 634 | + if ( ! target_directory ) 635 | + iFiles = n_files - 1; 636 | + int j; 637 | + 638 | + /* how many files are we copying */ 639 | + FILE *fp ; 640 | + char output[1024]; 641 | + fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL }); 642 | + if ( fp == NULL) 643 | + printf("failed to run find\r"); 644 | + else 645 | + { 646 | + char *line_buf = NULL; 647 | + size_t line_buf_size = 0; 648 | + int line_count = 0; 649 | + ssize_t line_size; 650 | + line_size = getline(&line_buf, &line_buf_size, fp); 651 | + while (line_size > 0) 652 | + { 653 | + line_count++; 654 | + line_size = getline(&line_buf, &line_buf_size, fp); 655 | + } 656 | + free (line_buf); 657 | + if ( line_count > n_files ) 658 | + g_iTotalFiles = line_count; 659 | + } 660 | + 661 | + for (j = 0; j < iFiles; j++) 662 | + { 663 | + /* call du -s for each file */ 664 | + fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL }); 665 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 666 | + printf("failed to run du\r" ); 667 | + } 668 | + else 669 | + { 670 | + /* isolate size */ 671 | + strchr ( output, '\t' )[0] = '\0'; 672 | + iTotalSize += atol ( output ); 673 | + 674 | + printf ( "calculating total size... %ld\r", iTotalSize ); 675 | + fflush ( stdout ); 676 | + } 677 | + 678 | + /* close */ 679 | + pclose(fp); 680 | + } 681 | + g_iTotalSize += iTotalSize; 682 | + } 683 | + /* END progress mod */ 684 | + 685 | if (target_directory) 686 | { 687 | /* cp file1...filen edir 688 | @@ -805,6 +885,56 @@ 689 | ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, NULL); 690 | } 691 | 692 | + /* BEGIN progress mod */ 693 | + if (progress) { 694 | + /* remove everything */ 695 | + int i; 696 | + if ( g_iTotalFiles > 1 ) 697 | + { 698 | + for ( i = 0; i < 6; i++ ) 699 | + printf ( "\033[K\n" ); 700 | + printf ( "\r\033[6A" ); 701 | + } 702 | + else 703 | + { 704 | + for ( i = 0; i < 3; i++ ) 705 | + printf ( "\033[K\n" ); 706 | + printf ( "\r\033[3A" ); 707 | + } 708 | + 709 | + /* save time */ 710 | + struct timeval end_time; 711 | + gettimeofday ( & end_time, NULL ); 712 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 713 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 714 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 715 | + 716 | + /* get total size */ 717 | + char sTotalWritten[20]; 718 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 719 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 720 | + 721 | + /* calculate speed */ 722 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 723 | + char s_copy_speed[20]; 724 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 725 | + 726 | + /* good-bye message */ 727 | + char sFType[20]; 728 | + if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied ) 729 | + sprintf ( sFType, "%s", "folder(s)" ); 730 | + else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied ) 731 | + sprintf ( sFType, "%s", "folder(s)/file(s)" ); 732 | + else 733 | + sprintf ( sFType, "%s", "file(s)" ); 734 | + 735 | + char f_time[20]; 736 | + format_time(f_time, sec_elapsed, false); 737 | + printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType, 738 | + sTotalWritten, f_time, s_copy_speed ); 739 | + } 740 | + /* END progress mod */ 741 | + 742 | return ok; 743 | } 744 | 745 | @@ -840,6 +970,7 @@ 746 | x->recursive = false; 747 | x->sparse_mode = SPARSE_AUTO; 748 | x->symbolic_link = false; 749 | + x->progress_bar = false; 750 | x->set_mode = false; 751 | x->mode = 0; 752 | 753 | @@ -978,7 +1109,8 @@ 754 | selinux_enabled = (0 < is_selinux_enabled ()); 755 | cp_option_init (&x); 756 | 757 | - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", 758 | + /* BEGIN and END progress mod - remove the g in the next line!*/ 759 | + while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ", 760 | long_opts, nullptr)) 761 | != -1) 762 | { 763 | @@ -1039,6 +1171,10 @@ 764 | x.unlink_dest_after_failed_open = true; 765 | break; 766 | 767 | + case 'g': 768 | + progress = true; 769 | + break; 770 | + 771 | case 'H': 772 | x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; 773 | break; 774 | @@ -1212,6 +1348,9 @@ 775 | usage (EXIT_FAILURE); 776 | } 777 | 778 | + if (progress) 779 | + x.reflink_mode = REFLINK_NEVER; 780 | + 781 | x.backup_type = (make_backups 782 | ? xget_version (_("backup type"), 783 | version_control_string) 784 | --- coreutils-9.5/src/mv.c 2023-04-10 06:14:08.000000000 -0400 785 | +++ coreutils-9.5/src/mv.c 2023-07-02 16:19:42.433714100 -0400 786 | @@ -79,6 +79,7 @@ 787 | {"target-directory", required_argument, nullptr, 't'}, 788 | {"update", optional_argument, nullptr, 'u'}, 789 | {"verbose", no_argument, nullptr, 'v'}, 790 | + {"progress-bar", no_argument, nullptr, 'g'}, 791 | {GETOPT_HELP_OPTION_DECL}, 792 | {GETOPT_VERSION_OPTION_DECL}, 793 | {NULL, 0, NULL, 0} 794 | @@ -170,9 +171,130 @@ 795 | { 796 | bool copy_into_self; 797 | bool rename_succeeded; 798 | + 799 | + /* BEGIN progress mod */ 800 | + struct timeval start_time; 801 | + struct stat sb; 802 | + 803 | + if (progress && x->rename_errno != 0) { 804 | + if (g_iTotalSize == 0) 805 | + g_iTotalSize = 0; 806 | + if (g_iTotalFiles == 0) 807 | + g_iTotalFiles = 0; 808 | + if (g_iFilesCopied == 0) 809 | + g_iFilesCopied = 0; 810 | + if (g_iDirectoriesCopied == 0) 811 | + g_iDirectoriesCopied = 0; 812 | + if (g_iTotalWritten == 0) 813 | + g_iTotalWritten = 0; 814 | + 815 | + if (target_directory_operand (source, &sb)) 816 | + g_iDirectoriesCopied++; 817 | + 818 | + gettimeofday (& start_time, NULL); 819 | + g_oStartTime = start_time; 820 | + 821 | + /* how many files are we copying */ 822 | + FILE *fp; 823 | + char output[1024]; 824 | + fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL }); 825 | + if ( fp == NULL) 826 | + printf("failed to run find\r"); 827 | + else 828 | + { 829 | + char *line_buf = NULL; 830 | + size_t line_buf_size = 0; 831 | + int line_count = 0; 832 | + ssize_t line_size; 833 | + line_size = getline(&line_buf, &line_buf_size, fp); 834 | + while (line_size > 0) 835 | + { 836 | + line_count++; 837 | + line_size = getline(&line_buf, &line_buf_size, fp); 838 | + } 839 | + free (line_buf); 840 | + g_iTotalFiles = line_count; 841 | + } 842 | + /* close */ 843 | + pclose(fp); 844 | + 845 | + printf ("calculating total size... \r"); 846 | + fflush (stdout); 847 | + long iTotalSize = 0; 848 | + /* call du -s for source */ 849 | + fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL }); 850 | + if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) { 851 | + printf("failed to run du\r" ); 852 | + } 853 | + else 854 | + { 855 | + /* isolate size */ 856 | + strchr ( output, '\t' )[0] = '\0'; 857 | + iTotalSize += atol ( output ); 858 | + printf ( "calculating total size... %ld\r", iTotalSize ); 859 | + fflush ( stdout ); 860 | + } 861 | + 862 | + /* close */ 863 | + pclose(fp); 864 | + g_iTotalSize += iTotalSize; 865 | + } 866 | + /* END progress mod */ 867 | + 868 | bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x, 869 | ©_into_self, &rename_succeeded); 870 | 871 | + /* BEGIN progress mod */ 872 | + if (progress && (x->rename_errno != 0 && ok)) { 873 | + /* remove everything */ 874 | + int i; 875 | + int limit = (g_iTotalFiles > 1 ? 6 : 3); 876 | + if (!rename_succeeded) 877 | + { 878 | + for ( i = 0; i < limit; i++ ) 879 | + printf ( "\033[K\n" ); 880 | + printf ( "\r\033[3A" ); 881 | + } 882 | + 883 | + /* save time */ 884 | + struct timeval end_time; 885 | + gettimeofday ( & end_time, NULL ); 886 | + int usec_elapsed = end_time.tv_usec - start_time.tv_usec; 887 | + double sec_elapsed = ( double ) usec_elapsed / 1000000.f; 888 | + sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec ); 889 | + 890 | + /* get total size */ 891 | + char sTotalWritten[20]; 892 | + file_size_format ( sTotalWritten, g_iTotalSize, 1 ); 893 | + /* TODO: using g_iTotalWritten would be more correct, but is less accurate */ 894 | + 895 | + /* calculate speed */ 896 | + int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed ); 897 | + char s_copy_speed[20]; 898 | + file_size_format ( s_copy_speed, copy_speed, 1 ); 899 | + 900 | + /* increase counter */ 901 | + g_iFilesCopied++; 902 | + 903 | + /* good-bye message */ 904 | + if ( x->last_file ) 905 | + { 906 | + char sFType[20]; 907 | + if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied ) 908 | + sprintf ( sFType, "%s", "folder(s)" ); 909 | + else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied ) 910 | + sprintf ( sFType, "%s", "folder(s)/file(s)" ); 911 | + else 912 | + sprintf ( sFType, "%s", "file(s)" ); 913 | + 914 | + char f_time[20]; 915 | + format_time(f_time, sec_elapsed, false); 916 | + printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType, 917 | + sTotalWritten, f_time, s_copy_speed ); 918 | + } 919 | + } 920 | + /* END progress mod */ 921 | + 922 | if (ok) 923 | { 924 | char const *dir_to_remove; 925 | @@ -275,6 +397,7 @@ 926 | "), stdout); 927 | fputs (_("\ 928 | -f, --force do not prompt before overwriting\n\ 929 | + -g, --progress-bar add progress-bar\n\ 930 | -i, --interactive prompt before overwrite\n\ 931 | -n, --no-clobber do not overwrite an existing file\n\ 932 | If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ 933 | @@ -337,7 +460,7 @@ 934 | /* Try to disable the ability to unlink a directory. */ 935 | priv_set_remove_linkdir (); 936 | 937 | - while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, nullptr)) 938 | + while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, nullptr)) 939 | != -1) 940 | { 941 | switch (c) 942 | @@ -350,6 +473,9 @@ 943 | case 'f': 944 | x.interactive = I_ALWAYS_YES; 945 | break; 946 | + case 'g': 947 | + progress = true; 948 | + break; 949 | case 'i': 950 | x.interactive = I_ASK_USER; 951 | break; 952 | --------------------------------------------------------------------------------