? slashpatch.diff
? src/[
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. miipekk@ihme.org */
+
 #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,19 @@ 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;
+#ifdef TIOCGWINSZ
+  struct winsize ws;
+#endif
 
   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,113 @@ 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
+	  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;
