Annotation of /trunk/coreutils/patches-5.94/coreutils-5.94-gen-progress-bar.patch
Parent Directory | Revision Log
Revision 115 -
(hide annotations)
(download)
Sun Mar 18 15:57:37 2007 UTC (17 years, 6 months ago) by niro
File size: 12890 byte(s)
Sun Mar 18 15:57:37 2007 UTC (17 years, 6 months ago) by niro
File size: 12890 byte(s)
patches for 5.94
1 | niro | 115 | Upstream has been contacted about this a few times ... |
2 | |||
3 | they dont want progress bars in mv/cp: | ||
4 | http://lists.gnu.org/archive/html/bug-coreutils/2003-08/msg00114.html | ||
5 | http://lists.gnu.org/archive/html/bug-coreutils/2003-09/msg00083.html | ||
6 | http://lists.gnu.org/archive/html/bug-coreutils/2003-09/msg00084.html | ||
7 | |||
8 | but they don't seem to mind a general util ... add this to future patchset: | ||
9 | http://lists.gnu.org/archive/html/bug-coreutils/2003-09/msg00101.html | ||
10 | http://lists.gnu.org/archive/html/bug-coreutils/2004-02/msg00071.html | ||
11 | |||
12 | --- coreutils/src/copy.c | ||
13 | +++ coreutils/src/copy.c | ||
14 | @@ -17,6 +17,8 @@ | ||
15 | |||
16 | /* Extracted from cp.c and librarified by Jim Meyering. */ | ||
17 | |||
18 | +/* Progress bar support added by Miika Pekkarinen. miipekk@ihme.org */ | ||
19 | + | ||
20 | #include <config.h> | ||
21 | #include <stdio.h> | ||
22 | #include <assert.h> | ||
23 | @@ -29,6 +31,10 @@ | ||
24 | # include <priv.h> | ||
25 | #endif | ||
26 | |||
27 | +#ifdef GWINSZ_IN_SYS_IOCTL | ||
28 | +# include <sys/ioctl.h> | ||
29 | +#endif | ||
30 | + | ||
31 | #include "system.h" | ||
32 | #include "backupfile.h" | ||
33 | #include "copy.h" | ||
34 | @@ -50,6 +56,7 @@ | ||
35 | #include "utimens.h" | ||
36 | #include "xreadlink.h" | ||
37 | #include "yesno.h" | ||
38 | +#include "xstrtol.h" | ||
39 | |||
40 | #ifndef HAVE_FCHMOD | ||
41 | # define HAVE_FCHMOD false | ||
42 | @@ -85,6 +92,8 @@ struct F_triple | ||
43 | /* Initial size of the above hash table. */ | ||
44 | #define DEST_INFO_INITIAL_CAPACITY 61 | ||
45 | |||
46 | +#define SAMPLE_MAX 10 | ||
47 | + | ||
48 | static bool copy_internal (char const *src_name, char const *dst_name, | ||
49 | bool new_dst, dev_t device, | ||
50 | struct dir_list *ancestors, | ||
51 | @@ -191,6 +200,62 @@ copy_dir (char const *src_name_in, char | ||
52 | return ok; | ||
53 | } | ||
54 | |||
55 | +/* Shorten a string '/long path/long file' to 'long fi...' | ||
56 | + Also adds padding bytes to end of the string if necessary */ | ||
57 | +char *shorten_name(const char *str, size_t max_width) | ||
58 | +{ | ||
59 | + char *shortname; | ||
60 | + char *filename = (char *) (rindex(str, '/')); | ||
61 | + size_t len; | ||
62 | + | ||
63 | + if (filename == NULL) | ||
64 | + { | ||
65 | + filename = (char *) str; | ||
66 | + } | ||
67 | + else | ||
68 | + { | ||
69 | + filename = (char *) &filename[1]; | ||
70 | + } | ||
71 | + | ||
72 | + len = strlen(filename); | ||
73 | + shortname = (char *) xmalloc (max_width + 1); | ||
74 | + strncpy (shortname, filename, max_width); | ||
75 | + shortname[max_width] = '\0'; | ||
76 | + if (len > max_width) | ||
77 | + { | ||
78 | + memset(&shortname[max_width - 3], '.', 3); | ||
79 | + } | ||
80 | + else | ||
81 | + { | ||
82 | + memset(&shortname[len], ' ', max_width - len); | ||
83 | + } | ||
84 | + | ||
85 | + return shortname; | ||
86 | +} | ||
87 | + | ||
88 | +char *si_units(off_t size) | ||
89 | +{ | ||
90 | + const static int buf_size = 20; | ||
91 | + char *buf; | ||
92 | + static char *unit_array[] = { "B", "KiB", "MiB", "GiB", "" }; | ||
93 | + size_t i; | ||
94 | + | ||
95 | + buf = xmalloc(20); | ||
96 | + for (i = 0; size > 10000; i++) | ||
97 | + { | ||
98 | + if (unit_array[i][0] == '\0') | ||
99 | + { | ||
100 | + i--; | ||
101 | + break; | ||
102 | + } | ||
103 | + size /= 1024; | ||
104 | + } | ||
105 | + | ||
106 | + snprintf (buf, buf_size, "%lu %s", (unsigned long)size, unit_array[i]); | ||
107 | + | ||
108 | + return buf; | ||
109 | +} | ||
110 | + | ||
111 | /* Copy a regular file from SRC_NAME to DST_NAME. | ||
112 | If the source file contains holes, copies holes and blocks of zeros | ||
113 | in the source file as holes in the destination file. | ||
114 | @@ -222,6 +287,19 @@ copy_reg (char const *src_name, char con | ||
115 | off_t n_read_total = 0; | ||
116 | bool last_write_made_hole = false; | ||
117 | bool make_holes = false; | ||
118 | + time_t t_start; | ||
119 | + time_t t_last; | ||
120 | + time_t t_now; | ||
121 | + off_t last_bytes = 0; | ||
122 | + int progress_bar_printed = 0; | ||
123 | + char *shortname = NULL; | ||
124 | + off_t sample_window[SAMPLE_MAX]; | ||
125 | + off_t sample_sum = 0; | ||
126 | + int sample_count = 0; | ||
127 | + long int line_length = 0; | ||
128 | +#ifdef TIOCGWINSZ | ||
129 | + struct winsize ws; | ||
130 | +#endif | ||
131 | |||
132 | source_desc = open (src_name, O_RDONLY | O_BINARY); | ||
133 | if (source_desc < 0) | ||
134 | @@ -326,6 +404,9 @@ copy_reg (char const *src_name, char con | ||
135 | buf = alloca (buf_size + sizeof (int) + buf_alignment - 1); | ||
136 | buf = ptr_align (buf, buf_alignment); | ||
137 | |||
138 | + time (&t_start); | ||
139 | + t_last = t_start; | ||
140 | + | ||
141 | for (;;) | ||
142 | { | ||
143 | ssize_t n_read = read (source_desc, buf, buf_size); | ||
144 | @@ -390,6 +471,113 @@ copy_reg (char const *src_name, char con | ||
145 | } | ||
146 | last_write_made_hole = false; | ||
147 | } | ||
148 | + | ||
149 | + time (&t_now); | ||
150 | + | ||
151 | + /* Progress bar stuff */ | ||
152 | + if (! x->pbar_show || t_now - t_start < x->pbar_delay) | ||
153 | + { | ||
154 | + continue; | ||
155 | + } | ||
156 | + | ||
157 | + if (! progress_bar_printed) | ||
158 | + { | ||
159 | + /* Column width check code copied from ls.c */ | ||
160 | + char const *p = getenv ("COLUMNS"); | ||
161 | + if (p && *p) | ||
162 | + { | ||
163 | + long int tmp_long; | ||
164 | + if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK | ||
165 | + && 0 < tmp_long && tmp_long <= INT_MAX) | ||
166 | + { | ||
167 | + line_length = tmp_long; | ||
168 | + } | ||
169 | + else | ||
170 | + { | ||
171 | + error (0, 0, | ||
172 | + _("ignoring invalid width in environment \ | ||
173 | + variable COLUMNS: %s"), | ||
174 | + quotearg (p)); | ||
175 | + } | ||
176 | + } | ||
177 | + | ||
178 | +#ifdef TIOCGWINSZ | ||
179 | + if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0) | ||
180 | + { | ||
181 | + line_length = ws.ws_col; | ||
182 | + } | ||
183 | +#endif | ||
184 | + if (line_length < 50) | ||
185 | + { | ||
186 | + continue; | ||
187 | + } | ||
188 | + | ||
189 | + /* Take a short filename for progress bar */ | ||
190 | + shortname = shorten_name(src_name, line_length - 48); | ||
191 | + progress_bar_printed = 1; | ||
192 | + } | ||
193 | + | ||
194 | + if (t_now == t_last) | ||
195 | + { | ||
196 | + continue; | ||
197 | + } | ||
198 | + | ||
199 | + if (sample_count == SAMPLE_MAX) | ||
200 | + { | ||
201 | + int i; | ||
202 | + | ||
203 | + sample_sum -= sample_window[0]; | ||
204 | + for (i = 0; i < SAMPLE_MAX - 1; i++) | ||
205 | + { | ||
206 | + sample_window[i] = sample_window[i + 1]; | ||
207 | + } | ||
208 | + } | ||
209 | + else | ||
210 | + { | ||
211 | + sample_count++; | ||
212 | + } | ||
213 | + | ||
214 | + { | ||
215 | + char *str_size; | ||
216 | + char *str_speed; | ||
217 | + char etabuf[64]; | ||
218 | + time_t t_temp; | ||
219 | + | ||
220 | + sample_window[sample_count - 1] = (n_read_total - last_bytes) / | ||
221 | + (t_now - t_last); | ||
222 | + sample_sum += sample_window[sample_count - 1]; | ||
223 | + | ||
224 | + /* Calculate the remaining time */ | ||
225 | + t_temp = (src_open_sb.st_size - n_read_total) / (sample_sum / sample_count); | ||
226 | + | ||
227 | + /* Don't print the progress bar if the estimated remaining | ||
228 | + time is low. */ | ||
229 | + if (progress_bar_printed == 1 && t_temp < x->pbar_min_est) | ||
230 | + { | ||
231 | + continue; | ||
232 | + } | ||
233 | + progress_bar_printed = 2; | ||
234 | + | ||
235 | + str_size = si_units(src_open_sb.st_size); | ||
236 | + str_speed = si_units(sample_sum / sample_count); | ||
237 | + | ||
238 | + strftime(etabuf, sizeof etabuf, "%H:%M.%S", | ||
239 | + gmtime(&t_temp)); | ||
240 | + printf (_("%s | %3lu%% | %9s | %9s/s | ETA %s\r"), shortname, | ||
241 | + (unsigned long)(n_read_total * 100 / src_open_sb.st_size), | ||
242 | + str_size, str_speed, etabuf); | ||
243 | + fflush (stdout); | ||
244 | + free(str_size); | ||
245 | + free(str_speed); | ||
246 | + t_last = t_now; | ||
247 | + last_bytes = n_read_total; | ||
248 | + } | ||
249 | + } | ||
250 | + | ||
251 | + /* Print a newline if progress bar is enabled and has been shown */ | ||
252 | + if (progress_bar_printed == 2) | ||
253 | + { | ||
254 | + printf ("%s | 100%%\n", shortname); | ||
255 | } | ||
256 | |||
257 | /* If the file ends with a `hole', something needs to be written at | ||
258 | @@ -488,6 +676,11 @@ close_src_desc: | ||
259 | return_val = false; | ||
260 | } | ||
261 | |||
262 | + if (shortname != NULL) | ||
263 | + { | ||
264 | + free (shortname); | ||
265 | + } | ||
266 | + | ||
267 | return return_val; | ||
268 | } | ||
269 | |||
270 | --- coreutils/src/copy.h | ||
271 | +++ coreutils/src/copy.h | ||
272 | @@ -175,6 +175,16 @@ struct cp_options | ||
273 | /* If true, display the names of the files before copying them. */ | ||
274 | bool verbose; | ||
275 | |||
276 | + /* If true, display a progress bar when the following conditions are | ||
277 | + * met: | ||
278 | + - pbar_delay defines how many seconds to wait before considering to | ||
279 | + display the progress bar | ||
280 | + - pbar_min_est defines how many seconds estimated operation complete | ||
281 | + time should be at least to show the progress bar. */ | ||
282 | + bool pbar_show; | ||
283 | + int pbar_delay; | ||
284 | + int pbar_min_est; | ||
285 | + | ||
286 | /* If true, stdin is a tty. */ | ||
287 | bool stdin_tty; | ||
288 | |||
289 | --- coreutils/src/cp.c | ||
290 | +++ coreutils/src/cp.c | ||
291 | @@ -81,6 +81,14 @@ enum | ||
292 | /* Initial number of entries in the inode hash table. */ | ||
293 | #define INITIAL_ENTRY_TAB_SIZE 70 | ||
294 | |||
295 | +/* Initial settings for progress bar when it's enabled. | ||
296 | + PROGRESS_DELAY defines how many seconds to wait before even | ||
297 | + considering to display a proggress bar. | ||
298 | + PROGRESS_MIN_EST defines how many seconds estimated operation | ||
299 | + complete time should be at least to show the progress bar. */ | ||
300 | +#define PROGRESS_DELAY 5 | ||
301 | +#define PROGRESS_MIN_EST 5 | ||
302 | + | ||
303 | /* The invocation name of this program. */ | ||
304 | char *program_name; | ||
305 | |||
306 | @@ -120,6 +128,7 @@ static struct option const long_opts[] = | ||
307 | {"copy-contents", no_argument, NULL, COPY_CONTENTS_OPTION}, | ||
308 | {"dereference", no_argument, NULL, 'L'}, | ||
309 | {"force", no_argument, NULL, 'f'}, | ||
310 | + {"progress", no_argument, NULL, 'g'}, | ||
311 | {"interactive", no_argument, NULL, 'i'}, | ||
312 | {"link", no_argument, NULL, 'l'}, | ||
313 | {"no-dereference", no_argument, NULL, 'P'}, | ||
314 | @@ -176,6 +185,8 @@ Mandatory arguments to long options are | ||
315 | fputs (_("\ | ||
316 | -f, --force if an existing destination file cannot be\n\ | ||
317 | opened, remove it and try again\n\ | ||
318 | + -g, --progress show a progress bar if operation is going to\n\ | ||
319 | + take a long time\n\ | ||
320 | -i, --interactive prompt before overwrite\n\ | ||
321 | -H follow command-line symbolic links\n\ | ||
322 | "), stdout); | ||
323 | @@ -705,6 +716,11 @@ cp_option_init (struct cp_options *x) | ||
324 | |||
325 | x->update = false; | ||
326 | x->verbose = false; | ||
327 | + | ||
328 | + x->pbar_show = false; | ||
329 | + x->pbar_delay = PROGRESS_DELAY; | ||
330 | + x->pbar_min_est = PROGRESS_MIN_EST; | ||
331 | + | ||
332 | x->dest_info = NULL; | ||
333 | x->src_info = NULL; | ||
334 | } | ||
335 | @@ -811,7 +827,7 @@ main (int argc, char **argv) | ||
336 | we'll actually use backup_suffix_string. */ | ||
337 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); | ||
338 | |||
339 | - while ((c = getopt_long (argc, argv, "abdfHilLprst:uvxPRS:T", | ||
340 | + while ((c = getopt_long (argc, argv, "abdfgHilLprst:uvxPRS:T", | ||
341 | long_opts, NULL)) | ||
342 | != -1) | ||
343 | { | ||
344 | @@ -871,6 +887,10 @@ main (int argc, char **argv) | ||
345 | x.dereference = DEREF_NEVER; | ||
346 | break; | ||
347 | |||
348 | + case 'g': | ||
349 | + x.pbar_show = 1; | ||
350 | + break; | ||
351 | + | ||
352 | case NO_PRESERVE_ATTRIBUTES_OPTION: | ||
353 | decode_preserve_arg (optarg, &x, false); | ||
354 | break; | ||
355 | --- coreutils/src/mv.c | ||
356 | +++ coreutils/src/mv.c | ||
357 | @@ -45,6 +45,14 @@ | ||
358 | /* Initial number of entries in the inode hash table. */ | ||
359 | #define INITIAL_ENTRY_TAB_SIZE 70 | ||
360 | |||
361 | +/* Initial settings for progress bar when it's enabled. | ||
362 | + PROGRESS_DELAY defines how many seconds to wait before even | ||
363 | + considering to display a proggress bar. | ||
364 | + PROGRESS_MIN_EST defines how many seconds estimated operation | ||
365 | + complete time should be at least to show the progress bar. */ | ||
366 | +#define PROGRESS_DELAY 5 | ||
367 | +#define PROGRESS_MIN_EST 5 | ||
368 | + | ||
369 | /* For long options that have no equivalent short option, use a | ||
370 | non-character as a pseudo short option, starting with CHAR_MAX + 1. */ | ||
371 | enum | ||
372 | @@ -75,6 +83,7 @@ static struct option const long_options[ | ||
373 | { | ||
374 | {"backup", optional_argument, NULL, 'b'}, | ||
375 | {"force", no_argument, NULL, 'f'}, | ||
376 | + {"progress", no_argument, NULL, 'g'}, | ||
377 | {"interactive", no_argument, NULL, 'i'}, | ||
378 | {"no-target-directory", no_argument, NULL, 'T'}, | ||
379 | {"reply", required_argument, NULL, REPLY_OPTION}, /* Deprecated 2005-07-03, | ||
380 | @@ -104,6 +113,10 @@ rm_option_init (struct rm_options *x) | ||
381 | |||
382 | x->verbose = false; | ||
383 | |||
384 | + x->pbar_show = false; | ||
385 | + x->pbar_delay = PROGRESS_DELAY; | ||
386 | + x->pbar_min_est = PROGRESS_MIN_EST; | ||
387 | + | ||
388 | /* Since this program may well have to process additional command | ||
389 | line arguments after any call to `rm', that function must preserve | ||
390 | the initial working directory, in case one of those is a | ||
391 | @@ -312,6 +325,8 @@ Mandatory arguments to long options are | ||
392 | --backup[=CONTROL] make a backup of each existing destination file\n\ | ||
393 | -b like --backup but does not accept an argument\n\ | ||
394 | -f, --force do not prompt before overwriting\n\ | ||
395 | + -g, --progress show a progress bar if operation is going to\n\ | ||
396 | + take a long time\n\ | ||
397 | -i, --interactive prompt before overwrite\n\ | ||
398 | "), stdout); | ||
399 | fputs (_("\ | ||
400 | @@ -375,7 +393,7 @@ main (int argc, char **argv) | ||
401 | we'll actually use backup_suffix_string. */ | ||
402 | backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); | ||
403 | |||
404 | - while ((c = getopt_long (argc, argv, "bfit:uvS:T", long_options, NULL)) | ||
405 | + while ((c = getopt_long (argc, argv, "bfgit:uvS:T", long_options, NULL)) | ||
406 | != -1) | ||
407 | { | ||
408 | switch (c) | ||
409 | @@ -388,6 +406,9 @@ main (int argc, char **argv) | ||
410 | case 'f': | ||
411 | x.interactive = I_ALWAYS_YES; | ||
412 | break; | ||
413 | + case 'g': | ||
414 | + x.pbar_show = 1; | ||
415 | + break; | ||
416 | case 'i': | ||
417 | x.interactive = I_ASK_USER; | ||
418 | break; | ||
419 | --- coreutils/src/remove.h | ||
420 | +++ coreutils/src/remove.h | ||
421 | @@ -48,6 +48,16 @@ struct rm_options | ||
422 | /* If true, display the name of each file removed. */ | ||
423 | bool verbose; | ||
424 | |||
425 | + /* If true, display a progress bar when the following conditions are | ||
426 | + * met: | ||
427 | + - pbar_delay defines how many seconds to wait before considering to | ||
428 | + display the progress bar | ||
429 | + - pbar_min_est defines how many seconds estimated operation complete | ||
430 | + time should be at least to show the progress bar. */ | ||
431 | + bool pbar_show; | ||
432 | + int pbar_delay; | ||
433 | + int pbar_min_est; | ||
434 | + | ||
435 | /* If true, treat the failure by the rm function to restore the | ||
436 | current working directory as a fatal error. I.e., if this field | ||
437 | is true and the rm function cannot restore cwd, it must exit with |