diff options
author | Eric Andersen <andersen@codepoet.org> | 2000-10-03 00:21:45 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2000-10-03 00:21:45 +0000 |
commit | b520e083e0507f15e05230733a760e52dcd9f133 (patch) | |
tree | a60784dceb176b5b85286bd764ba39ebb985a59a | |
parent | 25b669c144949816d3c9726c79dbd6e93d53ee8b (diff) | |
download | busybox-b520e083e0507f15e05230733a760e52dcd9f133.tar.gz |
Add a spiffy progress meter.
-Erik
-rw-r--r-- | Config.h | 3 | ||||
-rw-r--r-- | networking/wget.c | 251 | ||||
-rw-r--r-- | wget.c | 251 |
3 files changed, 463 insertions, 42 deletions
@@ -263,6 +263,9 @@ // compiled into the binary. (needs /proc filesystem) // #define BB_FEATURE_INSTALLER // +// Enable a nifty progress meter in wget (adds just under 2k) +//#define BB_FEATURE_STATUSBAR +// // Clean up all memory before exiting -- usually not needed // as the OS can clean up... Don't enable this unless you // have a really good reason for cleaning things up manually. diff --git a/networking/wget.c b/networking/wget.c index c95271b0d..02adc5520 100644 --- a/networking/wget.c +++ b/networking/wget.c @@ -5,6 +5,15 @@ * Chip Rosenthal * Covad Communications * <chip@laserlink.net> + * + * Note: According to RFC2616 section 3.6.1, "All HTTP/1.1 applications + * MUST be able to receive and decode the "chunked" transfer-coding, + * and MUST ignore chunk-extension extensions they do not understand." + * This prevents this particular wget app from completely RFC compliant, + * and as such, prevents it from being used as a general purpose web browser... + * + * This is a design decision, since it makes the code smaller. + * */ #include "busybox.h" @@ -13,7 +22,11 @@ #include <unistd.h> #include <ctype.h> #include <string.h> +#include <unistd.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> @@ -25,7 +38,17 @@ void parse_url(char *url, char **uri_host, int *uri_port, char **uri_path); FILE *open_socket(char *host, int port); char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc); - +void progressmeter(int flag); + +/* Globals (can be accessed from signal handlers */ +static off_t filesize = 0; /* content-length of the file */ +#ifdef BB_FEATURE_STATUSBAR +static char *curfile; /* Name of current file being transferred. */ +static struct timeval start; /* Time a transfer started. */ +volatile unsigned long statbytes; /* Number of bytes transferred so far. */ +/* For progressmeter() -- number of seconds before xfer considered "stalled" */ +#define STALLTIME 5 +#endif int wget_main(int argc, char **argv) { @@ -39,7 +62,7 @@ int wget_main(int argc, char **argv) int do_continue = 0; /* continue a prev transfer (-c) */ long beg_range = 0L; /* range at which continue begins */ int got_clen = 0; /* got content-length: from server */ - long clen = 1L; /* the content length */ + FILE *output; /* socket to web server */ /* * Crack command line. @@ -62,12 +85,20 @@ int wget_main(int argc, char **argv) /* Guess an output filename */ if (!fname_out) { - fname_out = get_last_path_component(argv[optind]); + fname_out = +#ifdef BB_FEATURE_STATUSBAR + curfile = +#endif + get_last_path_component(argv[optind]); +#ifdef BB_FEATURE_STATUSBAR + } else { + curfile=argv[optind]; +#endif } + if (do_continue && !fname_out) fatalError("wget: cannot specify continue (-c) without a filename (-O)\n"); - /* * Parse url into components. */ @@ -82,8 +113,11 @@ int wget_main(int argc, char **argv) * Open the output stream. */ if (fname_out != NULL) { - if (freopen(fname_out, (do_continue ? "a" : "w"), stdout) == NULL) + if ( (output=fopen(fname_out, (do_continue ? "a" : "w"))) + == NULL) fatalError("wget: freopen(%s): %s\n", fname_out, strerror(errno)); + } else { + output=stdout; } /* @@ -91,7 +125,7 @@ int wget_main(int argc, char **argv) */ if (do_continue) { struct stat sbuf; - if (fstat(fileno(stdout), &sbuf) < 0) + if (fstat(fileno(output), &sbuf) < 0) fatalError("wget: fstat(): %s\n", strerror(errno)); if (sbuf.st_size > 0) beg_range = sbuf.st_size; @@ -117,16 +151,16 @@ int wget_main(int argc, char **argv) for ( ; isspace(*s) ; ++s) ; switch (atoi(s)) { - case 200: - if (!do_continue) - break; - fatalError("wget: cannot continue - server does not properly support ranges\n"); - case 206: - if (do_continue) - break; - /*FALLTHRU*/ - default: - fatalError("wget: server returned error: %s", buf); + case 200: + if (!do_continue) + break; + fatalError("wget: server does not support ranges\n"); + case 206: + if (do_continue) + break; + /*FALLTHRU*/ + default: + fatalError("wget: server returned error: %s", buf); } /* @@ -134,12 +168,12 @@ int wget_main(int argc, char **argv) */ while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) { if (strcmp(buf, "content-length") == 0) { - clen = atol(s); + filesize = atol(s); got_clen = 1; continue; } if (strcmp(buf, "transfer-encoding") == 0) { - fatalError("wget: i do not do transfer encodings, server wants to do: %s\n", s); + fatalError("wget: server wants to do %s transfer encoding\n", s); continue; } } @@ -147,10 +181,18 @@ int wget_main(int argc, char **argv) /* * Retrieve HTTP body. */ - while (clen > 0 && (n = fread(buf, 1, sizeof(buf), sfp)) > 0) { - fwrite(buf, 1, n, stdout); +#ifdef BB_FEATURE_STATUSBAR + statbytes=0; + progressmeter(-1); +#endif + while (filesize > 0 && (n = fread(buf, 1, sizeof(buf), sfp)) > 0) { + fwrite(buf, 1, n, output); +#ifdef BB_FEATURE_STATUSBAR + statbytes+=n; + progressmeter(1); +#endif if (got_clen) - clen -= n; + filesize -= n; } if (n == 0 && ferror(sfp)) fatalError("wget: network read error: %s", strerror(errno)); @@ -259,6 +301,172 @@ char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc) return hdrval; } +#ifdef BB_FEATURE_STATUSBAR +/* Stuff below is from BSD rcp util.c, as added to openshh. */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: wget.c,v 1.5 2000/10/03 00:21:45 andersen Exp $ + */ + + +int +getttywidth(void) +{ + struct winsize winsize; + + if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) + return (winsize.ws_col ? winsize.ws_col : 80); + else + return (80); +} + +void +updateprogressmeter(int ignore) +{ + int save_errno = errno; + + progressmeter(0); + errno = save_errno; +} + +void +alarmtimer(int wait) +{ + struct itimerval itv; + + itv.it_value.tv_sec = wait; + itv.it_value.tv_usec = 0; + itv.it_interval = itv.it_value; + setitimer(ITIMER_REAL, &itv, NULL); +} + + +void +progressmeter(int flag) +{ + static const char prefixes[] = " KMGTP"; + static struct timeval lastupdate; + static off_t lastsize; + struct timeval now, td, wait; + off_t cursize, abbrevsize; + double elapsed; + int ratio, barlength, i, remaining; + char buf[256]; + + if (flag == -1) { + (void) gettimeofday(&start, (struct timezone *) 0); + lastupdate = start; + lastsize = 0; + } + + (void) gettimeofday(&now, (struct timezone *) 0); + cursize = statbytes; + if (filesize != 0) { + ratio = 100.0 * cursize / filesize; + ratio = MAX(ratio, 0); + ratio = MIN(ratio, 100); + } else + ratio = 100; + + snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); + + barlength = getttywidth() - 51; + if (barlength > 0) { + i = barlength * ratio / 100; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "|%.*s%*s|", i, + "*****************************************************************************" + "*****************************************************************************", + barlength - i, ""); + } + i = 0; + abbrevsize = cursize; + while (abbrevsize >= 100000 && i < sizeof(prefixes)) { + i++; + abbrevsize >>= 10; + } + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5d %c%c ", + (int) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : + 'B'); + + timersub(&now, &lastupdate, &wait); + if (cursize > lastsize) { + lastupdate = now; + lastsize = cursize; + if (wait.tv_sec >= STALLTIME) { + start.tv_sec += wait.tv_sec; + start.tv_usec += wait.tv_usec; + } + wait.tv_sec = 0; + } + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + + if (statbytes <= 0 || elapsed <= 0.0 || cursize > filesize) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " --:-- ETA"); + } else if (wait.tv_sec >= STALLTIME) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " - stalled -"); + } else { + remaining = (int) (filesize / (statbytes / elapsed) - elapsed); + i = remaining / 3600; + if (i) + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%2d:", i); + else + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " "); + i = remaining % 3600; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%02d:%02d ETA", i / 60, i % 60); + } + write(fileno(stdout), buf, strlen(buf)); + + if (flag == -1) { + struct sigaction sa; + sa.sa_handler = updateprogressmeter; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + alarmtimer(1); + } else if (flag == 1) { + alarmtimer(0); + statbytes = 0; + } +} +#endif /* Local Variables: c-file-style: "linux" @@ -266,3 +474,4 @@ c-basic-offset: 4 tab-width: 4 End: */ + @@ -5,6 +5,15 @@ * Chip Rosenthal * Covad Communications * <chip@laserlink.net> + * + * Note: According to RFC2616 section 3.6.1, "All HTTP/1.1 applications + * MUST be able to receive and decode the "chunked" transfer-coding, + * and MUST ignore chunk-extension extensions they do not understand." + * This prevents this particular wget app from completely RFC compliant, + * and as such, prevents it from being used as a general purpose web browser... + * + * This is a design decision, since it makes the code smaller. + * */ #include "busybox.h" @@ -13,7 +22,11 @@ #include <unistd.h> #include <ctype.h> #include <string.h> +#include <unistd.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> @@ -25,7 +38,17 @@ void parse_url(char *url, char **uri_host, int *uri_port, char **uri_path); FILE *open_socket(char *host, int port); char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc); - +void progressmeter(int flag); + +/* Globals (can be accessed from signal handlers */ +static off_t filesize = 0; /* content-length of the file */ +#ifdef BB_FEATURE_STATUSBAR +static char *curfile; /* Name of current file being transferred. */ +static struct timeval start; /* Time a transfer started. */ +volatile unsigned long statbytes; /* Number of bytes transferred so far. */ +/* For progressmeter() -- number of seconds before xfer considered "stalled" */ +#define STALLTIME 5 +#endif int wget_main(int argc, char **argv) { @@ -39,7 +62,7 @@ int wget_main(int argc, char **argv) int do_continue = 0; /* continue a prev transfer (-c) */ long beg_range = 0L; /* range at which continue begins */ int got_clen = 0; /* got content-length: from server */ - long clen = 1L; /* the content length */ + FILE *output; /* socket to web server */ /* * Crack command line. @@ -62,12 +85,20 @@ int wget_main(int argc, char **argv) /* Guess an output filename */ if (!fname_out) { - fname_out = get_last_path_component(argv[optind]); + fname_out = +#ifdef BB_FEATURE_STATUSBAR + curfile = +#endif + get_last_path_component(argv[optind]); +#ifdef BB_FEATURE_STATUSBAR + } else { + curfile=argv[optind]; +#endif } + if (do_continue && !fname_out) fatalError("wget: cannot specify continue (-c) without a filename (-O)\n"); - /* * Parse url into components. */ @@ -82,8 +113,11 @@ int wget_main(int argc, char **argv) * Open the output stream. */ if (fname_out != NULL) { - if (freopen(fname_out, (do_continue ? "a" : "w"), stdout) == NULL) + if ( (output=fopen(fname_out, (do_continue ? "a" : "w"))) + == NULL) fatalError("wget: freopen(%s): %s\n", fname_out, strerror(errno)); + } else { + output=stdout; } /* @@ -91,7 +125,7 @@ int wget_main(int argc, char **argv) */ if (do_continue) { struct stat sbuf; - if (fstat(fileno(stdout), &sbuf) < 0) + if (fstat(fileno(output), &sbuf) < 0) fatalError("wget: fstat(): %s\n", strerror(errno)); if (sbuf.st_size > 0) beg_range = sbuf.st_size; @@ -117,16 +151,16 @@ int wget_main(int argc, char **argv) for ( ; isspace(*s) ; ++s) ; switch (atoi(s)) { - case 200: - if (!do_continue) - break; - fatalError("wget: cannot continue - server does not properly support ranges\n"); - case 206: - if (do_continue) - break; - /*FALLTHRU*/ - default: - fatalError("wget: server returned error: %s", buf); + case 200: + if (!do_continue) + break; + fatalError("wget: server does not support ranges\n"); + case 206: + if (do_continue) + break; + /*FALLTHRU*/ + default: + fatalError("wget: server returned error: %s", buf); } /* @@ -134,12 +168,12 @@ int wget_main(int argc, char **argv) */ while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) { if (strcmp(buf, "content-length") == 0) { - clen = atol(s); + filesize = atol(s); got_clen = 1; continue; } if (strcmp(buf, "transfer-encoding") == 0) { - fatalError("wget: i do not do transfer encodings, server wants to do: %s\n", s); + fatalError("wget: server wants to do %s transfer encoding\n", s); continue; } } @@ -147,10 +181,18 @@ int wget_main(int argc, char **argv) /* * Retrieve HTTP body. */ - while (clen > 0 && (n = fread(buf, 1, sizeof(buf), sfp)) > 0) { - fwrite(buf, 1, n, stdout); +#ifdef BB_FEATURE_STATUSBAR + statbytes=0; + progressmeter(-1); +#endif + while (filesize > 0 && (n = fread(buf, 1, sizeof(buf), sfp)) > 0) { + fwrite(buf, 1, n, output); +#ifdef BB_FEATURE_STATUSBAR + statbytes+=n; + progressmeter(1); +#endif if (got_clen) - clen -= n; + filesize -= n; } if (n == 0 && ferror(sfp)) fatalError("wget: network read error: %s", strerror(errno)); @@ -259,6 +301,172 @@ char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc) return hdrval; } +#ifdef BB_FEATURE_STATUSBAR +/* Stuff below is from BSD rcp util.c, as added to openshh. */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: wget.c,v 1.5 2000/10/03 00:21:45 andersen Exp $ + */ + + +int +getttywidth(void) +{ + struct winsize winsize; + + if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) + return (winsize.ws_col ? winsize.ws_col : 80); + else + return (80); +} + +void +updateprogressmeter(int ignore) +{ + int save_errno = errno; + + progressmeter(0); + errno = save_errno; +} + +void +alarmtimer(int wait) +{ + struct itimerval itv; + + itv.it_value.tv_sec = wait; + itv.it_value.tv_usec = 0; + itv.it_interval = itv.it_value; + setitimer(ITIMER_REAL, &itv, NULL); +} + + +void +progressmeter(int flag) +{ + static const char prefixes[] = " KMGTP"; + static struct timeval lastupdate; + static off_t lastsize; + struct timeval now, td, wait; + off_t cursize, abbrevsize; + double elapsed; + int ratio, barlength, i, remaining; + char buf[256]; + + if (flag == -1) { + (void) gettimeofday(&start, (struct timezone *) 0); + lastupdate = start; + lastsize = 0; + } + + (void) gettimeofday(&now, (struct timezone *) 0); + cursize = statbytes; + if (filesize != 0) { + ratio = 100.0 * cursize / filesize; + ratio = MAX(ratio, 0); + ratio = MIN(ratio, 100); + } else + ratio = 100; + + snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); + + barlength = getttywidth() - 51; + if (barlength > 0) { + i = barlength * ratio / 100; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "|%.*s%*s|", i, + "*****************************************************************************" + "*****************************************************************************", + barlength - i, ""); + } + i = 0; + abbrevsize = cursize; + while (abbrevsize >= 100000 && i < sizeof(prefixes)) { + i++; + abbrevsize >>= 10; + } + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5d %c%c ", + (int) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : + 'B'); + + timersub(&now, &lastupdate, &wait); + if (cursize > lastsize) { + lastupdate = now; + lastsize = cursize; + if (wait.tv_sec >= STALLTIME) { + start.tv_sec += wait.tv_sec; + start.tv_usec += wait.tv_usec; + } + wait.tv_sec = 0; + } + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + + if (statbytes <= 0 || elapsed <= 0.0 || cursize > filesize) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " --:-- ETA"); + } else if (wait.tv_sec >= STALLTIME) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " - stalled -"); + } else { + remaining = (int) (filesize / (statbytes / elapsed) - elapsed); + i = remaining / 3600; + if (i) + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%2d:", i); + else + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + " "); + i = remaining % 3600; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), + "%02d:%02d ETA", i / 60, i % 60); + } + write(fileno(stdout), buf, strlen(buf)); + + if (flag == -1) { + struct sigaction sa; + sa.sa_handler = updateprogressmeter; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + alarmtimer(1); + } else if (flag == 1) { + alarmtimer(0); + statbytes = 0; + } +} +#endif /* Local Variables: c-file-style: "linux" @@ -266,3 +474,4 @@ c-basic-offset: 4 tab-width: 4 End: */ + |