[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[patch] progress bar support to cp and mv
From: |
Miika Pekkarinen |
Subject: |
[patch] progress bar support to cp and mv |
Date: |
Sun, 24 Aug 2003 13:35:35 +0300 |
Hi!
I have made a patch (attached at end of this file) that adds a
progress bar to the file utilities cp and mv. It can be activated with
a '-g' command line switch. The progress bar doesn't show up
immediately but after a certain amount of time which is currently 5s
after operation and estimated complete time is at least 5s.
The progress bar looks like this on a narrow terminal:
- - -
address@hidden coreutils]$ src/cp -vg
~/system_backup-2003-08-24.tar.gz .
/export/home/miipekk/system_backup-2003-08-24.tar.gz' ->
./system_backup-2003-08-24.tar.gz'
system_backup-200... | 9% | 300 MiB | 3750 KiB/s | ETA 00:01.14
- - -
Please send some comments about this patch. I can make all necessary
changes if it's possible to get the patch accepted.
Patch against the current CVS:
Index: src/copy.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/copy.c,v
retrieving revision 1.149
diff -u -3 -p -u -r1.149 copy.c
--- src/copy.c 9 Aug 2003 17:48:41 -0000 1.149
+++ src/copy.c 24 Aug 2003 09:16:11 -0000
@@ -17,6 +17,8 @@
/* Extracted from cp.c and librarified by Jim Meyering. */
+/* Progress bar support added by Miika Pekkarinen. */
+
#include <config.h>
#include <stdio.h>
#include <assert.h>
@@ -26,6 +28,10 @@
# include <hurd.h>
#endif
+#ifdef GWINSZ_IN_SYS_IOCTL
+# include <sys/ioctl.h>
+#endif
+
#include "system.h"
#include "error.h"
#include "backupfile.h"
@@ -42,6 +48,7 @@
#include "same.h"
#include "utimens.h"
#include "xreadlink.h"
+#include "xstrtol.h"
#define DO_CHOWN(Chown, File, New_uid,
New_gid) \
(Chown (File, New_uid, New_gid)
\
@@ -77,6 +84,9 @@ struct F_triple
/* Initial size of the above hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
+/* How many samples to take while calculating average speed */
+#define SAMPLE_MAX 10
+
int euidaccess ();
int yesno ();
@@ -189,6 +199,62 @@ copy_dir (const char *src_path_in, const
return -ret;
}
+/* Shorten a string '/long path/long file' to 'long fi...'
+ Also adds padding bytes to end of the string if necessary */
+char *shorten_path(const char *str, int max_width)
+{
+ char *shortname;
+ char *filename = (char *) (rindex(str, '/'));
+ int len;
+
+ if (filename == NULL)
+ {
+ filename = (char *) str;
+ }
+ else
+ {
+ filename = (char *) &filename[1];
+ }
+
+ len = strlen(filename);
+ shortname = (char *) xmalloc (max_width + 1);
+ strncpy (shortname, filename, max_width);
+ shortname[max_width] = '\0';
+ if (len > max_width)
+ {
+ memset(&shortname[max_width - 3], '.', 3);
+ }
+ else
+ {
+ memset(&shortname[len], ' ', max_width - len);
+ }
+
+ return shortname;
+}
+
+char *si_units(off_t size)
+{
+ const static int buf_size = 20;
+ char *buf;
+ static char *unit_array[] = { "B", "KiB", "MiB", "GiB", "" };
+ int i;
+
+ buf = xmalloc(20);
+ for (i = 0; size > 10000; i++)
+ {
+ if (unit_array[i][0] == '\0')
+ {
+ i--;
+ break;
+ }
+ size /= 1024;
+ }
+
+ snprintf (buf, buf_size, "%lu %s", (unsigned long)size,
unit_array[i]);
+
+ return buf;
+}
+
/* Copy a regular file from SRC_PATH to DST_PATH.
If the source file contains holes, copies holes and blocks of zeros
in the source file as holes in the destination file.
@@ -216,6 +282,16 @@ copy_reg (const char *src_path, const ch
off_t n_read_total = 0;
int last_write_made_hole = 0;
int make_holes = (x->sparse_mode == SPARSE_ALWAYS);
+ time_t t_start;
+ time_t t_last;
+ time_t t_now;
+ int last_bytes = 0;
+ int progress_bar_printed = 0;
+ char *shortname = NULL;
+ off_t sample_window[SAMPLE_MAX];
+ off_t sample_sum = 0;
+ int sample_count = 0;
+ int line_length = 80;
source_desc = open (src_path, O_RDONLY);
if (source_desc < 0)
@@ -313,6 +389,9 @@ copy_reg (const char *src_path, const ch
buf = (char *) alloca (buf_size + sizeof (int));
+ time (&t_start);
+ t_last = t_start;
+
for (;;)
{
ssize_t n_read = read (source_desc, buf, buf_size);
@@ -377,6 +456,115 @@ copy_reg (const char *src_path, const ch
}
last_write_made_hole = 0;
}
+
+ time (&t_now);
+
+ /* Progress bar stuff */
+ if (! x->pbar_show || t_now - t_start < x->pbar_delay)
+ {
+ continue;
+ }
+
+ if (! progress_bar_printed)
+ {
+ /* Column width check code copied from ls.c */
+ char const *p = getenv ("COLUMNS");
+ if (p && *p)
+ {
+ long int tmp_long;
+ if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
+ && 0 < tmp_long && tmp_long <= INT_MAX)
+ {
+ line_length = (int) tmp_long;
+ }
+ else
+ {
+ error (0, 0,
+ _("ignoring invalid width in environment \
+ variable COLUMNS: %s"),
+ quotearg (p));
+ }
+ }
+
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+
+ if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col
!= 0)
+ {
+ line_length = ws.ws_col;
+ }
+#endif
+ if (line_length < 50)
+ {
+ continue;
+ }
+
+ /* Take a short filename for progress bar */
+ shortname = shorten_path(src_path, line_length - 48);
+ progress_bar_printed = 1;
+ }
+
+ if (t_now == t_last)
+ {
+ continue;
+ }
+
+ if (sample_count == SAMPLE_MAX)
+ {
+ int i;
+
+ sample_sum -= sample_window[0];
+ for (i = 0; i < SAMPLE_MAX - 1; i++)
+ {
+ sample_window[i] = sample_window[i + 1];
+ }
+ }
+ else
+ {
+ sample_count++;
+ }
+
+ {
+ char *str_size;
+ char *str_speed;
+ char etabuf[64];
+ time_t t_temp;
+
+ sample_window[sample_count - 1] = (n_read_total - last_bytes) /
+ (t_now - t_last);
+ sample_sum += sample_window[sample_count - 1];
+
+ /* Calculate the remaining time */
+ t_temp = (src_open_sb.st_size - n_read_total) / (sample_sum /
sample_count);
+
+ /* Don't print the progress bar if the estimated remaining
+ time is low. */
+ if (progress_bar_printed == 1 && t_temp < x->pbar_min_est)
+ {
+ continue;
+ }
+ progress_bar_printed = 2;
+
+ str_size = si_units(src_open_sb.st_size);
+ str_speed = si_units(sample_sum / sample_count);
+
+ strftime(etabuf, sizeof etabuf, "%H:%M.%S",
+ gmtime(&t_temp));
+ printf (_("%s | %3d%% | %9s | %9s/s | ETA %s\r"), shortname,
+ (int)(n_read_total * 100 / src_open_sb.st_size),
+ str_size, str_speed, etabuf);
+ fflush (stdout);
+ free(str_size);
+ free(str_speed);
+ t_last = t_now;
+ last_bytes = n_read_total;
+ }
+ }
+
+ /* Print a newline if progress bar is enabled and has been shown */
+ if (progress_bar_printed == 2)
+ {
+ printf ("%s | 100%%\n", shortname);
}
/* If the file ends with a `hole', something needs to be written at
@@ -411,6 +599,11 @@ close_src_desc:
{
error (0, errno, _("closing %s"), quote (src_path));
return_val = -1;
+ }
+
+ if (shortname != NULL)
+ {
+ free (shortname);
}
return return_val;
Index: src/copy.h
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/copy.h,v
retrieving revision 1.27
diff -u -3 -p -u -r1.27 copy.h
--- src/copy.h 26 Mar 2003 18:47:49 -0000 1.27
+++ src/copy.h 24 Aug 2003 09:16:11 -0000
@@ -153,6 +153,15 @@ struct cp_options
/* If nonzero, display the names of the files before copying them. */
int verbose;
+ /* If nonzero, display a progress bar when following conditions are
met:
+ - pbar_delay defines how many seconds to wait before considering
to display
+ the proggress bar.
+ - pbar_min_est defines how many seconds estimated operation
+ complete time should be at least to show the progress bar. */
+ int pbar_show;
+ int pbar_delay;
+ int pbar_min_est;
+
/* A pointer to either lstat or stat, depending on
whether the copy should dereference symlinks. */
int (*xstat) ();
Index: src/cp.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/cp.c,v
retrieving revision 1.183
diff -u -3 -p -u -r1.183 cp.c
--- src/cp.c 9 Aug 2003 17:46:13 -0000 1.183
+++ src/cp.c 24 Aug 2003 09:16:12 -0000
@@ -84,6 +84,14 @@ enum
/* Initial number of entries in the inode hash table. */
#define INITIAL_ENTRY_TAB_SIZE 70
+/* Initial settings for progress bar when it's enabled.
+ PROGRESS_DELAY defines how many seconds to wait before even
+ considering to display a proggress bar.
+ PROGRESS_MIN_EST defines how many seconds estimated operation
+ complete time should be at least to show the progress bar. */
+#define PROGRESS_DELAY 5
+#define PROGRESS_MIN_EST 5
+
/* The invocation name of this program. */
char *program_name;
@@ -126,6 +134,7 @@ static struct option const long_opts[] =
{"copy-contents", no_argument, NULL, COPY_CONTENTS_OPTION},
{"dereference", no_argument, NULL, 'L'},
{"force", no_argument, NULL, 'f'},
+ {"progress", no_argument, NULL, 'g'},
{"interactive", no_argument, NULL, 'i'},
{"link", no_argument, NULL, 'l'},
{"no-dereference", no_argument, NULL, 'P'},
@@ -182,6 +191,8 @@ Mandatory arguments to long options are
--no-dereference never follow symbolic links\n\
-f, --force if an existing destination file cannot
be\n\
opened, remove it and try again\n\
+ -g, --progress show a progress bar if operation is
going to\n\
+ take a long time\n\
-i, --interactive prompt before overwrite\n\
-H follow command-line symbolic links\n\
"), stdout);
@@ -742,6 +753,11 @@ cp_option_init (struct cp_options *x)
x->update = 0;
x->verbose = 0;
+
+ x->pbar_show = 0;
+ x->pbar_delay = PROGRESS_DELAY;
+ x->pbar_min_est = PROGRESS_MIN_EST;
+
x->dest_info = NULL;
x->src_info = NULL;
}
@@ -846,7 +862,7 @@ main (int argc, char **argv)
we'll actually use backup_suffix_string. */
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
- while ((c = getopt_long (argc, argv, "abdfHilLprsuvxPRS:V:",
long_opts, NULL))
+ while ((c = getopt_long (argc, argv, "abdfgHilLprsuvxPRS:V:",
long_opts, NULL))
!= -1)
{
switch (c)
@@ -893,6 +909,10 @@ main (int argc, char **argv)
case 'f':
x.unlink_dest_after_failed_open = 1;
+ break;
+
+ case 'g':
+ x.pbar_show = 1;
break;
case 'H':
Index: src/mv.c
===================================================================
RCS file: /cvsroot/coreutils/coreutils/src/mv.c,v
retrieving revision 1.144
diff -u -3 -p -u -r1.144 mv.c
--- src/mv.c 12 Jul 2003 11:38:43 -0000 1.144
+++ src/mv.c 24 Aug 2003 09:16:12 -0000
@@ -45,6 +45,14 @@
/* Initial number of entries in the inode hash table. */
#define INITIAL_ENTRY_TAB_SIZE 70
+/* Initial settings for progress bar when it's enabled.
+ PROGRESS_DELAY defines how many seconds to wait before even
+ considering to display a proggress bar.
+ PROGRESS_MIN_EST defines how many seconds estimated operation
+ complete time should be at least to show the progress bar. */
+#define PROGRESS_DELAY 5
+#define PROGRESS_MIN_EST 5
+
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1.
*/
enum
@@ -79,6 +87,7 @@ static struct option const long_options[
{
{"backup", optional_argument, NULL, 'b'},
{"force", no_argument, NULL, 'f'},
+ {"progress", no_argument, NULL, 'g'},
{"interactive", no_argument, NULL, 'i'},
{"reply", required_argument, NULL, REPLY_OPTION},
{"strip-trailing-slashes", no_argument, NULL,
STRIP_TRAILING_SLASHES_OPTION},
@@ -141,6 +150,10 @@ cp_option_init (struct cp_options *x)
x->update = 0;
x->verbose = 0;
+ x->pbar_show = 0;
+ x->pbar_delay = PROGRESS_DELAY;
+ x->pbar_min_est = PROGRESS_MIN_EST;
+
x->xstat = lstat;
x->dest_info = NULL;
x->src_info = NULL;
@@ -322,6 +335,8 @@ Mandatory arguments to long options are
-b like --backup but does not accept an
argument\n\
-f, --force do not prompt before overwriting\n\
equivalent to --reply=yes\n\
+ -g, --progress show a progress bar if operation is
going to\n\
+ take a long time\n\
-i, --interactive prompt before overwrite\n\
equivalent to --reply=query\n\
"), stdout);
@@ -365,6 +380,9 @@ main (int argc, char **argv)
int c;
int errors;
int make_backups = 0;
+ int pbar_show = 0;
+ int pbar_delay = PROGRESS_DELAY;
+ int pbar_min_est = PROGRESS_MIN_EST;
int dest_is_dir;
char *backup_suffix_string;
char *version_control_string = NULL;
@@ -390,7 +408,7 @@ main (int argc, char **argv)
errors = 0;
- while ((c = getopt_long (argc, argv, "bfiuvS:V:", long_options,
NULL)) != -1)
+ while ((c = getopt_long (argc, argv, "bfgiuvS:V:", long_options,
NULL)) != -1)
{
switch (c)
{
@@ -411,6 +429,9 @@ main (int argc, char **argv)
break;
case 'f':
x.interactive = I_ALWAYS_YES;
+ break;
+ case 'g':
+ x.pbar_show = 1;
break;
case 'i':
x.interactive = I_ASK_USER;
End Of Patch
Regards,
--
Miika Pekkarinen
<address@hidden>
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [patch] progress bar support to cp and mv,
Miika Pekkarinen <=