Compare commits

...

88 Commits

Author SHA1 Message Date
Arun Prakash Jana 1a574b81d8 Merge pull request #46 from upsetdog/master
Fix Fish shell alias command syntax in README
2026-07-27 20:56:31 +05:30
Arun Prakash Jana 14e3206e79 Merge pull request #49 from seanybaggins/add-9.11-patch
Add patch for coreutils 9.11
2026-07-27 20:55:57 +05:30
Sean Link 21556b579c docs: add progress-bar option to coreutils.texi references 2026-07-23 09:47:43 -06:00
Sean Link 8dc8419e5d feat: add patch for coreutils 9.11 2026-07-23 09:33:52 -06:00
Arun Prakash Jana 95b3ff2fbc Merge pull request #48 from nerun/patch_9.6-9.7
Create patches for versions 9.6 and 9.7 of coreutils
2026-01-17 22:38:02 +05:30
nerun 7ad6ab4d6c Create patches for versions 9.6 and 9.7 of coreutils. Update install script. 2026-01-17 13:45:18 -03:00
doggo 7312ed3039 Fix Fish shell alias command syntax in README
Previously, the single quotes would get eaten, when already running in a fish shell. Added double quotes to prevent this.
2025-11-29 00:14:18 +00:00
Arun Prakash Jana 1635eb96e5 Merge pull request #45 from zenoprax/master
switch install script from HTTP to HTTPS for tarball
2025-11-01 10:38:03 +05:30
Corey Newton 0df9ccf0f4 switch install script from HTTP to HTTPS for tarball
Rather than verifying signatures, switching to "https://ftp..."
removes any insecure connections entirely.
2025-10-27 07:01:14 -07:00
Arun Prakash Jana 3d6c670089 Merge pull request #43 from Milziade/add-alias
Fix #41
2025-05-12 21:03:58 +05:30
Milziade 1e039bdd64 Fix #41 2025-05-12 11:24:24 +02:00
Arun Prakash Jana 1e2b1c6b74 Update to patch v9.5 2024-04-05 21:50:39 +05:30
Arun ba7b157dd8 Merge pull request #40 from Tatsh/9.5-support
Create advcpmv-0.9-9.5.patch
2024-04-05 21:47:13 +05:30
Andrew Udvare 42cadf5b79 Create advcpmv-0.9-9.5.patch
Signed-off-by: Andrew Udvare <audvare@gmail.com>
2024-03-30 04:50:39 -04:00
Arun a1f8b505e6 Merge pull request #35 from Tatsh/9.4-support
Create advcpmv-0.9-9.4.patch
2023-09-10 09:33:56 +05:30
Andrew Udvare 2961dc3ec8 Create advcpmv-0.9-9.4.patch 2023-09-06 12:10:21 -04:00
Arun 4fe520c882 Merge pull request #33 from yellowhat/master
fix(9.3): file path
2023-07-16 17:41:56 +05:30
yellowhat 066ba086bf fix(9.3): file path 2023-07-16 11:16:45 +02:00
Zackptg5 69d2f9d3c7 Merge pull request #31 from Zackptg5/master
Create v9.3 patch
2023-07-02 16:35:54 -04:00
Zackptg5 0064105957 Create v9.3 patch 2023-07-02 16:35:17 -04:00
Zackptg5 2253f7a380 Merge pull request #30 from Zackptg5/master
v9.2 Patch
2023-04-18 11:16:09 -04:00
Zackptg5 d63f908a3a update install.sh 2023-04-18 11:04:45 -04:00
Zackptg5 35988dc8d7 Create advcpmv-0.9-9.2.patch 2023-04-13 00:33:12 -04:00
Arun 2845cfcc0d Merge pull request #27 from Pysis868/patch-1
Update README.md
2023-03-22 05:27:41 +05:30
Pysis 2eec987a8c Update README.md
Added mirrored fish commands.
2023-03-02 11:10:39 -05:00
Pysis b6ceba0130 Update README.md 2023-02-26 18:47:26 -05:00
Arun 376337eea1 Merge pull request #22 from jarun/9.1-test
v9.1
2022-06-11 21:37:28 +05:30
Zackptg5 ea268d870b Tweak line numbers 2022-06-04 10:31:00 -04:00
Zackptg5 195034f1cd update to 9.1 2022-06-04 00:31:34 -04:00
Arun 9121e8dc4f Merge pull request #18 from navneetankur/master
replace wget with curl in install.sh
2022-05-12 18:36:39 +05:30
Navneet Aman 32275808ba replace wget with curl in install.sh 2022-04-12 21:38:33 +05:30
Arun a047797e92 Merge pull request #17 from SlrG/fixmovebars
fix progressbars not showing when moving files and global progress
2022-04-04 02:10:47 +05:30
Michael Wehr dff0994e1f fix progressbars not showing when moving files and global progress 2022-04-03 21:05:19 +02:00
Arun 096b047523 Merge pull request #16 from Gelma/fix
Fix mistake in build script
2022-04-03 01:57:27 +05:30
Andrea Gelmini 74254ddf1c Fix mistake in build script
Signed-off-by: Andrea Gelmini <andrea.gelmini@gelma.net>
2022-03-28 22:07:38 +02:00
Arun Prakash Jana c878eab0f8 Use command strings 2022-03-27 07:00:06 +05:30
Arun f231f05455 Merge pull request #14 from SlrG/fixandimprovements
Fix and Improvements
2022-03-27 05:39:37 +05:30
Michael Wehr bb5b892ace revert some things for review purposes 2022-02-14 21:15:28 +01:00
Michael Wehr f854b119e9 backport changes to 8.32 patch 2022-02-14 21:11:16 +01:00
Michael Wehr dfb9623dcb fix NULL in middle of spawn and typecast 2022-02-14 21:07:00 +01:00
Michael Wehr 7fe882ec46 backport changes to 8.32 patch 2022-02-13 16:15:53 +01:00
Michael Wehr b62cfd957e fix elapsed message showing hours and minutes when zero 2022-02-13 16:13:00 +01:00
Michael Wehr 2532fc3ca3 switch to fork/exec/dup2 instead of popen
To get rid of the problems from having to escape certain special chars
when using popen to call the find and du system commands, on suggestion
of N-R-K this switches popen with a fork/exec/dup2 based approach.
See https://github.com/jarun/advcpmv/pull/14#issuecomment-1035880454 and
https://github.com/jarun/advcpmv/pull/14#issuecomment-1035892465.
2022-02-13 15:50:06 +01:00
Arun 46703ea4f5 Merge pull request #12 from abrahammurciano/master
create install script
2022-02-10 23:42:46 +05:30
Michael Wehr fb08e1aba6 backport changes to 8.32 patch 2022-01-12 20:11:02 +01:00
Michael Wehr 8a8af288bb fix output of ftime instead of ttime 2022-01-11 23:28:10 +01:00
Michael Wehr 30b6aff569 refactor format_time function and calls to fix memory leak 2022-01-11 22:11:46 +01:00
Michael Wehr 1e12a7c529 add remaining time for file 2022-01-11 20:54:12 +01:00
Michael Wehr 53a449f84c move total remaining time to global bar and fix format_time 2022-01-10 23:39:30 +01:00
Michael Wehr b79170e69b show total number of files to copy 2022-01-09 21:18:46 +01:00
Michael Wehr ae89bfdb24 use function to also calculate and format elapsed time 2022-01-09 21:15:26 +01:00
Michael Wehr d09e9fb8b6 backport changes to 8.32 patch 2022-01-04 22:00:35 +01:00
Michael Wehr d8013b9f35 add missed comments to identify all progress mod changes 2022-01-04 21:58:45 +01:00
Michael Wehr 2f468f6896 show elapsed time in good-bye message as xh xm xs instead seconds only 2022-01-04 21:11:43 +01:00
Abraham Murciano 60daf2a133 clarified how to choose version 2021-12-27 11:25:20 +02:00
Abraham Murciano a054d7d892 made installation command one line 2021-12-27 11:22:56 +02:00
Abraham Murciano 72b2b58117 simplified language 2021-12-27 11:12:02 +02:00
Abraham Murciano ac2cac8d4e removed bash dependancy 2021-12-27 11:09:08 +02:00
Arun 54f1b797db Merge pull request #13 from SlrG/master
fix compiler warning for 0.9 patch
2021-12-26 22:58:19 +05:30
Michael Wehr 801478c31a backport changes to 8.32 patch 2021-12-25 11:28:22 +01:00
Michael Wehr cf51b9bd8b switch back to using calloc instead of malloc
using malloc generated errors when memory leak checking with valgrind
because malloc creates uninitialized space
2021-12-25 11:16:03 +01:00
Michael Wehr c8149fab75 show global progress bar in some cases where not shown before
when using wildcards:
cp -r -g ./src/normal/* ./sinkhole/
when giving multiple source files:
cp -r -g ./src/normal/{testfile0,testfile2}
or
cp -g -t ../../sinkhole/ testfile0 testfile2
2021-12-25 11:05:23 +01:00
Michael Wehr 2fc918bd98 backport the grave accent fix to the 8.32 patch 2021-12-24 20:17:02 +01:00
Michael Wehr a620f86965 escape grave accents, they break the double quoted system commands 2021-12-24 20:11:32 +01:00
Michael Wehr 04bae585e6 backport changes to 8.32 patch 2021-12-23 19:35:38 +01:00
Michael Wehr 3ac56d553f show global progressbar when moving files in copy mode (between devices) 2021-12-23 19:21:33 +01:00
Michael Wehr e529e310ea fix casing of output, to better match unmodded messages 2021-12-23 18:55:23 +01:00
Michael Wehr a1cc785674 add more comments to identify all progress mod changes 2021-12-23 12:00:26 +01:00
Michael Wehr aa4d645a11 move comment to enclose progess struct 2021-12-23 09:07:53 +01:00
Michael Wehr 24ad5c292c when moving display one good-bye message and show what was moved 2021-12-22 23:43:26 +01:00
Michael Wehr edec49ec6f escape double quotes, so system commands don't break anymore 2021-12-22 21:16:20 +01:00
Michael Wehr 7e2df2e0b7 harmonize string handling, so only double quotes break system commands 2021-12-22 14:19:51 +01:00
Michael Wehr c9d7d6779f change comment positioning 2021-12-22 14:09:12 +01:00
Michael Wehr 3b2fdc6f42 fix comment casing 2021-12-22 13:57:59 +01:00
Michael Wehr d337f35f5b fix whitespace 2021-12-22 13:48:56 +01:00
Michael Wehr 9d614d1883 fix compiler warning for 0.9 patch 2021-12-22 11:57:51 +01:00
Abraham Murciano 4989cfaf78 set -e on install script
make the script exit if any command fails
2021-12-21 16:50:13 +02:00
Abraham Murciano 4e9c8bc2b4 documented installing other versions 2021-12-21 14:30:54 +00:00
Abraham Murciano 17957ce329 simplified directories 2021-12-21 14:15:48 +00:00
Abraham Murciano 4839af6e65 create install script 2021-12-21 13:47:10 +00:00
Arun Prakash Jana 47dbf85998 Add image, update text on reflink. 2021-11-14 18:42:11 +05:30
Arun 4144872297 Merge pull request #11 from whalehub/patch-1
Fix compiler warnings
2021-11-12 14:47:29 +05:30
Aaron 3ef18c8f20 Fix compiler warnings
Signed-off-by: Aaron <admin@datahoarder.dev>
2021-11-12 09:53:29 +01:00
Arun 35d117d60b Merge pull request #10 from Zackptg5/master
old bug fix, add readme note
2021-11-12 08:26:45 +05:30
Zackptg5 39c571c505 old bug fix, add readme note 2021-11-11 21:55:13 -05:00
Arun 8e1c0d4f8a Merge pull request #9 from Zackptg5/master
Add 9.0 patch
2021-11-10 13:10:54 +00:00
Zackptg5 fd0fc50b1a Add 9.0 patch
Doesn't work with reflink, disable if using  progress-bar
2021-11-09 12:42:26 -05:00
Arun e81fa04c0a Merge pull request #8 from jarun/revert-7-master
Revert "Add 9.0 patch"
2021-11-06 07:05:33 +05:30
12 changed files with 8998 additions and 127 deletions
+47 -16
View File
@@ -1,4 +1,6 @@
## Advanced Copy ##
![advcpmv](https://web.archive.org/web/20131217004029im_/http://beatex.org/web/advcopy/advcpmv-screen-20130313.png)
## Advanced Copy
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.
@@ -8,40 +10,69 @@ advcpmv-0.5-8.21.patch was the last patch released by the author (on February 14
## Build instructions
The latest GNU Core Utilities source can be found here: https://ftp.gnu.org/gnu/coreutils/
Requires the following dependencies:
- patch
- gcc
Run the following command to download, patch, compile coreutils and generate the files: `./advcpmv/advcp` and `./advcpmv/advmv`.
Bash:
```
wget http://ftp.gnu.org/gnu/coreutils/coreutils-8.32.tar.xz
tar xvJf coreutils-8.32.tar.xz
cd coreutils-8.32/
wget https://raw.githubusercontent.com/jarun/advcpmv/master/advcpmv-0.8-8.32.patch
patch -p1 -i advcpmv-0.8-8.32.patch
./configure
make
curl https://raw.githubusercontent.com/jarun/advcpmv/master/install.sh --create-dirs -o ./advcpmv/install.sh && (cd advcpmv && sh install.sh)
```
Fish:
```
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
```
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.
```
... sh install.sh 0.8 8.32 ...
```
## Usage
### Change your behaviour
You can install the binaries and use `cpg -g` and `mvg -g` instead of cp and mv
You can install the binaries and use `cpg -g` and `mvg -g` instead of cp and mv:
```
sudo mv ./src/cp /usr/local/bin/cpg
sudo mv ./src/mv /usr/local/bin/mvg
sudo mv ./advcpmv/advcp /usr/local/bin/cpg
sudo mv ./advcpmv/advmv /usr/local/bin/mvg
```
Progress bar does not work with reflink (introduced v9.0 onwards). So reflink is disabled if using progress bar, left unchanged otherwise.
### Alias
You can install the binaries and create aliases for bash (or whatever you use)
```
sudo mv ./src/cp /usr/local/bin/advcp
sudo mv ./src/mv /usr/local/bin/advmv
echo alias cp '/usr/local/bin/advcp -g' >> ~/.bashrc
echo alias mv '/usr/local/bin/advmv -g' >> ~/.bashrc
sudo mv ./advcpmv/advcp /usr/local/bin/
sudo mv ./advcpmv/advmv /usr/local/bin/
```
Bash:
```
echo -e 'alias cp=\x27/usr/local/bin/cpg -g\x27' >> ~/.bashrc
echo -e 'alias mv=\x27/usr/local/bin/mvg -g\x27' >> ~/.bashrc
```
Fish:
```
echo "alias cp '/usr/local/bin/advcp -g'" >> ~/.config/fish/config.fish
echo "alias mv '/usr/local/bin/advmv -g'" >> ~/.config/fish/config.fish
```
```
## Upstream merge
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.
```
+279 -111
View File
@@ -1,10 +1,11 @@
diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
--- coreutils-8.32/src/copy.c 2020-01-01 15:13:12.000000000 +0100
+++ coreutils-8.32-patched/src/copy.c 2021-08-11 19:48:19.008225844 +0200
@@ -129,6 +129,72 @@
+++ coreutils-8.32-patched/src/copy.c 2022-04-03 21:03:04.869415222 +0200
@@ -129,6 +129,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
@@ -13,7 +14,69 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
+ struct stat src_open_sb;
+};
+
+/* Begin progress Mod*/
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
@@ -68,13 +131,11 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
+
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -258,10 +324,11 @@
@@ -258,18 +385,125 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
@@ -88,13 +149,22 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
{
*last_write_made_hole = false;
*total_n_read = 0;
@@ -270,6 +337,85 @@
bool make_hole = false;
off_t psize = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* BEGIN progress mod */
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
@@ -108,6 +178,7 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
@@ -121,20 +192,38 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int min_remaining = sec_remaining / 60;
+ sec_remaining -= min_remaining * 60;
+ int hours_remaining = min_remaining / 60;
+ min_remaining -= hours_remaining * 60;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "Moving at %s/s (about %uh %um %us remaining)"
+ : "Copying at %s/s (about %uh %um %us remaining)", s_copy_speed,
+ hours_remaining, min_remaining, sec_remaining );
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
@@ -168,13 +257,13 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ /* END progress mod */
+ }
+ /* END progress mod */
+
ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size));
if (n_read < 0)
{
@@ -354,6 +500,14 @@
@@ -354,6 +588,14 @@
certain files in /proc or /sys with linux kernels. */
}
@@ -189,7 +278,7 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -420,9 +574,11 @@
@@ -420,9 +662,11 @@
static bool
extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
size_t hole_size, off_t src_total_size,
@@ -203,7 +292,7 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
{
struct extent_scan scan;
off_t last_ext_start = 0;
@@ -553,10 +709,16 @@
@@ -553,10 +797,16 @@
last_ext_len = ext_len;
bool read_hole;
@@ -218,11 +307,11 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
- true, src_name, dst_name, ext_len, &n_read,
- &read_hole))
+ true, move_mode, src_name, dst_name, ext_len,
+ &n_read, &read_hole,&s_progress))
+ &n_read, &read_hole, &s_progress))
goto fail;
dest_pos = ext_start + n_read;
@@ -1305,6 +1467,71 @@
@@ -1305,6 +1555,75 @@
buf_alloc = xmalloc (buf_size + buf_alignment);
buf = ptr_align (buf_alloc, buf_alignment);
@@ -261,7 +350,11 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = sprintf ( cProgressField[1], "%d files copied so far...", g_iFilesCopied );
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
@@ -294,7 +387,7 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
if (sparse_src)
{
bool normal_copy_required;
@@ -1316,7 +1543,9 @@
@@ -1316,7 +1635,9 @@
if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
@@ -305,11 +398,13 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
goto preserve_metadata;
if (! normal_copy_required)
@@ -1328,11 +1557,12 @@
@@ -1328,11 +1649,14 @@
off_t n_read;
bool wrote_hole_at_eof;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! sparse_copy (source_desc, dest_desc, buf, buf_size,
make_holes ? hole_size : 0,
- x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name,
@@ -321,22 +416,20 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1343,6 +1573,14 @@
@@ -1343,6 +1667,12 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
preserve_metadata:
@@ -1716,15 +1954,15 @@
@@ -1716,15 +2046,15 @@
fprintf (stderr,
(x->move_mode || x->unlink_dest_before_opening
|| x->unlink_dest_after_failed_open)
@@ -357,7 +450,7 @@ diff -aur coreutils-8.32/src/copy.c coreutils-8.32-patched/src/copy.c
diff -aur coreutils-8.32/src/copy.h coreutils-8.32-patched/src/copy.h
--- coreutils-8.32/src/copy.h 2020-01-01 15:13:12.000000000 +0100
+++ coreutils-8.32-patched/src/copy.h 2021-08-11 19:40:37.729820358 +0200
+++ coreutils-8.32-patched/src/copy.h 2022-04-03 21:03:04.873415280 +0200
@@ -234,6 +234,9 @@
Create destination directories as usual. */
bool symbolic_link;
@@ -368,25 +461,32 @@ diff -aur coreutils-8.32/src/copy.h coreutils-8.32-patched/src/copy.h
/* If true, do not copy a nondirectory that has an existing destination
with the same or newer modification time. */
bool update;
@@ -304,4 +307,15 @@
@@ -304,4 +307,22 @@
bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN OF PROGRESS MOD */
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END OF PROGRESS MOD */
+/* END progress mod */
+
#endif
diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c
--- coreutils-8.32/src/cp.c 2020-01-01 15:13:12.000000000 +0100
+++ coreutils-8.32-patched/src/cp.c 2021-08-11 19:40:37.729820358 +0200
+++ coreutils-8.32-patched/src/cp.c 2022-04-03 21:03:04.873415280 +0200
@@ -131,6 +131,7 @@
{"symbolic-link", no_argument, NULL, 's'},
{"target-directory", required_argument, NULL, 't'},
@@ -395,31 +495,42 @@ diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c
{"verbose", no_argument, NULL, 'v'},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
@@ -170,6 +171,7 @@
@@ -170,6 +171,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -635,6 +637,70 @@
@@ -635,6 +639,84 @@
die (EXIT_FAILURE, 0, _("target %s is not a directory"),
quoteaf (file[n_files - 1]));
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ /* BEGIN progress mod */
+ g_iTotalSize = 0;
+ g_iTotalFiles = 0;
+ g_iFilesCopied = 0;
+ g_iTotalWritten = 0;
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb, &new_dst, forcing))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "Calculating total size... \r" );
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
@@ -428,31 +539,36 @@ diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c
+ int j;
+
+ /* how many files are we copying */
+ char command[1024];
+ sprintf( command, "find \"%s\" -type f | wc -l", file[0]);
+ FILE *fp ;
+ char output[1024];
+ fp = popen(command,"r");
+ if ( fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL)
+ printf("failed to run find.\n");
+ char fcmd[] = "find";
+ fp = spawn(fcmd, (char *[]){ fcmd, file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ g_iTotalFiles = atoi( output ) ;
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ /* create command */
+ char command[1024];
+ sprintf ( command, "du -s \"%s\"", file[j] );
+ /* TODO: replace all quote signs in file[i] */
+
+ FILE *fp;
+ char output[1024];
+
+ /* run command */
+ fp = popen(command, "r");
+ char dcmd[] = "du";
+ fp = spawn(dcmd, (char *[]){ dcmd, "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du.\n" );
+ printf("failed to run du\r" );
+ }
+ else
+ {
@@ -460,26 +576,26 @@ diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "Calculating total size... %ld\r", iTotalSize );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize = iTotalSize;
+ /* END progress mod */
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
@@ -777,6 +843,46 @@
@@ -777,6 +859,56 @@
ok = copy (source, new_dest, 0, x, &unused, NULL);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* BEGIN progress mod */
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
@@ -513,15 +629,25 @@ diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ printf ( "%d files (%s) copied in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten,
+ sec_elapsed, s_copy_speed );
+ /* END progress mod */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -812,6 +918,7 @@
@@ -812,6 +944,7 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
@@ -529,7 +655,7 @@ diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c
x->set_mode = false;
x->mode = 0;
@@ -950,7 +1057,7 @@
@@ -950,7 +1083,7 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
@@ -538,7 +664,7 @@ diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c
long_opts, NULL))
!= -1)
{
@@ -1007,6 +1114,10 @@
@@ -1007,6 +1140,10 @@
x.unlink_dest_after_failed_open = true;
break;
@@ -551,75 +677,103 @@ diff -aur coreutils-8.32/src/cp.c coreutils-8.32-patched/src/cp.c
break;
diff -aur coreutils-8.32/src/mv.c coreutils-8.32-patched/src/mv.c
--- coreutils-8.32/src/mv.c 2020-01-01 15:13:12.000000000 +0100
+++ coreutils-8.32-patched/src/mv.c 2021-08-11 19:40:37.729820358 +0200
+++ coreutils-8.32-patched/src/mv.c 2022-04-03 21:03:04.873415280 +0200
@@ -66,6 +66,7 @@
{"target-directory", required_argument, NULL, 't'},
{"update", no_argument, NULL, 'u'},
{"verbose", no_argument, NULL, 'v'},
+ {"progress-ar", no_argument, NULL, 'g'},
+ {"progress-bar", no_argument, NULL, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -168,10 +169,86 @@
static bool
do_move (const char *source, const char *dest, const struct cp_options *x)
@@ -170,8 +171,130 @@
{
+ struct timeval start_time;
+
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+
+ if(progress && x->rename_errno != 0) {
+ /* BEGIN progress mod */
+ g_iTotalSize = 0;
+ g_iFilesCopied = 0;
+ g_iTotalWritten = 0;
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ printf ("Calculating total size... \r");
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ char fcmd[] = "find";
+ fp = spawn(fcmd, (char *[]){ fcmd, (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for each file */
+ /* create command */
+ char command[1024];
+ sprintf ( command, "du -s '%s'", source );
+ /* TODO: replace all quote signs in file[i] */
+
+ FILE *fp;
+ char output[1024];
+
+ /* run command */
+ fp = popen(command, "r");
+ char dcmd[] = "du";
+ fp = spawn(dcmd, (char *[]){ dcmd, "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ //printf("failed to run du.\n" );
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "Calculating total size... %ld\r", iTotalSize );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize = iTotalSize;
+ /* END progress mod */
+
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, false, x, &copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* BEGIN progress mod */
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
@@ -638,16 +792,32 @@ diff -aur coreutils-8.32/src/mv.c coreutils-8.32-patched/src/mv.c
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ printf ( "%d files (%s) moved in %.1f seconds (%s/s).\n", g_iFilesCopied, sTotalWritten,
+ sec_elapsed, s_copy_speed );
+ /* END progress mod */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -306,6 +383,7 @@
@@ -306,6 +429,7 @@
\n\
-b like --backup but does not accept an argument\n\
-f, --force do not prompt before overwriting\n\
@@ -655,7 +825,7 @@ diff -aur coreutils-8.32/src/mv.c coreutils-8.32-patched/src/mv.c
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -361,7 +439,7 @@
@@ -361,7 +485,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
@@ -664,15 +834,13 @@ diff -aur coreutils-8.32/src/mv.c coreutils-8.32-patched/src/mv.c
!= -1)
{
switch (c)
@@ -407,6 +485,11 @@
@@ -407,6 +531,9 @@
case 'v':
x.verbose = true;
break;
+
+ case 'g':
+ progress = true;
+ break;
+
case 'S':
make_backups = true;
backup_suffix = optarg;
+959
View File
@@ -0,0 +1,959 @@
diff -aur coreutils-9.0/src/copy.c coreutils-9.0-patched/src/copy.c
--- coreutils-9.0/src/copy.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.0-patched/src/copy.c 2022-04-03 21:03:04.881415398 +0200
@@ -129,6 +129,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -299,14 +426,23 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
- size_t hole_size, bool punch_holes, bool allow_reflink,
+ size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode,
char const *src_name, char const *dst_name,
uintmax_t max_n_read, off_t *total_n_read,
- bool *last_write_made_hole)
+ bool *last_write_made_hole,
+ struct progress_status *s_progress)
{
*last_write_made_hole = false;
*total_n_read = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
/* If not looking for holes, use copy_file_range if functional,
but don't use if reflink disallowed as that may be implicit. */
if ((! hole_size) && allow_reflink && functional_copy_file_range ())
@@ -317,8 +453,105 @@
surely aligned well. */
ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
ptrdiff_t copy_max = MIN (ssize_max, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size );
+ s_progress->cProgressField[5][s_progress->iBarLength-2] = '0';
+ s_progress->cProgressField[5][s_progress->iBarLength-1] = '%';
+ s_progress->cProgressField[5][s_progress->iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
MIN (max_n_read, copy_max), 0);
+
if (n_copied == 0)
{
/* copy_file_range incorrectly returns 0 when reading from
@@ -355,6 +588,14 @@
}
max_n_read -= n_copied;
*total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
bool make_hole = false;
@@ -362,6 +603,104 @@
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size));
if (n_read < 0)
{
@@ -446,6 +785,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -516,9 +863,11 @@
static bool
lseek_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
size_t hole_size, off_t ext_start, off_t src_total_size,
- enum Sparse_type sparse_mode,
+ enum Sparse_type sparse_mode, bool move_mode,
bool allow_reflink,
- char const *src_name, char const *dst_name)
+ char const *src_name, char const *dst_name,
+ int iCountDown, char ** cProgressField, struct timeval last_time,
+ int last_size, int iBarLength, struct stat src_open_sb)
{
off_t last_ext_start = 0;
off_t last_ext_len = 0;
@@ -590,10 +939,16 @@
is conservative and may miss some holes. */
off_t n_read;
bool read_hole;
+
+ struct timeval a;
+ struct stat b;
+
+ struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
sparse_mode == SPARSE_NEVER ? 0 : hole_size,
- true, allow_reflink, src_name, dst_name,
- ext_len, &n_read, &read_hole))
+ true, allow_reflink, move_mode, src_name,
+ dst_name, ext_len, &n_read, &read_hole, &s_progress))
return false;
dest_pos = ext_start + n_read;
@@ -1374,8 +1729,80 @@
buf_alloc = xmalloc (buf_size + buf_alignment);
buf = ptr_align (buf_alloc, buf_alignment);
+ /* BEGIN progress mod */
+ /* create a field of 6 lines */
+ char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( src_name ) > iBarLength - 22 )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", src_name );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+ /* END progress mod */
+
off_t n_read;
bool wrote_hole_at_eof = false;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! (
#ifdef SEEK_HOLE
scantype == LSEEK_SCANTYPE
@@ -1383,15 +1810,17 @@
scan_inference.ext_start, src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name)
+ x->move_mode, src_name, dst_name,
+ iCountDown, cProgressField, last_time, last_size,
+ iBarLength, src_open_sb)
:
#endif
sparse_copy (source_desc, dest_desc, buf, buf_size,
make_holes ? hole_size : 0,
x->sparse_mode == SPARSE_ALWAYS,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name, UINTMAX_MAX, &n_read,
- &wrote_hole_at_eof)))
+ x->move_mode, src_name, dst_name, UINTMAX_MAX,
+ &n_read, &wrote_hole_at_eof, &s_progress)))
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1402,6 +1831,14 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
if (x->preserve_timestamps)
diff -aur coreutils-9.0/src/copy.h coreutils-9.0-patched/src/copy.h
--- coreutils-9.0/src/copy.h 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.0-patched/src/copy.h 2022-04-03 21:03:04.881415398 +0200
@@ -236,6 +236,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* If true, do not copy a nondirectory that has an existing destination
with the same or newer modification time. */
bool update;
@@ -308,4 +311,22 @@
bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
diff -aur coreutils-9.0/src/cp.c coreutils-9.0-patched/src/cp.c
--- coreutils-9.0/src/cp.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.0-patched/src/cp.c 2022-04-03 21:03:04.885415457 +0200
@@ -131,6 +131,7 @@
{"symbolic-link", no_argument, NULL, 's'},
{"target-directory", required_argument, NULL, 't'},
{"update", no_argument, NULL, 'u'},
+ {"progress-bar", no_argument, NULL, 'g'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
@@ -170,6 +171,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -634,6 +638,82 @@
die (EXIT_FAILURE, 0, _("target %s is not a directory"),
quoteaf (file[n_files - 1]));
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb, &new_dst, forcing))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
@@ -781,6 +861,56 @@
ok = copy (source, new_dest, 0, x, &unused, NULL);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -816,6 +946,9 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+
+ x->progress_bar = false;
+
x->set_mode = false;
x->mode = 0;
@@ -954,7 +1087,8 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ /* BEGIN and END progress mod - remove the g in the next line!*/
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, NULL))
!= -1)
{
@@ -1011,6 +1145,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1171,6 +1309,9 @@
usage (EXIT_FAILURE);
}
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
diff -aur coreutils-9.0/src/mv.c coreutils-9.0-patched/src/mv.c
--- coreutils-9.0/src/mv.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.0-patched/src/mv.c 2022-04-03 21:03:04.885415457 +0200
@@ -66,6 +66,7 @@
{"target-directory", required_argument, NULL, 't'},
{"update", no_argument, NULL, 'u'},
{"verbose", no_argument, NULL, 'v'},
+ {"progress-bar", no_argument, NULL, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -170,8 +171,128 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+
+ if (progress && x->rename_errno != 0) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, false, x, &copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -306,6 +427,7 @@
\n\
-b like --backup but does not accept an argument\n\
-f, --force do not prompt before overwriting\n\
+ -g, --progress-bar add progress-bar\n\
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -361,7 +483,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))
+ while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL))
!= -1)
{
switch (c)
@@ -407,6 +529,9 @@
case 'v':
x.verbose = true;
break;
+ case 'g':
+ progress = true;
+ break;
case 'S':
make_backups = true;
backup_suffix = optarg;
+957
View File
@@ -0,0 +1,957 @@
diff -aur coreutils-9.1/src/copy.c coreutils-9.1-patched/src/copy.c
--- coreutils-9.1/src/copy.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.1-patched/src/copy.c 2022-04-03 21:03:04.881415398 +0200
@@ -121,6 +121,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -236,14 +363,23 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
- size_t hole_size, bool punch_holes, bool allow_reflink,
+ size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode,
char const *src_name, char const *dst_name,
uintmax_t max_n_read, off_t *total_n_read,
- bool *last_write_made_hole)
+ bool *last_write_made_hole,
+ struct progress_status *s_progress)
{
*last_write_made_hole = false;
*total_n_read = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
/* If not looking for holes, use copy_file_range if functional,
but don't use if reflink disallowed as that may be implicit. */
if (!hole_size && allow_reflink)
@@ -253,6 +389,102 @@
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size );
+ s_progress->cProgressField[5][s_progress->iBarLength-2] = '0';
+ s_progress->cProgressField[5][s_progress->iBarLength-1] = '%';
+ s_progress->cProgressField[5][s_progress->iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
@@ -291,6 +523,14 @@
}
max_n_read -= n_copied;
*total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
bool make_hole = false;
@@ -298,6 +538,103 @@
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ /* END progress mod */
+ }
+
if (!*abuf)
*abuf = xalignalloc (getpagesize (), buf_size);
char *buf = *abuf;
@@ -385,6 +722,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -456,9 +801,11 @@
static bool
lseek_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
size_t hole_size, off_t ext_start, off_t src_total_size,
- enum Sparse_type sparse_mode,
+ enum Sparse_type sparse_mode, bool move_mode,
bool allow_reflink,
- char const *src_name, char const *dst_name)
+ char const *src_name, char const *dst_name,
+ int iCountDown, char ** cProgressField, struct timeval last_time,
+ int last_size, int iBarLength, struct stat src_open_sb)
{
off_t last_ext_start = 0;
off_t last_ext_len = 0;
@@ -530,10 +877,16 @@
is conservative and may miss some holes. */
off_t n_read;
bool read_hole;
+
+ struct timeval a;
+ struct stat b;
+
+ struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if ( ! sparse_copy (src_fd, dest_fd, abuf, buf_size,
sparse_mode == SPARSE_NEVER ? 0 : hole_size,
- true, allow_reflink, src_name, dst_name,
- ext_len, &n_read, &read_hole))
+ true, allow_reflink, move_mode, src_name,
+ dst_name, ext_len, &n_read, &read_hole, &s_progress))
return false;
dest_pos = ext_start + n_read;
@@ -1344,8 +1697,80 @@
buf_size = blcm;
}
+ /* BEGIN progress mod */
+ /* create a field of 6 lines */
+ char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( src_name ) > iBarLength - 22 )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", src_name );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+ /* END progress mod */
+
off_t n_read;
bool wrote_hole_at_eof = false;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! (
#ifdef SEEK_HOLE
scantype == LSEEK_SCANTYPE
@@ -1353,15 +1778,17 @@
scan_inference.ext_start, src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name)
+ x->move_mode, src_name, dst_name,
+ iCountDown, cProgressField, last_time, last_size,
+ iBarLength, src_open_sb)
:
#endif
sparse_copy (source_desc, dest_desc, &buf, buf_size,
make_holes ? hole_size : 0,
x->sparse_mode == SPARSE_ALWAYS,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name, UINTMAX_MAX, &n_read,
- &wrote_hole_at_eof)))
+ x->move_mode, src_name, dst_name, UINTMAX_MAX,
+ &n_read, &wrote_hole_at_eof, &s_progress)))
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1372,6 +1799,14 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
if (x->preserve_timestamps)
diff -aur coreutils-9.1/src/copy.h coreutils-9.1-patched/src/copy.h
--- coreutils-9.1/src/copy.h 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.1-patched/src/copy.h 2022-04-03 21:03:04.881415398 +0200
@@ -236,6 +236,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* If true, do not copy a nondirectory that has an existing destination
with the same or newer modification time. */
bool update;
@@ -313,4 +316,22 @@
_GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
diff -aur coreutils-9.1/src/cp.c coreutils-9.1-patched/src/cp.c
--- coreutils-9.1/src/cp.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.1-patched/src/cp.c 2022-04-03 21:03:04.885415457 +0200
@@ -128,6 +128,7 @@
{"symbolic-link", no_argument, NULL, 's'},
{"target-directory", required_argument, NULL, 't'},
{"update", no_argument, NULL, 'u'},
+ {"progress-bar", no_argument, NULL, 'g'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
@@ -167,6 +168,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -640,6 +644,82 @@
}
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
/* cp file1...filen edir
@@ -779,6 +859,56 @@
ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, NULL);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -814,6 +944,9 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+
+ x->progress_bar = false;
+
x->set_mode = false;
x->mode = 0;
@@ -952,7 +1085,8 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ /* BEGIN and END progress mod - remove the g in the next line!*/
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, NULL))
!= -1)
{
@@ -1009,6 +1143,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1159,6 +1297,9 @@
usage (EXIT_FAILURE);
}
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
diff -aur coreutils-9.1/src/mv.c coreutils-9.1-patched/src/mv.c
--- coreutils-9.1/src/mv.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.1-patched/src/mv.c 2022-04-03 21:03:04.885415457 +0200
@@ -64,6 +64,7 @@
{"target-directory", required_argument, NULL, 't'},
{"update", no_argument, NULL, 'u'},
{"verbose", no_argument, NULL, 'v'},
+ {"progress-bar", no_argument, NULL, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -155,9 +156,130 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ struct stat sb;
+
+ if (progress && x->rename_errno != 0) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source, &sb))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
&copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -255,6 +377,7 @@
\n\
-b like --backup but does not accept an argument\n\
-f, --force do not prompt before overwriting\n\
+ -g, --progress-bar add progress-bar\n\
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -311,7 +434,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))
+ while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL))
!= -1)
{
switch (c)
@@ -347,6 +470,9 @@
case 'v':
x.verbose = true;
break;
+ case 'g':
+ progress = true;
+ break;
case 'S':
make_backups = true;
backup_suffix = optarg;
+932
View File
@@ -0,0 +1,932 @@
--- coreutils-9.11/src/copy-file-data.c 2026-07-23 09:47:29.805900811 -0600
+++ coreutils-9.11k/src/copy-file-data.c 2026-07-23 09:47:29.837329370 -0600
@@ -28,6 +28,153 @@
#include "full-write.h"
#include "ioblksize.h"
+#include <sys/ioctl.h>
+#include <termios.h>
+
+/* BEGIN progress mod */
+
+/* The progress globals (g_iTotalSize, g_iTotalWritten, ...) count KiB, not
+ bytes, because the prescan in cp.c/mv.c derives totals from "du -s" which
+ reports KiB blocks. Divide a raw byte count by this before accumulating. */
+#define PROGRESS_BYTES_PER_KIB 1024
+
+/* Each progress row is iBarLength chars wide. The right-aligned size readout
+ produced by file_size_format ("542.3 MiB") is written starting this many
+ columns from the end of the row so it sits just left of the bar tail. */
+#define PROGRESS_SIZE_COL 21
+
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+/* Progress state for the file currently being copied. Set up in
+ copy_file_data (which has the cp_options and source size) and read by
+ sparse_copy/lseek_copy while they run. Only touched when the global
+ progress flag is set. */
+static struct progress_status g_s_progress;
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ /* hours */
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ /* minutes */
+ int min = ( (int) seconds / 60) % 60;
+ /* seconds */
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Result of infer_scantype when it returns LSEEK_SCANTYPE. */
struct scan_inference
{
@@ -117,6 +264,16 @@
{
count_t total_n_read = 0;
+ /* BEGIN progress mod */
+ if (progress) {
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size / PROGRESS_BYTES_PER_KIB;
+ }
+ /* END progress mod */
+
if (debug->sparse_detection == COPY_DEBUG_UNKNOWN)
debug->sparse_detection = hole_size ? COPY_DEBUG_YES : COPY_DEBUG_NO;
else if (hole_size && debug->sparse_detection == COPY_DEBUG_EXTERNAL)
@@ -131,6 +288,100 @@
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ g_s_progress.iCountDown--;
+ char * sProgressBar = g_s_progress.cProgressField[5];
+ if ( g_s_progress.iCountDown < 0 )
+ g_s_progress.iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( g_s_progress.iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + total_n_read / PROGRESS_BYTES_PER_KIB;
+ int cur_fsize = g_iFTotalWritten + total_n_read / PROGRESS_BYTES_PER_KIB;
+ int usec_elapsed = cur_time.tv_usec - g_s_progress.last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - g_s_progress.last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - g_s_progress.last_size )
+ / sec_elapsed );
+ char s_copy_speed[PROGRESS_NUM_BUF];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ g_s_progress.last_time = cur_time;
+ g_s_progress.last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[PROGRESS_NUM_BUF];
+ char f_ftime[PROGRESS_NUM_BUF];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( g_s_progress.cProgressField[1],
+ "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ g_s_progress.cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[PROGRESS_ETA_BUF] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( g_s_progress.cProgressField[2], g_s_progress.iBarLength,
+ g_iTotalWritten + total_n_read / PROGRESS_BYTES_PER_KIB, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( g_s_progress.cProgressField[1] + g_s_progress.iBarLength - PROGRESS_SIZE_COL,
+ g_iTotalWritten + total_n_read / PROGRESS_BYTES_PER_KIB, 1 );
+ g_s_progress.cProgressField[1][g_s_progress.iBarLength - PROGRESS_SIZE_COL + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, g_s_progress.iBarLength, g_s_progress.src_open_sb.st_size, g_s_progress.src_open_sb.st_size );
+ g_s_progress.cProgressField[5][g_s_progress.iBarLength-2] = '0';
+ g_s_progress.cProgressField[5][g_s_progress.iBarLength-1] = '%';
+ g_s_progress.cProgressField[5][g_s_progress.iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( g_s_progress.cProgressField[4] + g_s_progress.iBarLength - PROGRESS_SIZE_COL, g_s_progress.src_open_sb.st_size, 0 );
+ g_s_progress.cProgressField[4][g_s_progress.iBarLength - PROGRESS_SIZE_COL + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", g_s_progress.cProgressField[it] );
+ if ( strlen ( g_s_progress.cProgressField[it] ) < (size_t) g_s_progress.iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
@@ -182,6 +433,14 @@
debug->offload = COPY_DEBUG_YES;
max_n_read -= n_copied;
total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += total_n_read / PROGRESS_BYTES_PER_KIB;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
else
debug->offload = COPY_DEBUG_AVOIDED;
@@ -192,6 +451,98 @@
while (0 < max_n_read)
{
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ g_s_progress.iCountDown--;
+ char * sProgressBar = g_s_progress.cProgressField[5];
+ if ( g_s_progress.iCountDown < 0 )
+ g_s_progress.iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( g_s_progress.iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + total_n_read / PROGRESS_BYTES_PER_KIB;
+ int cur_fsize = g_iFTotalWritten + total_n_read / PROGRESS_BYTES_PER_KIB;
+ int usec_elapsed = cur_time.tv_usec - g_s_progress.last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - g_s_progress.last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - g_s_progress.last_size )
+ / sec_elapsed );
+ char s_copy_speed[PROGRESS_NUM_BUF];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ g_s_progress.last_time = cur_time;
+ g_s_progress.last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[PROGRESS_NUM_BUF];
+ char f_ftime[PROGRESS_NUM_BUF];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( g_s_progress.cProgressField[1],
+ "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ g_s_progress.cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[PROGRESS_ETA_BUF] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( g_s_progress.cProgressField[3],
+ "copying at %s/s %s", s_copy_speed, s_ftime );
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( g_s_progress.cProgressField[2], g_s_progress.iBarLength,
+ g_iTotalWritten + total_n_read / PROGRESS_BYTES_PER_KIB, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( g_s_progress.cProgressField[1] + g_s_progress.iBarLength - PROGRESS_SIZE_COL,
+ g_iTotalWritten + total_n_read / PROGRESS_BYTES_PER_KIB, 1 );
+ g_s_progress.cProgressField[1][g_s_progress.iBarLength - PROGRESS_SIZE_COL + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, g_s_progress.iBarLength, total_n_read, g_s_progress.src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( g_s_progress.cProgressField[4] + g_s_progress.iBarLength - PROGRESS_SIZE_COL, total_n_read, 0 );
+ g_s_progress.cProgressField[4][g_s_progress.iBarLength - PROGRESS_SIZE_COL + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", g_s_progress.cProgressField[it] );
+ if ( strlen ( g_s_progress.cProgressField[it] ) < (size_t) g_s_progress.iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
if (!*abuf)
*abuf = xalignalloc (getpagesize (), buf_size);
char *buf = *abuf;
@@ -270,6 +621,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += total_n_read / PROGRESS_BYTES_PER_KIB;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
if (hole_size)
*hole_size = make_hole ? psize : 0;
return total_n_read;
@@ -602,6 +961,86 @@
intmax_t result;
off_t hole_size = 0;
+ /* BEGIN progress mod */
+ /* Set up the per-file progress field that sparse_copy/lseek_copy render
+ into. g_s_progress is a file-scope global read by those functions. */
+ char ** cProgressField = NULL;
+ if (progress)
+ {
+ struct stat src_open_sb = *ist;
+
+ /* create a field of 6 lines */
+ cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( iname ) > (size_t) (iBarLength - 22) )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", iname + ( strlen ( iname ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", iname );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+
+ g_s_progress = ( struct progress_status )
+ { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb };
+ }
+ /* END progress mod */
+
if (scantype == LSEEK_SCANTYPE)
{
#ifdef SEEK_HOLE
@@ -641,6 +1080,15 @@
}
}
+ /* BEGIN progress mod */
+ if (progress && cProgressField) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
+
alignfree (buf);
return result;
}
--- coreutils-9.11/src/copy.h 2026-07-23 09:47:29.805951169 -0600
+++ coreutils-9.11k/src/copy.h 2026-07-23 09:47:29.837358788 -0600
@@ -255,6 +255,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* Control if destination files are replaced. */
enum Update_type update;
@@ -360,4 +363,32 @@
_GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+
+/* Scratch-buffer sizes for the short formatted strings the progress code
+ builds with sprintf. PROGRESS_NUM_BUF holds a file_size_format /
+ format_time result ("542.3 MiB", "1h 2m 3.4s"). PROGRESS_ETA_BUF holds
+ the longer "(about ... remaining)" string. PROGRESS_FTYPE_BUF holds the
+ file-type label ("folder(s)/file(s)" is the longest at 17 chars). */
+#define PROGRESS_NUM_BUF 20
+#define PROGRESS_ETA_BUF 40
+#define PROGRESS_FTYPE_BUF 20
+
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
--- coreutils-9.11/src/cp.c 2026-07-23 09:47:29.806107878 -0600
+++ coreutils-9.11k/src/cp.c 2026-07-23 09:47:29.837379316 -0600
@@ -141,6 +141,7 @@
{"symbolic-link", no_argument, NULL, 's'},
{"target-directory", required_argument, NULL, 't'},
{"update", optional_argument, NULL, 'u'},
+ {"progress-bar", no_argument, NULL, 'g'},
{"verbose", no_argument, NULL, 'v'},
{"keep-directory-symlink", no_argument, NULL,
KEEP_DIRECTORY_SYMLINK_OPTION},
@@ -203,6 +204,11 @@
again (this option is ignored when the -n option is also used)\n\
"));
oputs (_("\
+ -g, --progress-bar\n\
+ add a progress bar. Note that this doesn't work with reflink;\n\
+ reflink will be automatically disabled\n\
+"));
+ oputs (_("\
-i, --interactive\n\
prompt before overwrite (overrides a previous -n option)\n\
"));
@@ -737,6 +743,74 @@
}
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+
+ if (target_directory_operand (file[0], &sb) >= 0)
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
/* cp file1...filen edir
@@ -876,6 +950,56 @@
ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, NULL);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[PROGRESS_NUM_BUF];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[PROGRESS_NUM_BUF];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[PROGRESS_FTYPE_BUF];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[PROGRESS_NUM_BUF];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -911,6 +1035,7 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+ x->progress_bar = false;
x->set_mode = false;
x->mode = 0;
@@ -1049,7 +1174,7 @@
cp_option_init (&x);
int c;
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, NULL))
!= -1)
{
@@ -1110,6 +1235,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1276,6 +1405,11 @@
usage (EXIT_FAILURE);
}
+ /* BEGIN progress mod */
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+ /* END progress mod */
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
--- coreutils-9.11/src/mv.c 2026-07-23 09:47:29.807078459 -0600
+++ coreutils-9.11k/src/mv.c 2026-07-23 09:47:29.837402508 -0600
@@ -80,6 +80,7 @@
{"target-directory", required_argument, NULL, 't'},
{"update", optional_argument, NULL, 'u'},
{"verbose", no_argument, NULL, 'v'},
+ {"progress-bar", no_argument, NULL, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -171,9 +172,119 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ struct stat sb;
+
+ if (progress && x->rename_errno != 0) {
+ if (target_directory_operand (source, &sb) >= 0)
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (char *)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
&copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[PROGRESS_NUM_BUF];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[PROGRESS_NUM_BUF];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[PROGRESS_FTYPE_BUF];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[PROGRESS_NUM_BUF];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -283,6 +394,10 @@
do not prompt before overwriting\n\
"));
oputs (_("\
+ -g, --progress-bar\n\
+ add a progress bar\n\
+"));
+ oputs (_("\
-i, --interactive\n\
prompt before overwrite\n\
"));
@@ -365,7 +480,7 @@
priv_set_remove_linkdir ();
int c;
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))
+ while ((c = getopt_long (argc, argv, "bfingt:uvS:TZ", long_options, NULL))
!= -1)
{
switch (c)
@@ -378,6 +493,9 @@
case 'f':
x.interactive = I_ALWAYS_YES;
break;
+ case 'g':
+ progress = true;
+ break;
case 'i':
x.interactive = I_ASK_USER;
break;
--- coreutils-9.11/doc/coreutils.texi 2026-07-23 09:47:29.758119162 -0600
+++ coreutils-9.11k/doc/coreutils.texi 2026-07-23 09:47:29.838680665 -0600
@@ -8825,6 +8825,11 @@
This option is ignored when the @option{--no-clobber} or @option{-n} option
is also used.
+@optItem{cp,-g,}
+@optItemx{cp,--progress-bar,}
+Display a progress bar while copying, showing the percentage complete,
+copy speed, estimated time remaining, and per-file and overall progress.
+
@optItem{cp,-i,}
@optItemx{cp,--interactive,}
When copying a file other than a directory, prompt whether to
@@ -9995,6 +10000,12 @@
@end macro
@mvOptsIfn
+@optItem{mv,-g,}
+@optItemx{mv,--progress-bar,}
+Display a progress bar while moving files across file systems, showing the
+percentage complete, copy speed, estimated time remaining, and per-file and
+overall progress.
+
@optItem{mv,-i,}
@optItemx{mv,--interactive,}
@cindex prompts, forcing
+957
View File
@@ -0,0 +1,957 @@
diff -aur coreutils-9.2/src/copy.c coreutils-9.2-patched/src/copy.c
--- coreutils-9.2/src/copy.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.2-patched/src/copy.c 2022-04-03 21:03:04.881415398 +0200
@@ -121,6 +121,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -302,14 +429,23 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
- size_t hole_size, bool punch_holes, bool allow_reflink,
+ size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode,
char const *src_name, char const *dst_name,
uintmax_t max_n_read, off_t *total_n_read,
- bool *last_write_made_hole)
+ bool *last_write_made_hole,
+ struct progress_status *s_progress)
{
*last_write_made_hole = false;
*total_n_read = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
if (copy_debug.sparse_detection == COPY_DEBUG_UNKNOWN)
copy_debug.sparse_detection = hole_size ? COPY_DEBUG_YES : COPY_DEBUG_NO;
@@ -322,6 +458,102 @@
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size );
+ s_progress->cProgressField[5][s_progress->iBarLength-2] = '0';
+ s_progress->cProgressField[5][s_progress->iBarLength-1] = '%';
+ s_progress->cProgressField[5][s_progress->iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
@@ -367,6 +599,14 @@
copy_debug.offload = COPY_DEBUG_YES;
max_n_read -= n_copied;
*total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
else
copy_debug.offload = COPY_DEBUG_AVOIDED;
@@ -377,6 +617,103 @@
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ /* END progress mod */
+ }
+
if (!*abuf)
*abuf = xalignalloc (getpagesize (), buf_size);
char *buf = *abuf;
@@ -464,6 +801,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -535,9 +880,11 @@
static bool
lseek_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
size_t hole_size, off_t ext_start, off_t src_total_size,
- enum Sparse_type sparse_mode,
+ enum Sparse_type sparse_mode, bool move_mode,
bool allow_reflink,
- char const *src_name, char const *dst_name)
+ char const *src_name, char const *dst_name,
+ int iCountDown, char ** cProgressField, struct timeval last_time,
+ int last_size, int iBarLength, struct stat src_open_sb)
{
off_t last_ext_start = 0;
off_t last_ext_len = 0;
@@ -611,10 +958,16 @@
is conservative and may miss some holes. */
off_t n_read;
bool read_hole;
+
+ struct timeval a;
+ struct stat b;
+
+ struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if ( ! sparse_copy (src_fd, dest_fd, abuf, buf_size,
sparse_mode != SPARSE_ALWAYS ? 0 : hole_size,
- true, allow_reflink, src_name, dst_name,
- ext_len, &n_read, &read_hole))
+ true, allow_reflink, move_mode, src_name,
+ dst_name, ext_len, &n_read, &read_hole, &s_progress))
return false;
dest_pos = ext_start + n_read;
@@ -1580,8 +1933,80 @@
buf_size = blcm;
}
+ /* BEGIN progress mod */
+ /* create a field of 6 lines */
+ char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( src_name ) > iBarLength - 22 )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", src_name );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+ /* END progress mod */
+
off_t n_read;
bool wrote_hole_at_eof = false;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! (
#ifdef SEEK_HOLE
scantype == LSEEK_SCANTYPE
@@ -1589,15 +2014,17 @@
scan_inference.ext_start, src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name)
+ x->move_mode, src_name, dst_name,
+ iCountDown, cProgressField, last_time, last_size,
+ iBarLength, src_open_sb)
:
#endif
sparse_copy (source_desc, dest_desc, &buf, buf_size,
make_holes ? hole_size : 0,
x->sparse_mode == SPARSE_ALWAYS,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name, UINTMAX_MAX, &n_read,
- &wrote_hole_at_eof)))
+ x->move_mode, src_name, dst_name, UINTMAX_MAX,
+ &n_read, &wrote_hole_at_eof, &s_progress)))
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1608,6 +2035,14 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
if (x->preserve_timestamps)
diff -aur coreutils-9.2/src/copy.h coreutils-9.2-patched/src/copy.h
--- coreutils-9.2/src/copy.h 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.2-patched/src/copy.h 2022-04-03 21:03:04.881415398 +0200
@@ -235,6 +235,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* If true, do not copy a nondirectory that has an existing destination
with the same or newer modification time. */
bool update;
@@ -315,4 +318,22 @@
_GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
diff -aur coreutils-9.2/src/cp.c coreutils-9.2-patched/src/cp.c
--- coreutils-9.2/src/cp.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.2-patched/src/cp.c 2022-04-03 21:03:04.885415457 +0200
@@ -130,6 +130,7 @@
{"symbolic-link", no_argument, NULL, 's'},
{"target-directory", required_argument, NULL, 't'},
{"update", no_argument, NULL, 'u'},
+ {"progress-bar", no_argument, NULL, 'g'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
@@ -172,6 +173,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -649,6 +653,82 @@
}
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
/* cp file1...filen edir
@@ -788,6 +868,56 @@
ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, NULL);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -823,6 +953,9 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+
+ x->progress_bar = false;
+
x->set_mode = false;
x->mode = 0;
@@ -961,7 +1094,8 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ /* BEGIN and END progress mod - remove the g in the next line!*/
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, NULL))
!= -1)
{
@@ -1022,6 +1156,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1172,6 +1310,9 @@
usage (EXIT_FAILURE);
}
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
diff -aur coreutils-9.2/src/mv.c coreutils-9.2-patched/src/mv.c
--- coreutils-9.2/src/mv.c 2021-09-24 13:31:05.000000000 +0200
+++ coreutils-9.2-patched/src/mv.c 2022-04-03 21:03:04.885415457 +0200
@@ -68,6 +68,7 @@
{"target-directory", required_argument, NULL, 't'},
{"update", no_argument, NULL, 'u'},
{"verbose", no_argument, NULL, 'v'},
+ {"progress-bar", no_argument, NULL, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -159,9 +160,130 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ struct stat sb;
+
+ if (progress && x->rename_errno != 0) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source, &sb))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
&copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -264,6 +386,7 @@
"), stdout);
fputs (_("\
-f, --force do not prompt before overwriting\n\
+ -g, --progress-bar add progress-bar\n\
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -321,7 +444,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))
+ while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL))
!= -1)
{
switch (c)
@@ -363,6 +486,9 @@
case 'v':
x.verbose = true;
break;
+ case 'g':
+ progress = true;
+ break;
case 'S':
make_backups = true;
backup_suffix = optarg;
+972
View File
@@ -0,0 +1,972 @@
--- coreutils-9.3/src/ls.c 2023-03-13 14:08:10.000000000 -0400
+++ coreutils-9.3/src/ls.c 2023-07-02 16:10:41.947444700 -0400
@@ -2279,18 +2279,11 @@
if (linelen < 0)
{
/* Suppress bogus warning re comparing ws.ws_col to big integer. */
-# if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wtype-limits"
-# endif
struct winsize ws;
if (stdout_isatty ()
&& 0 <= ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws)
&& 0 < ws.ws_col)
linelen = ws.ws_col <= MIN (PTRDIFF_MAX, SIZE_MAX) ? ws.ws_col : 0;
-# if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
-# pragma GCC diagnostic pop
-# endif
}
#endif
if (linelen < 0)
--- coreutils-9.3/src/copy.c 2023-04-17 16:43:34.000000000 -0400
+++ coreutils-9.3/src/copy.c 2023-07-02 16:25:44.187962600 -0400
@@ -121,6 +121,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -317,14 +444,23 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
- size_t hole_size, bool punch_holes, bool allow_reflink,
+ size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode,
char const *src_name, char const *dst_name,
uintmax_t max_n_read, off_t *total_n_read,
- bool *last_write_made_hole)
+ bool *last_write_made_hole,
+ struct progress_status *s_progress)
{
*last_write_made_hole = false;
*total_n_read = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
if (copy_debug.sparse_detection == COPY_DEBUG_UNKNOWN)
copy_debug.sparse_detection = hole_size ? COPY_DEBUG_YES : COPY_DEBUG_NO;
else if (hole_size && copy_debug.sparse_detection == COPY_DEBUG_EXTERNAL)
@@ -339,6 +475,102 @@
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size );
+ s_progress->cProgressField[5][s_progress->iBarLength-2] = '0';
+ s_progress->cProgressField[5][s_progress->iBarLength-1] = '%';
+ s_progress->cProgressField[5][s_progress->iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
@@ -382,6 +614,14 @@
copy_debug.offload = COPY_DEBUG_YES;
max_n_read -= n_copied;
*total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
else
copy_debug.offload = COPY_DEBUG_AVOIDED;
@@ -392,6 +632,103 @@
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ /* END progress mod */
+ }
+
if (!*abuf)
*abuf = xalignalloc (getpagesize (), buf_size);
char *buf = *abuf;
@@ -479,6 +816,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -550,9 +895,11 @@
static bool
lseek_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
size_t hole_size, off_t ext_start, off_t src_total_size,
- enum Sparse_type sparse_mode,
+ enum Sparse_type sparse_mode, bool move_mode,
bool allow_reflink,
- char const *src_name, char const *dst_name)
+ char const *src_name, char const *dst_name,
+ int iCountDown, char ** cProgressField, struct timeval last_time,
+ int last_size, int iBarLength, struct stat src_open_sb)
{
off_t last_ext_start = 0;
off_t last_ext_len = 0;
@@ -626,10 +973,16 @@
is conservative and may miss some holes. */
off_t n_read;
bool read_hole;
+
+ struct timeval a;
+ struct stat b;
+
+ struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if ( ! sparse_copy (src_fd, dest_fd, abuf, buf_size,
sparse_mode != SPARSE_ALWAYS ? 0 : hole_size,
- true, allow_reflink, src_name, dst_name,
- ext_len, &n_read, &read_hole))
+ true, allow_reflink, move_mode, src_name,
+ dst_name, ext_len, &n_read, &read_hole, &s_progress))
return false;
dest_pos = ext_start + n_read;
@@ -1592,8 +1945,80 @@
buf_size = blcm;
}
+ /* BEGIN progress mod */
+ /* create a field of 6 lines */
+ char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( src_name ) > iBarLength - 22 )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", src_name );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+ /* END progress mod */
+
off_t n_read;
bool wrote_hole_at_eof = false;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! (
#ifdef SEEK_HOLE
scantype == LSEEK_SCANTYPE
@@ -1601,15 +2026,17 @@
scan_inference.ext_start, src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name)
+ x->move_mode, src_name, dst_name,
+ iCountDown, cProgressField, last_time, last_size,
+ iBarLength, src_open_sb)
:
#endif
sparse_copy (source_desc, dest_desc, &buf, buf_size,
make_holes ? hole_size : 0,
x->sparse_mode == SPARSE_ALWAYS,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name, UINTMAX_MAX, &n_read,
- &wrote_hole_at_eof)))
+ x->move_mode, src_name, dst_name, UINTMAX_MAX,
+ &n_read, &wrote_hole_at_eof, &s_progress)))
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1620,6 +2047,14 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
if (x->preserve_timestamps)
--- coreutils-9.3/src/copy.h 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.3/src/copy.h 2023-07-02 16:15:50.875580500 -0400
@@ -249,6 +249,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* If true, do not copy a nondirectory that has an existing destination
with the same or newer modification time. */
bool update;
@@ -329,4 +332,22 @@
_GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
--- coreutils-9.3/src/cp.cg 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.3/src/cp.c 2023-07-02 16:26:48.895801600 -0400
@@ -140,6 +140,7 @@
{"symbolic-link", no_argument, NULL, 's'},
{"target-directory", required_argument, NULL, 't'},
{"update", optional_argument, NULL, 'u'},
+ {"progress-bar", no_argument, NULL, 'g'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
@@ -182,6 +183,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -666,6 +670,82 @@
}
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
/* cp file1...filen edir
@@ -805,6 +885,56 @@
ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, NULL);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -840,6 +970,7 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+ x->progress_bar = false;
x->set_mode = false;
x->mode = 0;
@@ -978,7 +1109,8 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ /* BEGIN and END progress mod - remove the g in the next line!*/
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, NULL))
!= -1)
{
@@ -1039,6 +1171,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1212,6 +1348,9 @@
usage (EXIT_FAILURE);
}
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
--- coreutils-9.3/src/mv.c 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.3/src/mv.c 2023-07-02 16:19:42.433714100 -0400
@@ -79,6 +79,7 @@
{"target-directory", required_argument, NULL, 't'},
{"update", optional_argument, NULL, 'u'},
{"verbose", no_argument, NULL, 'v'},
+ {"progress-bar", no_argument, NULL, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -170,9 +171,130 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ struct stat sb;
+
+ if (progress && x->rename_errno != 0) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source, &sb))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
&copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -275,6 +397,7 @@
"), stdout);
fputs (_("\
-f, --force do not prompt before overwriting\n\
+ -g, --progress-bar add progress-bar\n\
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -337,7 +460,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))
+ while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, NULL))
!= -1)
{
switch (c)
@@ -350,6 +473,9 @@
case 'f':
x.interactive = I_ALWAYS_YES;
break;
+ case 'g':
+ progress = true;
+ break;
case 'i':
x.interactive = I_ASK_USER;
break;
+972
View File
@@ -0,0 +1,972 @@
--- coreutils-9.4/src/ls.c 2023-03-13 14:08:10.000000000 -0400
+++ coreutils-9.4/src/ls.c 2023-07-02 16:10:41.947444700 -0400
@@ -2279,18 +2279,11 @@
if (linelen < 0)
{
/* Suppress bogus warning re comparing ws.ws_col to big integer. */
-# if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wtype-limits"
-# endif
struct winsize ws;
if (stdout_isatty ()
&& 0 <= ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws)
&& 0 < ws.ws_col)
linelen = ws.ws_col <= MIN (PTRDIFF_MAX, SIZE_MAX) ? ws.ws_col : 0;
-# if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
-# pragma GCC diagnostic pop
-# endif
}
#endif
if (linelen < 0)
--- coreutils-9.4/src/copy.c 2023-04-17 16:43:34.000000000 -0400
+++ coreutils-9.4/src/copy.c 2023-07-02 16:25:44.187962600 -0400
@@ -121,6 +121,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -317,14 +444,23 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
- size_t hole_size, bool punch_holes, bool allow_reflink,
+ size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode,
char const *src_name, char const *dst_name,
uintmax_t max_n_read, off_t *total_n_read,
- bool *last_write_made_hole)
+ bool *last_write_made_hole,
+ struct progress_status *s_progress)
{
*last_write_made_hole = false;
*total_n_read = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
if (copy_debug.sparse_detection == COPY_DEBUG_UNKNOWN)
copy_debug.sparse_detection = hole_size ? COPY_DEBUG_YES : COPY_DEBUG_NO;
else if (hole_size && copy_debug.sparse_detection == COPY_DEBUG_EXTERNAL)
@@ -339,6 +475,102 @@
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size );
+ s_progress->cProgressField[5][s_progress->iBarLength-2] = '0';
+ s_progress->cProgressField[5][s_progress->iBarLength-1] = '%';
+ s_progress->cProgressField[5][s_progress->iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, nullptr, dest_fd, nullptr,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
@@ -382,6 +614,14 @@
copy_debug.offload = COPY_DEBUG_YES;
max_n_read -= n_copied;
*total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
else
copy_debug.offload = COPY_DEBUG_AVOIDED;
@@ -392,6 +632,103 @@
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ /* END progress mod */
+ }
+
if (!*abuf)
*abuf = xalignalloc (getpagesize (), buf_size);
char *buf = *abuf;
@@ -479,6 +816,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -550,9 +895,11 @@
static bool
lseek_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
size_t hole_size, off_t ext_start, off_t src_total_size,
- enum Sparse_type sparse_mode,
+ enum Sparse_type sparse_mode, bool move_mode,
bool allow_reflink,
- char const *src_name, char const *dst_name)
+ char const *src_name, char const *dst_name,
+ int iCountDown, char ** cProgressField, struct timeval last_time,
+ int last_size, int iBarLength, struct stat src_open_sb)
{
off_t last_ext_start = 0;
off_t last_ext_len = 0;
@@ -626,10 +973,16 @@
is conservative and may miss some holes. */
off_t n_read;
bool read_hole;
+
+ struct timeval a;
+ struct stat b;
+
+ struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if ( ! sparse_copy (src_fd, dest_fd, abuf, buf_size,
sparse_mode != SPARSE_ALWAYS ? 0 : hole_size,
- true, allow_reflink, src_name, dst_name,
- ext_len, &n_read, &read_hole))
+ true, allow_reflink, move_mode, src_name,
+ dst_name, ext_len, &n_read, &read_hole, &s_progress))
return false;
dest_pos = ext_start + n_read;
@@ -1592,8 +1945,80 @@
buf_size = blcm;
}
+ /* BEGIN progress mod */
+ /* create a field of 6 lines */
+ char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( src_name ) > iBarLength - 22 )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", src_name );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+ /* END progress mod */
+
off_t n_read;
bool wrote_hole_at_eof = false;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! (
#ifdef SEEK_HOLE
scantype == LSEEK_SCANTYPE
@@ -1601,15 +2026,17 @@
scan_inference.ext_start, src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name)
+ x->move_mode, src_name, dst_name,
+ iCountDown, cProgressField, last_time, last_size,
+ iBarLength, src_open_sb)
:
#endif
sparse_copy (source_desc, dest_desc, &buf, buf_size,
make_holes ? hole_size : 0,
x->sparse_mode == SPARSE_ALWAYS,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name, UINTMAX_MAX, &n_read,
- &wrote_hole_at_eof)))
+ x->move_mode, src_name, dst_name, UINTMAX_MAX,
+ &n_read, &wrote_hole_at_eof, &s_progress)))
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1620,6 +2047,14 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
if (x->preserve_timestamps)
--- coreutils-9.4/src/copy.h 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.4/src/copy.h 2023-07-02 16:15:50.875580500 -0400
@@ -249,6 +249,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* If true, do not copy a nondirectory that has an existing destination
with the same or newer modification time. */
bool update;
@@ -329,4 +332,22 @@
_GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
--- coreutils-9.4/src/cp.cg 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.4/src/cp.c 2023-07-02 16:26:48.895801600 -0400
@@ -140,6 +140,7 @@
{"symbolic-link", no_argument, nullptr, 's'},
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
+ {"progress-bar", no_argument, nullptr, 'g'},
{"verbose", no_argument, nullptr, 'v'},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
@@ -182,6 +183,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -666,6 +670,82 @@
}
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
/* cp file1...filen edir
@@ -805,6 +885,56 @@
ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, NULL);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -840,6 +970,7 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+ x->progress_bar = false;
x->set_mode = false;
x->mode = 0;
@@ -978,7 +1109,8 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ /* BEGIN and END progress mod - remove the g in the next line!*/
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, nullptr))
!= -1)
{
@@ -1039,6 +1171,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1212,6 +1348,9 @@
usage (EXIT_FAILURE);
}
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
--- coreutils-9.4/src/mv.c 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.4/src/mv.c 2023-07-02 16:19:42.433714100 -0400
@@ -79,6 +79,7 @@
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
{"verbose", no_argument, nullptr, 'v'},
+ {"progress-bar", no_argument, nullptr, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -170,9 +171,130 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ struct stat sb;
+
+ if (progress && x->rename_errno != 0) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source, &sb))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
&copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -275,6 +397,7 @@
"), stdout);
fputs (_("\
-f, --force do not prompt before overwriting\n\
+ -g, --progress-bar add progress-bar\n\
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -337,7 +460,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, nullptr))
+ while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, nullptr))
!= -1)
{
switch (c)
@@ -350,6 +473,9 @@
case 'f':
x.interactive = I_ALWAYS_YES;
break;
+ case 'g':
+ progress = true;
+ break;
case 'i':
x.interactive = I_ASK_USER;
break;
+951
View File
@@ -0,0 +1,951 @@
--- coreutils-9.5/src/copy.c 2023-04-17 16:43:34.000000000 -0400
+++ coreutils-9.5/src/copy.c 2023-07-02 16:25:44.187962600 -0400
@@ -121,6 +121,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -317,14 +444,23 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
- size_t hole_size, bool punch_holes, bool allow_reflink,
+ size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode,
char const *src_name, char const *dst_name,
uintmax_t max_n_read, off_t *total_n_read,
- bool *last_write_made_hole)
+ bool *last_write_made_hole,
+ struct progress_status *s_progress)
{
*last_write_made_hole = false;
*total_n_read = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
if (copy_debug.sparse_detection == COPY_DEBUG_UNKNOWN)
copy_debug.sparse_detection = hole_size ? COPY_DEBUG_YES : COPY_DEBUG_NO;
else if (hole_size && copy_debug.sparse_detection == COPY_DEBUG_EXTERNAL)
@@ -339,6 +475,102 @@
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size );
+ s_progress->cProgressField[5][s_progress->iBarLength-2] = '0';
+ s_progress->cProgressField[5][s_progress->iBarLength-1] = '%';
+ s_progress->cProgressField[5][s_progress->iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, nullptr, dest_fd, nullptr,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
@@ -382,6 +614,14 @@
copy_debug.offload = COPY_DEBUG_YES;
max_n_read -= n_copied;
*total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
else
copy_debug.offload = COPY_DEBUG_AVOIDED;
@@ -392,6 +632,103 @@
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ /* END progress mod */
+ }
+
if (!*abuf)
*abuf = xalignalloc (getpagesize (), buf_size);
char *buf = *abuf;
@@ -479,6 +816,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -550,9 +895,11 @@
static bool
lseek_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
size_t hole_size, off_t ext_start, off_t src_total_size,
- enum Sparse_type sparse_mode,
+ enum Sparse_type sparse_mode, bool move_mode,
bool allow_reflink,
- char const *src_name, char const *dst_name)
+ char const *src_name, char const *dst_name,
+ int iCountDown, char ** cProgressField, struct timeval last_time,
+ int last_size, int iBarLength, struct stat src_open_sb)
{
off_t last_ext_start = 0;
off_t last_ext_len = 0;
@@ -626,10 +973,16 @@
is conservative and may miss some holes. */
off_t n_read;
bool read_hole;
+
+ struct timeval a;
+ struct stat b;
+
+ struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if ( ! sparse_copy (src_fd, dest_fd, abuf, buf_size,
sparse_mode != SPARSE_ALWAYS ? 0 : hole_size,
- true, allow_reflink, src_name, dst_name,
- ext_len, &n_read, &read_hole))
+ true, allow_reflink, move_mode, src_name,
+ dst_name, ext_len, &n_read, &read_hole, &s_progress))
return false;
dest_pos = ext_start + n_read;
@@ -1592,8 +1945,80 @@
buf_size = blcm;
}
+ /* BEGIN progress mod */
+ /* create a field of 6 lines */
+ char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( src_name ) > iBarLength - 22 )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", src_name );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+ /* END progress mod */
+
off_t n_read;
bool wrote_hole_at_eof = false;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! (
#ifdef SEEK_HOLE
scantype == LSEEK_SCANTYPE
@@ -1601,15 +2026,17 @@
scan_inference.ext_start, src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name)
+ x->move_mode, src_name, dst_name,
+ iCountDown, cProgressField, last_time, last_size,
+ iBarLength, src_open_sb)
:
#endif
sparse_copy (source_desc, dest_desc, &buf, buf_size,
make_holes ? hole_size : 0,
x->sparse_mode == SPARSE_ALWAYS,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name, UINTMAX_MAX, &n_read,
- &wrote_hole_at_eof)))
+ x->move_mode, src_name, dst_name, UINTMAX_MAX,
+ &n_read, &wrote_hole_at_eof, &s_progress)))
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1620,6 +2047,14 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
if (x->preserve_timestamps)
--- coreutils-9.5/src/copy.h 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.5/src/copy.h 2023-07-02 16:15:50.875580500 -0400
@@ -249,6 +249,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* If true, do not copy a nondirectory that has an existing destination
with the same or newer modification time. */
bool update;
@@ -329,4 +332,22 @@
_GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
--- coreutils-9.5/src/cp.cg 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.5/src/cp.c 2023-07-02 16:26:48.895801600 -0400
@@ -140,6 +140,7 @@
{"symbolic-link", no_argument, nullptr, 's'},
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
+ {"progress-bar", no_argument, nullptr, 'g'},
{"verbose", no_argument, nullptr, 'v'},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
@@ -182,6 +183,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -666,6 +670,82 @@
}
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
/* cp file1...filen edir
@@ -805,6 +885,56 @@
ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, NULL);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -840,6 +970,7 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+ x->progress_bar = false;
x->set_mode = false;
x->mode = 0;
@@ -978,7 +1109,8 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ /* BEGIN and END progress mod - remove the g in the next line!*/
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, nullptr))
!= -1)
{
@@ -1039,6 +1171,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1212,6 +1348,9 @@
usage (EXIT_FAILURE);
}
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
--- coreutils-9.5/src/mv.c 2023-04-10 06:14:08.000000000 -0400
+++ coreutils-9.5/src/mv.c 2023-07-02 16:19:42.433714100 -0400
@@ -79,6 +79,7 @@
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
{"verbose", no_argument, nullptr, 'v'},
+ {"progress-bar", no_argument, nullptr, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -170,9 +171,130 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ struct stat sb;
+
+ if (progress && x->rename_errno != 0) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source, &sb))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
&copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -275,6 +397,7 @@
"), stdout);
fputs (_("\
-f, --force do not prompt before overwriting\n\
+ -g, --progress-bar add progress-bar\n\
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -337,7 +460,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, nullptr))
+ while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, nullptr))
!= -1)
{
switch (c)
@@ -350,6 +473,9 @@
case 'f':
x.interactive = I_ALWAYS_YES;
break;
+ case 'g':
+ progress = true;
+ break;
case 'i':
x.interactive = I_ASK_USER;
break;
+976
View File
@@ -0,0 +1,976 @@
diff -Naur coreutils-9.7/src/copy.c coreutils-9.7k/src/copy.c
--- coreutils-9.7/src/copy.c 2025-02-20 09:56:20.000000000 -0300
+++ coreutils-9.7k/src/copy.c 2026-01-17 13:17:40.208485733 -0300
@@ -113,6 +113,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -317,14 +444,23 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
- size_t hole_size, bool punch_holes, bool allow_reflink,
+ size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode,
char const *src_name, char const *dst_name,
uintmax_t max_n_read, off_t *total_n_read,
- bool *last_write_made_hole)
+ bool *last_write_made_hole,
+ struct progress_status *s_progress)
{
*last_write_made_hole = false;
*total_n_read = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
if (copy_debug.sparse_detection == COPY_DEBUG_UNKNOWN)
copy_debug.sparse_detection = hole_size ? COPY_DEBUG_YES : COPY_DEBUG_NO;
else if (hole_size && copy_debug.sparse_detection == COPY_DEBUG_EXTERNAL)
@@ -339,6 +475,102 @@
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size );
+ s_progress->cProgressField[5][s_progress->iBarLength-2] = '0';
+ s_progress->cProgressField[5][s_progress->iBarLength-1] = '%';
+ s_progress->cProgressField[5][s_progress->iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, nullptr, dest_fd, nullptr,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
@@ -382,6 +614,14 @@
copy_debug.offload = COPY_DEBUG_YES;
max_n_read -= n_copied;
*total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
else
copy_debug.offload = COPY_DEBUG_AVOIDED;
@@ -392,6 +632,103 @@
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ /* END progress mod */
+ }
+
if (!*abuf)
*abuf = xalignalloc (getpagesize (), buf_size);
char *buf = *abuf;
@@ -479,6 +816,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -550,9 +895,11 @@
static bool
lseek_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
size_t hole_size, off_t ext_start, off_t src_total_size,
- enum Sparse_type sparse_mode,
+ enum Sparse_type sparse_mode, bool move_mode,
bool allow_reflink,
- char const *src_name, char const *dst_name)
+ char const *src_name, char const *dst_name,
+ int iCountDown, char ** cProgressField, struct timeval last_time,
+ int last_size, int iBarLength, struct stat src_open_sb)
{
off_t last_ext_start = 0;
off_t last_ext_len = 0;
@@ -626,10 +973,16 @@
is conservative and may miss some holes. */
off_t n_read;
bool read_hole;
+
+ struct timeval a;
+ struct stat b;
+
+ struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if ( ! sparse_copy (src_fd, dest_fd, abuf, buf_size,
sparse_mode != SPARSE_ALWAYS ? 0 : hole_size,
- true, allow_reflink, src_name, dst_name,
- ext_len, &n_read, &read_hole))
+ true, allow_reflink, move_mode, src_name,
+ dst_name, ext_len, &n_read, &read_hole, &s_progress))
return false;
dest_pos = ext_start + n_read;
@@ -1596,8 +1949,80 @@
buf_size = blcm;
}
+ /* BEGIN progress mod */
+ /* create a field of 6 lines */
+ char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( src_name ) > iBarLength - 22 )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", src_name );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+ /* END progress mod */
+
off_t n_read;
bool wrote_hole_at_eof = false;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! (
#ifdef SEEK_HOLE
scantype == LSEEK_SCANTYPE
@@ -1605,15 +2030,17 @@
scan_inference.ext_start, src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name)
+ x->move_mode, src_name, dst_name,
+ iCountDown, cProgressField, last_time, last_size,
+ iBarLength, src_open_sb)
:
#endif
sparse_copy (source_desc, dest_desc, &buf, buf_size,
make_holes ? hole_size : 0,
x->sparse_mode == SPARSE_ALWAYS,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name, UINTMAX_MAX, &n_read,
- &wrote_hole_at_eof)))
+ x->move_mode, src_name, dst_name, UINTMAX_MAX,
+ &n_read, &wrote_hole_at_eof, &s_progress)))
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1624,6 +2051,14 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
if (x->preserve_timestamps)
diff -Naur coreutils-9.7/src/copy.h coreutils-9.7k/src/copy.h
--- coreutils-9.7/src/copy.h 2025-01-07 14:31:45.000000000 -0300
+++ coreutils-9.7k/src/copy.h 2026-01-17 13:24:50.938944306 -0300
@@ -98,14 +98,14 @@
DEREF_ALWAYS
};
-# define VALID_SPARSE_MODE(Mode) \
- ((Mode) == SPARSE_NEVER \
- || (Mode) == SPARSE_AUTO \
+# define VALID_SPARSE_MODE(Mode) \
+ ((Mode) == SPARSE_NEVER \
+ || (Mode) == SPARSE_AUTO \
|| (Mode) == SPARSE_ALWAYS)
-# define VALID_REFLINK_MODE(Mode) \
- ((Mode) == REFLINK_NEVER \
- || (Mode) == REFLINK_AUTO \
+# define VALID_REFLINK_MODE(Mode) \
+ ((Mode) == REFLINK_NEVER \
+ || (Mode) == REFLINK_AUTO \
|| (Mode) == REFLINK_ALWAYS)
/* These options control how files are copied by at least the
@@ -255,6 +255,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* Control if destination files are replaced. */
enum Update_type update;
@@ -328,4 +331,22 @@
_GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
diff -Naur coreutils-9.7/src/cp.c coreutils-9.7k/src/cp.c
--- coreutils-9.7/src/cp.c 2025-01-07 14:31:45.000000000 -0300
+++ coreutils-9.7k/src/cp.c 2026-01-17 13:17:40.209427521 -0300
@@ -141,6 +141,7 @@
{"symbolic-link", no_argument, nullptr, 's'},
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
+ {"progress-bar", no_argument, nullptr, 'g'},
{"verbose", no_argument, nullptr, 'v'},
{"keep-directory-symlink", no_argument, nullptr,
KEEP_DIRECTORY_SYMLINK_OPTION},
@@ -185,6 +186,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -682,6 +686,82 @@
}
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
/* cp file1...filen edir
@@ -822,6 +902,56 @@
ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, nullptr);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -857,6 +987,7 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+ x->progress_bar = false;
x->set_mode = false;
x->mode = 0;
@@ -996,7 +1127,8 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ /* BEGIN and END progress mod - remove the g in the next line!*/
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, nullptr))
!= -1)
{
@@ -1057,6 +1189,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1223,6 +1359,9 @@
usage (EXIT_FAILURE);
}
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
diff -Naur coreutils-9.7/src/mv.c coreutils-9.7k/src/mv.c
--- coreutils-9.7/src/mv.c 2025-01-07 14:31:45.000000000 -0300
+++ coreutils-9.7k/src/mv.c 2026-01-17 13:17:40.210002773 -0300
@@ -80,6 +80,7 @@
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
{"verbose", no_argument, nullptr, 'v'},
+ {"progress-bar", no_argument, nullptr, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{nullptr, 0, nullptr, 0}
@@ -171,9 +172,130 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ struct stat sb;
+
+ if (progress && x->rename_errno != 0) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source, &sb))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
&copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -279,6 +401,7 @@
"), stdout);
fputs (_("\
-f, --force do not prompt before overwriting\n\
+ -g, --progress-bar add progress-bar\n\
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -341,7 +464,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, nullptr))
+ while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, nullptr))
!= -1)
{
switch (c)
@@ -354,6 +477,9 @@
case 'f':
x.interactive = I_ALWAYS_YES;
break;
+ case 'g':
+ progress = true;
+ break;
case 'i':
x.interactive = I_ASK_USER;
break;
+976
View File
@@ -0,0 +1,976 @@
diff -Naur coreutils-9.7/src/copy.c coreutils-9.7k/src/copy.c
--- coreutils-9.7/src/copy.c 2025-02-20 09:56:20.000000000 -0300
+++ coreutils-9.7k/src/copy.c 2026-01-17 13:17:40.208485733 -0300
@@ -113,6 +113,133 @@
dev_t dev;
};
+/* BEGIN progress mod */
+struct progress_status {
+ int iCountDown;
+ char ** cProgressField;
+ struct timeval last_time;
+ int last_size, iBarLength;
+ struct stat src_open_sb;
+};
+
+FILE * spawn( const char *cmd, char *const argv[] )
+{
+ FILE *ret = NULL;
+ int pfd_read[2];
+ pid_t pid;
+
+ if (cmd == NULL || argv == NULL)
+ return ret;
+
+ if (pipe(pfd_read) < 0) {
+ error(0, errno, "pipe: %s", cmd);
+ return ret;
+ }
+
+ if ((pid = fork()) == 0) {
+ int err = dup2(pfd_read[1], 1) < 0;
+ close(pfd_read[0]);
+ close(pfd_read[1]);
+
+ if (err)
+ error(EXIT_FAILURE, errno, "dup2: %s", cmd);
+ execvp(cmd, argv);
+ error(EXIT_FAILURE, errno, "exec: %s", cmd);
+ }
+
+ close(pfd_read[1]);
+
+ if (pid < 0) {
+ close(pfd_read[0]);
+ error(0, errno, "fork: %s", cmd);
+ return ret;
+ }
+
+ ret = fdopen(pfd_read[0], "r");
+ return ret;
+}
+
+void format_time ( char * _cDest, double seconds, bool showall )
+{
+ // hours
+ int hr = ( (int) seconds / (60 * 60)) % 24;
+ // minutes
+ int min = ( (int) seconds / 60) % 60;
+ // seconds
+ double sec = seconds - (hr * (60 * 60)) - (min * 60);
+ if ( showall )
+ {
+ if ( seconds < 0 )
+ sprintf(_cDest, "%2ch %2cm %2cs", '0', '0', '?');
+ else
+ sprintf(_cDest, "%2dh %2dm %2ds", hr, min, (int) sec);
+ } else if ( seconds >= 3600 )
+ {
+ sprintf(_cDest, "%2dh %2dm %4.1fs", hr, min, sec);
+ } else if ( seconds >= 60 )
+ {
+ sprintf(_cDest, "%2dm %4.1fs", min, sec);
+ } else
+ {
+ sprintf(_cDest, "%4.1fs", sec);
+ }
+}
+
+static void file_progress_bar ( char * _cDest, int _iBarLength, long _lProgress, long _lTotal )
+{
+ double dPercent = (double) _lProgress / (double) _lTotal * 100.f;
+ sprintf( _cDest + ( _iBarLength - 6), "%4.1f", dPercent );
+ _cDest[_iBarLength - 2] = ' ';
+
+ int i;
+ for ( i=1; i<=_iBarLength - 9; i++)
+ {
+ if ( dPercent > (double) (i-1) / (_iBarLength - 10) * 100.f )
+ {
+ _cDest[i] = '=';
+ }
+ else
+ {
+ _cDest[i] = ' ';
+ }
+ }
+ for ( i=1; i<_iBarLength - 9; i++)
+ {
+ if ( ( _cDest[i+1] == ' ' ) && ( _cDest[i] == '=' ) )
+ _cDest[i] = '>' ;
+ }
+}
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter )
+{
+ int iCounter = _iCounter;
+ double dSize = ( double ) _lSize;
+ while ( dSize >= 1000. )
+ {
+ dSize /= 1024.;
+ iCounter++;
+ }
+
+ /* get unit */
+ char * sUnit;
+ if ( iCounter == 0 )
+ sUnit = "B";
+ else if ( iCounter == 1 )
+ sUnit = "KiB";
+ else if ( iCounter == 2 )
+ sUnit = "MiB";
+ else if ( iCounter == 3 )
+ sUnit = "GiB";
+ else if ( iCounter == 4 )
+ sUnit = "TiB";
+ else
+ sUnit = "N/A";
+
+ /* write number */
+ return sprintf ( _cDst, "%5.1f %s", dSize, sUnit );
+}
+/* END progress mod */
+
/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
@@ -317,14 +444,23 @@
bytes read. */
static bool
sparse_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
- size_t hole_size, bool punch_holes, bool allow_reflink,
+ size_t hole_size, bool punch_holes, bool allow_reflink, bool move_mode,
char const *src_name, char const *dst_name,
uintmax_t max_n_read, off_t *total_n_read,
- bool *last_write_made_hole)
+ bool *last_write_made_hole,
+ struct progress_status *s_progress)
{
*last_write_made_hole = false;
*total_n_read = 0;
+ /* BEGIN progress mod */
+ gettimeofday ( & g_oFStartTime, NULL );
+ g_iFTotalWritten = 0;
+ struct stat st;
+ stat(src_name, &st);
+ g_iFTotalSize = st.st_size/1024;
+ /* END progress mod */
+
if (copy_debug.sparse_detection == COPY_DEBUG_UNKNOWN)
copy_debug.sparse_detection = hole_size ? COPY_DEBUG_YES : COPY_DEBUG_NO;
else if (hole_size && copy_debug.sparse_detection == COPY_DEBUG_EXTERNAL)
@@ -339,6 +475,102 @@
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* set current progress bar to be always 100 % */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, s_progress->src_open_sb.st_size, s_progress->src_open_sb.st_size );
+ s_progress->cProgressField[5][s_progress->iBarLength-2] = '0';
+ s_progress->cProgressField[5][s_progress->iBarLength-1] = '%';
+ s_progress->cProgressField[5][s_progress->iBarLength] = '\0';
+
+ /* print the status as always full filesize*/
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, s_progress->src_open_sb.st_size, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ }
+ /* END progress mod */
+
ssize_t n_copied = copy_file_range (src_fd, nullptr, dest_fd, nullptr,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
@@ -382,6 +614,14 @@
copy_debug.offload = COPY_DEBUG_YES;
max_n_read -= n_copied;
*total_n_read += n_copied;
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
}
else
copy_debug.offload = COPY_DEBUG_AVOIDED;
@@ -392,6 +632,103 @@
while (max_n_read)
{
+
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update countdown */
+ s_progress->iCountDown--;
+ char * sProgressBar = s_progress->cProgressField[5];
+ if ( s_progress->iCountDown < 0 )
+ s_progress->iCountDown = 100;
+
+ /* just print one line with the percentage, but not always */
+ if ( s_progress->iCountDown == 0 )
+ {
+ /* calculate current speed */
+ struct timeval cur_time;
+ gettimeofday ( & cur_time, NULL );
+ int cur_size = g_iTotalWritten + *total_n_read / 1024;
+ int cur_fsize = g_iFTotalWritten + *total_n_read / 1024;
+ int usec_elapsed = cur_time.tv_usec - s_progress->last_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( cur_time.tv_sec - s_progress->last_time.tv_sec );
+ int copy_speed = ( int ) ( ( double ) ( cur_size - s_progress->last_size )
+ / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed >= 0 ? copy_speed : 0, 1 );
+ /* update vars */
+ s_progress->last_time = cur_time;
+ s_progress->last_size = cur_size;
+
+ /* how many time has passed since the start? */
+ int isec_elapsed = cur_time.tv_sec - g_oStartTime.tv_sec;
+ int isec_felapsed = cur_time.tv_sec - g_oFStartTime.tv_sec;
+ int sec_remaining = ( int ) ( ( double ) isec_elapsed / cur_size
+ * g_iTotalSize ) - isec_elapsed;
+ int sec_fremaining = ( int ) ( ( double ) isec_felapsed / cur_fsize
+ * g_iFTotalSize ) - isec_felapsed;
+ /* print out */
+
+ char f_ttime[20];
+ char f_ftime[20];
+ format_time(f_ttime, sec_remaining, true);
+ format_time(f_ftime, sec_fremaining, true);
+
+ int fs_len;
+ fs_len = sprintf ( s_progress->cProgressField[1],
+ move_mode
+ ? "%d of %d files moved (about %s remaining)"
+ : "%d of %d files copied (about %s remaining)",
+ g_iFilesCopied, g_iTotalFiles, f_ttime );
+ s_progress->cProgressField[1][fs_len] = ' ';
+
+ char s_ftime[40] = "";
+
+ if (g_iTotalFiles > 1)
+ sprintf ( s_ftime, "(about %s remaining)", f_ftime );
+ else
+ sprintf ( s_ftime, "(about %s remaining)", f_ttime );
+
+ sprintf ( s_progress->cProgressField[3],
+ move_mode
+ ? "moving at %s/s %s"
+ : "copying at %s/s %s", s_copy_speed, s_ftime );
+ if ( g_iTotalFiles > 1 )
+ {
+ /* global progress bar */
+ file_progress_bar ( s_progress->cProgressField[2], s_progress->iBarLength,
+ g_iTotalWritten + *total_n_read / 1024, g_iTotalSize );
+
+ /* print the global status */
+ fs_len = file_size_format ( s_progress->cProgressField[1] + s_progress->iBarLength - 21,
+ g_iTotalWritten + *total_n_read / 1024, 1 );
+ s_progress->cProgressField[1][s_progress->iBarLength - 21 + fs_len] = ' ';
+ }
+
+ /* current progress bar */
+ file_progress_bar ( sProgressBar, s_progress->iBarLength, *total_n_read, s_progress->src_open_sb.st_size );
+
+ /* print the status */
+ fs_len = file_size_format ( s_progress->cProgressField[4] + s_progress->iBarLength - 21, *total_n_read, 0 );
+ s_progress->cProgressField[4][s_progress->iBarLength - 21 + fs_len] = ' ';
+
+ /* print the field */
+ int it;
+ for ( it = g_iTotalFiles>1 ? 0 : 3; it < 6; it++ )
+ {
+ printf ( "\033[K%s\n", s_progress->cProgressField[it] );
+ if ( strlen ( s_progress->cProgressField[it] ) < s_progress->iBarLength )
+ printf ( "%s", "" );
+ }
+ if ( g_iTotalFiles > 1 )
+ printf ( "\r\033[6A" );
+ else
+ printf ( "\r\033[3A" );
+ fflush ( stdout );
+ }
+ /* END progress mod */
+ }
+
if (!*abuf)
*abuf = xalignalloc (getpagesize (), buf_size);
char *buf = *abuf;
@@ -479,6 +816,14 @@
certain files in /proc or /sys with linux kernels. */
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* update total size */
+ g_iTotalWritten += *total_n_read / 1024;
+ g_iFilesCopied++;
+ }
+ /* END progress mod */
+
/* Ensure a trailing hole is created, so that subsequent
calls of sparse_copy() start at the correct offset. */
if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
@@ -550,9 +895,11 @@
static bool
lseek_copy (int src_fd, int dest_fd, char **abuf, size_t buf_size,
size_t hole_size, off_t ext_start, off_t src_total_size,
- enum Sparse_type sparse_mode,
+ enum Sparse_type sparse_mode, bool move_mode,
bool allow_reflink,
- char const *src_name, char const *dst_name)
+ char const *src_name, char const *dst_name,
+ int iCountDown, char ** cProgressField, struct timeval last_time,
+ int last_size, int iBarLength, struct stat src_open_sb)
{
off_t last_ext_start = 0;
off_t last_ext_len = 0;
@@ -626,10 +973,16 @@
is conservative and may miss some holes. */
off_t n_read;
bool read_hole;
+
+ struct timeval a;
+ struct stat b;
+
+ struct progress_status s_progress={iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if ( ! sparse_copy (src_fd, dest_fd, abuf, buf_size,
sparse_mode != SPARSE_ALWAYS ? 0 : hole_size,
- true, allow_reflink, src_name, dst_name,
- ext_len, &n_read, &read_hole))
+ true, allow_reflink, move_mode, src_name,
+ dst_name, ext_len, &n_read, &read_hole, &s_progress))
return false;
dest_pos = ext_start + n_read;
@@ -1596,8 +1949,80 @@
buf_size = blcm;
}
+ /* BEGIN progress mod */
+ /* create a field of 6 lines */
+ char ** cProgressField = ( char ** ) calloc ( 6, sizeof ( char * ) );
+ /* get console width */
+ int iBarLength = 80;
+ struct winsize win;
+ if ( ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &win) == 0 && win.ws_col > 0 )
+ if (win.ws_col > iBarLength) /* String printed may be longer on smaller screens */
+ iBarLength = win.ws_col;
+ /* create rows */
+ int it;
+ for ( it = 0; it < 6; it++ )
+ {
+ cProgressField[it] = ( char * ) malloc ( iBarLength + 1 );
+ /* init with spaces */
+ int j;
+ for ( j = 0; j < iBarLength; j++ )
+ cProgressField[it][j] = ' ';
+ cProgressField[it][iBarLength] = '\0';
+ }
+
+ /* global progress bar? */
+ if ( g_iTotalFiles > 1 )
+ {
+ /* init global progress bar */
+ cProgressField[2][0] = '[';
+ cProgressField[2][iBarLength - 8] = ']';
+ cProgressField[2][iBarLength - 7] = ' ';
+ cProgressField[2][iBarLength - 1] = '%';
+
+ /* total size */
+ cProgressField[1][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[1] + iBarLength - 9, g_iTotalSize, 1 );
+
+ /* show how many files were written */
+ int sum_length = 0;
+ sum_length = sprintf ( cProgressField[1],
+ x->move_mode
+ ? "%d of %d files moved so far"
+ : "%d of %d files copied so far", g_iFilesCopied, g_iTotalFiles );
+ cProgressField[1][sum_length] = ' ';
+ }
+
+ /* truncate filename? */
+ int fn_length;
+ if ( strlen ( src_name ) > iBarLength - 22 )
+ fn_length =
+ sprintf ( cProgressField[4], "...%s", src_name + ( strlen ( src_name ) - iBarLength + 25 ) );
+ else
+ fn_length = sprintf ( cProgressField[4], "%s", src_name );
+ cProgressField[4][fn_length] = ' ';
+
+ /* filesize */
+ cProgressField[4][iBarLength - 11] = '/';
+ file_size_format ( cProgressField[4] + iBarLength - 9, src_open_sb.st_size, 0 );
+
+ int iCountDown = 1;
+ char * sProgressBar = cProgressField[5];
+ sProgressBar[0] = '[';
+ sProgressBar[iBarLength - 8] = ']';
+ sProgressBar[iBarLength - 7] = ' ';
+ sProgressBar[iBarLength - 1] = '%';
+
+ /* this will always save the time in between */
+ struct timeval last_time;
+ gettimeofday ( & last_time, NULL );
+ int last_size = g_iTotalWritten;
+ /* END progress mod */
+
off_t n_read;
bool wrote_hole_at_eof = false;
+
+ struct progress_status s_progress = { iCountDown, cProgressField, last_time, last_size, iBarLength, src_open_sb};
+
if (! (
#ifdef SEEK_HOLE
scantype == LSEEK_SCANTYPE
@@ -1605,15 +2030,17 @@
scan_inference.ext_start, src_open_sb.st_size,
make_holes ? x->sparse_mode : SPARSE_NEVER,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name)
+ x->move_mode, src_name, dst_name,
+ iCountDown, cProgressField, last_time, last_size,
+ iBarLength, src_open_sb)
:
#endif
sparse_copy (source_desc, dest_desc, &buf, buf_size,
make_holes ? hole_size : 0,
x->sparse_mode == SPARSE_ALWAYS,
x->reflink_mode != REFLINK_NEVER,
- src_name, dst_name, UINTMAX_MAX, &n_read,
- &wrote_hole_at_eof)))
+ x->move_mode, src_name, dst_name, UINTMAX_MAX,
+ &n_read, &wrote_hole_at_eof, &s_progress)))
{
return_val = false;
goto close_src_and_dst_desc;
@@ -1624,6 +2051,14 @@
return_val = false;
goto close_src_and_dst_desc;
}
+ /* BEGIN progress mod */
+ if (progress) {
+ int i;
+ for ( i = 0; i < 6; i++ )
+ free ( cProgressField[i] );
+ free ( cProgressField );
+ }
+ /* END progress mod */
}
if (x->preserve_timestamps)
diff -Naur coreutils-9.7/src/copy.h coreutils-9.7k/src/copy.h
--- coreutils-9.7/src/copy.h 2025-01-07 14:31:45.000000000 -0300
+++ coreutils-9.7k/src/copy.h 2026-01-17 13:24:50.938944306 -0300
@@ -98,14 +98,14 @@
DEREF_ALWAYS
};
-# define VALID_SPARSE_MODE(Mode) \
- ((Mode) == SPARSE_NEVER \
- || (Mode) == SPARSE_AUTO \
+# define VALID_SPARSE_MODE(Mode) \
+ ((Mode) == SPARSE_NEVER \
+ || (Mode) == SPARSE_AUTO \
|| (Mode) == SPARSE_ALWAYS)
-# define VALID_REFLINK_MODE(Mode) \
- ((Mode) == REFLINK_NEVER \
- || (Mode) == REFLINK_AUTO \
+# define VALID_REFLINK_MODE(Mode) \
+ ((Mode) == REFLINK_NEVER \
+ || (Mode) == REFLINK_AUTO \
|| (Mode) == REFLINK_ALWAYS)
/* These options control how files are copied by at least the
@@ -255,6 +255,9 @@
Create destination directories as usual. */
bool symbolic_link;
+ /* If true, draw a nice progress bar on screen */
+ bool progress_bar;
+
/* Control if destination files are replaced. */
enum Update_type update;
@@ -328,4 +331,22 @@
_GL_ATTRIBUTE_NONNULL () _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
+/* BEGIN progress mod */
+FILE * spawn( const char *cmd, char *const argv[] );
+void format_time ( char * _cDst, double seconds, bool showall );
+
+int file_size_format ( char * _cDst, long _lSize, int _iCounter );
+
+__attribute__((__common__)) long g_iTotalSize;
+__attribute__((__common__)) long g_iFTotalSize;
+__attribute__((__common__)) long g_iTotalWritten;
+__attribute__((__common__)) long g_iFTotalWritten;
+__attribute__((__common__)) int g_iFilesCopied;
+__attribute__((__common__)) int g_iDirectoriesCopied;
+__attribute__((__common__)) struct timeval g_oStartTime;
+__attribute__((__common__)) struct timeval g_oFStartTime;
+__attribute__((__common__)) int g_iTotalFiles;
+__attribute__((__common__)) bool progress;
+/* END progress mod */
+
#endif
diff -Naur coreutils-9.7/src/cp.c coreutils-9.7k/src/cp.c
--- coreutils-9.7/src/cp.c 2025-01-07 14:31:45.000000000 -0300
+++ coreutils-9.7k/src/cp.c 2026-01-17 13:17:40.209427521 -0300
@@ -141,6 +141,7 @@
{"symbolic-link", no_argument, nullptr, 's'},
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
+ {"progress-bar", no_argument, nullptr, 'g'},
{"verbose", no_argument, nullptr, 'v'},
{"keep-directory-symlink", no_argument, nullptr,
KEEP_DIRECTORY_SYMLINK_OPTION},
@@ -185,6 +186,9 @@
-f, --force if an existing destination file cannot be\n\
opened, remove it and try again (this option\n\
is ignored when the -n option is also used)\n\
+ -g, --progress-bar add a progress bar.\n\
+ Note that this doesn't work with reflink,\n\
+ reflink will be automatically disabled\n\
-i, --interactive prompt before overwrite (overrides a previous -n\
\n\
option)\n\
@@ -682,6 +686,82 @@
}
}
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ if (progress) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = n_files;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (file[0], &sb))
+ g_iDirectoriesCopied++;
+
+ /* save time */
+ gettimeofday ( & start_time, NULL );
+ g_oStartTime = start_time;
+
+ printf ( "calculating total size... \r" );
+ fflush ( stdout );
+ long iTotalSize = 0;
+ int iFiles = n_files;
+ if ( ! target_directory )
+ iFiles = n_files - 1;
+ int j;
+
+ /* how many files are we copying */
+ FILE *fp ;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", file[0], "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ if ( line_count > n_files )
+ g_iTotalFiles = line_count;
+ }
+
+ for (j = 0; j < iFiles; j++)
+ {
+ /* call du -s for each file */
+ fp = spawn("du", (char *[]){ "du", "-s", file[j], NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ }
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
if (target_directory)
{
/* cp file1...filen edir
@@ -822,6 +902,56 @@
ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, nullptr);
}
+ /* BEGIN progress mod */
+ if (progress) {
+ /* remove everything */
+ int i;
+ if ( g_iTotalFiles > 1 )
+ {
+ for ( i = 0; i < 6; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[6A" );
+ }
+ else
+ {
+ for ( i = 0; i < 3; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* good-bye message */
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) copied in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ /* END progress mod */
+
return ok;
}
@@ -857,6 +987,7 @@
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
+ x->progress_bar = false;
x->set_mode = false;
x->mode = 0;
@@ -996,7 +1127,8 @@
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
- while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ /* BEGIN and END progress mod - remove the g in the next line!*/
+ while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:TZ",
long_opts, nullptr))
!= -1)
{
@@ -1057,6 +1189,10 @@
x.unlink_dest_after_failed_open = true;
break;
+ case 'g':
+ progress = true;
+ break;
+
case 'H':
x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
break;
@@ -1223,6 +1359,9 @@
usage (EXIT_FAILURE);
}
+ if (progress)
+ x.reflink_mode = REFLINK_NEVER;
+
x.backup_type = (make_backups
? xget_version (_("backup type"),
version_control_string)
diff -Naur coreutils-9.7/src/mv.c coreutils-9.7k/src/mv.c
--- coreutils-9.7/src/mv.c 2025-01-07 14:31:45.000000000 -0300
+++ coreutils-9.7k/src/mv.c 2026-01-17 13:17:40.210002773 -0300
@@ -80,6 +80,7 @@
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
{"verbose", no_argument, nullptr, 'v'},
+ {"progress-bar", no_argument, nullptr, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{nullptr, 0, nullptr, 0}
@@ -171,9 +172,130 @@
{
bool copy_into_self;
bool rename_succeeded;
+
+ /* BEGIN progress mod */
+ struct timeval start_time;
+ struct stat sb;
+
+ if (progress && x->rename_errno != 0) {
+ if (g_iTotalSize == 0)
+ g_iTotalSize = 0;
+ if (g_iTotalFiles == 0)
+ g_iTotalFiles = 0;
+ if (g_iFilesCopied == 0)
+ g_iFilesCopied = 0;
+ if (g_iDirectoriesCopied == 0)
+ g_iDirectoriesCopied = 0;
+ if (g_iTotalWritten == 0)
+ g_iTotalWritten = 0;
+
+ if (target_directory_operand (source, &sb))
+ g_iDirectoriesCopied++;
+
+ gettimeofday (& start_time, NULL);
+ g_oStartTime = start_time;
+
+ /* how many files are we copying */
+ FILE *fp;
+ char output[1024];
+ fp = spawn("find", (char *[]){ "find", (char *)source, "-type", "f", NULL });
+ if ( fp == NULL)
+ printf("failed to run find\r");
+ else
+ {
+ char *line_buf = NULL;
+ size_t line_buf_size = 0;
+ int line_count = 0;
+ ssize_t line_size;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ while (line_size > 0)
+ {
+ line_count++;
+ line_size = getline(&line_buf, &line_buf_size, fp);
+ }
+ free (line_buf);
+ g_iTotalFiles = line_count;
+ }
+ /* close */
+ pclose(fp);
+
+ printf ("calculating total size... \r");
+ fflush (stdout);
+ long iTotalSize = 0;
+ /* call du -s for source */
+ fp = spawn("du", (char *[]){ "du", "-s", (unsigned char *)(size_t)source, NULL });
+ if (fp == NULL || fgets(output, sizeof(output)-1, fp) == NULL) {
+ printf("failed to run du\r" );
+ }
+ else
+ {
+ /* isolate size */
+ strchr ( output, '\t' )[0] = '\0';
+ iTotalSize += atol ( output );
+ printf ( "calculating total size... %ld\r", iTotalSize );
+ fflush ( stdout );
+ }
+
+ /* close */
+ pclose(fp);
+ g_iTotalSize += iTotalSize;
+ }
+ /* END progress mod */
+
bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
&copy_into_self, &rename_succeeded);
+ /* BEGIN progress mod */
+ if (progress && (x->rename_errno != 0 && ok)) {
+ /* remove everything */
+ int i;
+ int limit = (g_iTotalFiles > 1 ? 6 : 3);
+ if (!rename_succeeded)
+ {
+ for ( i = 0; i < limit; i++ )
+ printf ( "\033[K\n" );
+ printf ( "\r\033[3A" );
+ }
+
+ /* save time */
+ struct timeval end_time;
+ gettimeofday ( & end_time, NULL );
+ int usec_elapsed = end_time.tv_usec - start_time.tv_usec;
+ double sec_elapsed = ( double ) usec_elapsed / 1000000.f;
+ sec_elapsed += ( double ) ( end_time.tv_sec - start_time.tv_sec );
+
+ /* get total size */
+ char sTotalWritten[20];
+ file_size_format ( sTotalWritten, g_iTotalSize, 1 );
+ /* TODO: using g_iTotalWritten would be more correct, but is less accurate */
+
+ /* calculate speed */
+ int copy_speed = ( int ) ( ( double ) g_iTotalWritten / sec_elapsed );
+ char s_copy_speed[20];
+ file_size_format ( s_copy_speed, copy_speed, 1 );
+
+ /* increase counter */
+ g_iFilesCopied++;
+
+ /* good-bye message */
+ if ( x->last_file )
+ {
+ char sFType[20];
+ if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied == g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)" );
+ else if ( g_iDirectoriesCopied > 0 && g_iDirectoriesCopied < g_iFilesCopied )
+ sprintf ( sFType, "%s", "folder(s)/file(s)" );
+ else
+ sprintf ( sFType, "%s", "file(s)" );
+
+ char f_time[20];
+ format_time(f_time, sec_elapsed, false);
+ printf ( "%d %s (%s) moved in %s (%s/s).\n", g_iFilesCopied, sFType,
+ sTotalWritten, f_time, s_copy_speed );
+ }
+ }
+ /* END progress mod */
+
if (ok)
{
char const *dir_to_remove;
@@ -279,6 +401,7 @@
"), stdout);
fputs (_("\
-f, --force do not prompt before overwriting\n\
+ -g, --progress-bar add progress-bar\n\
-i, --interactive prompt before overwrite\n\
-n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
@@ -341,7 +464,7 @@
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, nullptr))
+ while ((c = getopt_long (argc, argv, "bfint:uvgS:TZ", long_options, nullptr))
!= -1)
{
switch (c)
@@ -354,6 +477,9 @@
case 'f':
x.interactive = I_ALWAYS_YES;
break;
+ case 'g':
+ progress = true;
+ break;
case 'i':
x.interactive = I_ASK_USER;
break;
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
set -e
ADVCPMV_VERSION=${1:-0.9}
CORE_UTILS_VERSION=${2:-9.11}
curl -LO https://ftp.gnu.org/gnu/coreutils/coreutils-$CORE_UTILS_VERSION.tar.xz
tar xvJf coreutils-$CORE_UTILS_VERSION.tar.xz
rm coreutils-$CORE_UTILS_VERSION.tar.xz
(
cd coreutils-$CORE_UTILS_VERSION/
curl -LO https://raw.githubusercontent.com/jarun/advcpmv/master/advcpmv-$ADVCPMV_VERSION-$CORE_UTILS_VERSION.patch
patch -p1 -i advcpmv-$ADVCPMV_VERSION-$CORE_UTILS_VERSION.patch
./configure
make
cp ./src/cp ../advcp
cp ./src/mv ../advmv
)
rm -rf coreutils-$CORE_UTILS_VERSION