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: - - - [EMAIL PROTECTED] 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 <[EMAIL PROTECTED]> _______________________________________________ Bug-coreutils mailing list [EMAIL PROTECTED] http://mail.gnu.org/mailman/listinfo/bug-coreutils