--- coreutils-8.4/src/copy.c 2010-01-03 17:06:20.000000000 +0000 +++ coreutils-8.4/src/copy.c 2010-04-04 20:39:12.000000000 +0100 @@ -16,6 +16,8 @@ /* Extracted from cp.c and librarified by Jim Meyering. */ +/* Progress bar support added by Miika Pekkarinen. miipekk@ihme.org */ + #include #include #include @@ -29,6 +31,10 @@ # include #endif +#ifdef GWINSZ_IN_SYS_IOCTL +# include +#endif + #include "system.h" #include "acl.h" #include "backupfile.h" @@ -53,6 +59,9 @@ #include "write-any-file.h" #include "areadlink.h" #include "yesno.h" +#include "xstrtol.h" +#include "human.h" +#include "quotearg.h" #if USE_XATTR # include @@ -103,6 +112,8 @@ /* Initial size of the cp.dest_info hash table. */ #define DEST_INFO_INITIAL_CAPACITY 61 +#define SAMPLE_MAX 10 + static bool copy_internal (char const *src_name, char const *dst_name, bool new_dst, dev_t device, struct dir_list *ancestors, @@ -457,6 +468,31 @@ return lchmod (name, mode); } +/* Shorten a string '/long path/long file' to 'long fi...' + Also adds padding bytes to end of the string if necessary */ +char *shorten_name(const char *str, size_t max_width) +{ + char *shortname; + const char *filename; + size_t len; + + filename = base_name (str); + 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; +} + /* Copy a regular file from SRC_NAME to DST_NAME. If the source file contains holes, copies holes and blocks of zeros in the source file as holes in the destination file. @@ -486,6 +522,19 @@ struct stat src_open_sb; bool return_val = true; bool data_copy_required = true; + time_t t_start; + time_t t_last; + time_t t_now; + off_t 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; + long int line_length = 0; +#ifdef TIOCGWINSZ + struct winsize ws; +#endif source_desc = open (src_name, (O_RDONLY | O_BINARY @@ -706,6 +755,9 @@ buf_alloc = xmalloc (buf_size + buf_alignment_slop); buf = ptr_align (buf_alloc, buf_alignment); + time (&t_start); + t_last = t_start; + for (;;) { word *wp = NULL; @@ -787,6 +839,110 @@ file. Unfortunately that doesn't work for certain files in /proc with linux kernels from at least 2.6.9 .. 2.6.29. */ } + + 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 = tmp_long; + } + else + { + error (0, 0, + _("ignoring invalid width in environment \ + variable COLUMNS: %s"), + quotearg (p)); + } + } + +#ifdef TIOCGWINSZ + 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_name(src_name, 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[LONGEST_HUMAN_READABLE + 1]; + char str_speed[LONGEST_HUMAN_READABLE + 1]; + 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; + + strftime(etabuf, sizeof etabuf, "%H:%M.%S", + gmtime(&t_temp)); + printf (_("%s | %3lu%% | %9s | %9s/s | ETA %s\r"), shortname, + (unsigned long)(n_read_total * 100 / src_open_sb.st_size), + human_readable(src_open_sb.st_size, str_size, human_autoscale|human_base_1024|human_space_before_unit|human_SI|human_B, 1, 1), + human_readable(sample_sum / sample_count, str_speed, human_autoscale|human_base_1024|human_space_before_unit|human_SI|human_B, 1, 1), + etabuf); + fflush (stdout); + 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', we need to do something to record @@ -900,6 +1056,11 @@ return_val = false; } + if (shortname != NULL) + { + free (shortname); + } + free (buf_alloc); free (name_alloc); return return_val; --- coreutils-8.4/src/copy.h 2010-01-03 17:06:20.000000000 +0000 +++ coreutils-8.4/src/copy.h 2010-04-04 20:44:54.000000000 +0100 @@ -228,7 +228,17 @@ /* If true, display the names of the files before copying them. */ bool verbose; - + + /* If true, display a progress bar when the following conditions are + * met: + - pbar_delay defines how many seconds to wait before considering to + display the progress bar + - pbar_min_est defines how many seconds estimated operation complete + time should be at least to show the progress bar. */ + bool pbar_show; + int pbar_delay; + int pbar_min_est; + /* If true, stdin is a tty. */ bool stdin_tty; --- coreutils-8.4/src/cp.c 2010-01-04 15:46:06.000000000 +0000 +++ coreutils-8.4/src/cp.c 2010-04-04 20:51:44.000000000 +0100 @@ -49,6 +49,14 @@ } \ while (0) +/* 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 official name of this program (e.g., no `g' prefix). */ #define PROGRAM_NAME "cp" @@ -119,6 +127,7 @@ {"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-clobber", no_argument, NULL, 'n'}, @@ -176,6 +185,8 @@ -f, --force if an existing destination file cannot be\n\ opened, remove it and try again (redundant if\n\ the -n option is used)\n\ + -g, --progress show a progress bar if operation is going to\n\ + take a long time\n\ -i, --interactive prompt before overwrite (overrides a previous -n\n\ option)\n\ -H follow command-line symbolic links in SOURCE\n\ @@ -793,6 +804,10 @@ x->update = false; x->verbose = false; + + x->pbar_show = false; + x->pbar_delay = PROGRESS_DELAY; + x->pbar_min_est = PROGRESS_MIN_EST; /* By default, refuse to open a dangling destination symlink, because in general one cannot do that safely, give the current semantics of @@ -923,7 +938,7 @@ we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:T", + while ((c = getopt_long (argc, argv, "abdfgHilLnprst:uvxPRS:T", long_opts, NULL)) != -1) { @@ -975,6 +990,10 @@ x.unlink_dest_after_failed_open = true; break; + case 'g': + x.pbar_show = true; + break; + case 'H': x.dereference = DEREF_COMMAND_LINE_ARGUMENTS; break; --- coreutils-8.4/src/mv.c 2010-01-03 17:06:20.000000000 +0000 +++ coreutils-8.4/src/mv.c 2010-04-04 20:55:58.000000000 +0100 @@ -34,6 +34,14 @@ #include "root-dev-ino.h" #include "priv-set.h" +/* 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 official name of this program (e.g., no `g' prefix). */ #define PROGRAM_NAME "mv" @@ -56,6 +64,7 @@ { {"backup", optional_argument, NULL, 'b'}, {"force", no_argument, NULL, 'f'}, + {"progress", no_argument, NULL, 'g'}, {"interactive", no_argument, NULL, 'i'}, {"no-clobber", no_argument, NULL, 'n'}, {"no-target-directory", no_argument, NULL, 'T'}, @@ -83,6 +92,10 @@ x->verbose = false; + x->pbar_show = false; + x->pbar_delay = PROGRESS_DELAY; + x->pbar_min_est = PROGRESS_MIN_EST; + /* Since this program may well have to process additional command line arguments after any call to `rm', that function must preserve the initial working directory, in case one of those is a @@ -133,6 +146,11 @@ x->open_dangling_dest_symlink = false; x->update = false; x->verbose = false; + + x->pbar_show = false; + x->pbar_delay = PROGRESS_DELAY; + x->pbar_min_est = PROGRESS_MIN_EST; + x->dest_info = NULL; x->src_info = NULL; } @@ -298,6 +316,8 @@ --backup[=CONTROL] make a backup of each existing destination file\n\ -b like --backup but does not accept an argument\n\ -f, --force do not prompt before overwriting\n\ + -g, --progress show a progress bar if operation is going to\n\ + take a long time\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\ @@ -366,7 +386,7 @@ we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - while ((c = getopt_long (argc, argv, "bfint:uvS:T", long_options, NULL)) + while ((c = getopt_long (argc, argv, "bfgint:uvS:T", long_options, NULL)) != -1) { switch (c) @@ -379,6 +399,9 @@ case 'f': x.interactive = I_ALWAYS_YES; break; + case 'g': + x.pbar_show = true; + break; case 'i': x.interactive = I_ASK_USER; break; --- coreutils-8.4/src/remove.h 2010-01-03 17:06:20.000000000 +0000 +++ coreutils-8.4/src/remove.h 2010-04-04 20:56:43.000000000 +0100 @@ -58,6 +58,16 @@ /* If true, display the name of each file removed. */ bool verbose; + + /* If true, display a progress bar when the following conditions are + * met: + - pbar_delay defines how many seconds to wait before considering to + display the progress bar + - pbar_min_est defines how many seconds estimated operation complete + time should be at least to show the progress bar. */ + bool pbar_show; + int pbar_delay; + int pbar_min_est; /* If true, treat the failure by the rm function to restore the current working directory as a fatal error. I.e., if this field