Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/networking/wget.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 815 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 6  Line 6 
6   *   *
7   */   */
8    
9  /* We want libc to give us xxx64 functions also */  #include "libbb.h"
 /* http://www.unix.org/version2/whatsnew/lfs20mar.html */  
 #define _LARGEFILE64_SOURCE 1  
   
 #include "busybox.h"  
 #include <getopt.h> /* for struct option */  
10    
11  struct host_info {  struct host_info {
12   // May be used if we ever will want to free() all xstrdup()s...   // May be used if we ever will want to free() all xstrdup()s...
13   /* char *allocated; */   /* char *allocated; */
14   char *host;   const char *path;
15   int port;   const char *user;
16   char *path;   char       *host;
17   int is_ftp;   int         port;
18   char *user;   smallint    is_ftp;
19  };  };
20    
21  static void parse_url(char *url, struct host_info *h);  
22  static FILE *open_socket(len_and_sockaddr *lsa);  /* Globals (can be accessed from signal handlers) */
23  static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);  struct globals {
24  static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf);   off_t content_len;        /* Content-length of the file */
25     off_t beg_range;          /* Range at which continue begins */
 /* Globals (can be accessed from signal handlers */  
 static off_t content_len;        /* Content-length of the file */  
 static off_t beg_range;          /* Range at which continue begins */  
26  #if ENABLE_FEATURE_WGET_STATUSBAR  #if ENABLE_FEATURE_WGET_STATUSBAR
27  static off_t transferred;        /* Number of bytes transferred so far */   off_t lastsize;
28     off_t totalsize;
29     off_t transferred;        /* Number of bytes transferred so far */
30     const char *curfile;      /* Name of current file being transferred */
31     unsigned lastupdate_sec;
32     unsigned start_sec;
33  #endif  #endif
34  static int chunked;                     /* chunked transfer encoding */   smallint chunked;             /* chunked transfer encoding */
35    };
36    #define G (*(struct globals*)&bb_common_bufsiz1)
37    struct BUG_G_too_big {
38     char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
39    };
40    #define content_len     (G.content_len    )
41    #define beg_range       (G.beg_range      )
42    #define lastsize        (G.lastsize       )
43    #define totalsize       (G.totalsize      )
44    #define transferred     (G.transferred    )
45    #define curfile         (G.curfile        )
46    #define lastupdate_sec  (G.lastupdate_sec )
47    #define start_sec       (G.start_sec      )
48    #define chunked         (G.chunked        )
49    #define INIT_G() do { } while (0)
50    
51    
52  #if ENABLE_FEATURE_WGET_STATUSBAR  #if ENABLE_FEATURE_WGET_STATUSBAR
 static void progressmeter(int flag);  
 static char *curfile;                   /* Name of current file being transferred */  
 static struct timeval start;            /* Time a transfer started */  
53  enum {  enum {
54   STALLTIME = 5                   /* Seconds when xfer considered "stalled" */   STALLTIME = 5                   /* Seconds when xfer considered "stalled" */
55  };  };
56  #else  
57  static void progressmeter(int flag) {}  static unsigned int getttywidth(void)
58    {
59     unsigned width;
60     get_terminal_width_height(0, &width, NULL);
61     return width;
62    }
63    
64    static void progressmeter(int flag)
65    {
66     /* We can be called from signal handler */
67     int save_errno = errno;
68     off_t abbrevsize;
69     unsigned since_last_update, elapsed;
70     unsigned ratio;
71     int barlength, i;
72    
73     if (flag == -1) { /* first call to progressmeter */
74     start_sec = monotonic_sec();
75     lastupdate_sec = start_sec;
76     lastsize = 0;
77     totalsize = content_len + beg_range; /* as content_len changes.. */
78     }
79    
80     ratio = 100;
81     if (totalsize != 0 && !chunked) {
82     /* long long helps to have it working even if !LFS */
83     ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize);
84     if (ratio > 100) ratio = 100;
85     }
86    
87     fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);
88    
89     barlength = getttywidth() - 49;
90     if (barlength > 0) {
91     /* god bless gcc for variable arrays :) */
92     i = barlength * ratio / 100;
93     {
94     char buf[i+1];
95     memset(buf, '*', i);
96     buf[i] = '\0';
97     fprintf(stderr, "|%s%*s|", buf, barlength - i, "");
98     }
99     }
100     i = 0;
101     abbrevsize = transferred + beg_range;
102     while (abbrevsize >= 100000) {
103     i++;
104     abbrevsize >>= 10;
105     }
106     /* see http://en.wikipedia.org/wiki/Tera */
107     fprintf(stderr, "%6d%c ", (int)abbrevsize, " kMGTPEZY"[i]);
108    
109    // Nuts! Ain't it easier to update progress meter ONLY when we transferred++?
110    
111     elapsed = monotonic_sec();
112     since_last_update = elapsed - lastupdate_sec;
113     if (transferred > lastsize) {
114     lastupdate_sec = elapsed;
115     lastsize = transferred;
116     if (since_last_update >= STALLTIME) {
117     /* We "cut off" these seconds from elapsed time
118     * by adjusting start time */
119     start_sec += since_last_update;
120     }
121     since_last_update = 0; /* we are un-stalled now */
122     }
123     elapsed -= start_sec; /* now it's "elapsed since start" */
124    
125     if (since_last_update >= STALLTIME) {
126     fprintf(stderr, " - stalled -");
127     } else {
128     off_t to_download = totalsize - beg_range;
129     if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || chunked) {
130     fprintf(stderr, "--:--:-- ETA");
131     } else {
132     /* to_download / (transferred/elapsed) - elapsed: */
133     int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);
134     /* (long long helps to have working ETA even if !LFS) */
135     i = eta % 3600;
136     fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);
137     }
138     }
139    
140     if (flag == 0) {
141     /* last call to progressmeter */
142     alarm(0);
143     transferred = 0;
144     fputc('\n', stderr);
145     } else {
146     if (flag == -1) { /* first call to progressmeter */
147     signal_SA_RESTART_empty_mask(SIGALRM, progressmeter);
148     }
149     alarm(1);
150     }
151    
152     errno = save_errno;
153    }
154    /* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
155     * much of which was blatantly stolen from openssh.  */
156    /*-
157     * Copyright (c) 1992, 1993
158     * The Regents of the University of California.  All rights reserved.
159     *
160     * Redistribution and use in source and binary forms, with or without
161     * modification, are permitted provided that the following conditions
162     * are met:
163     * 1. Redistributions of source code must retain the above copyright
164     *    notice, this list of conditions and the following disclaimer.
165     * 2. Redistributions in binary form must reproduce the above copyright
166     *    notice, this list of conditions and the following disclaimer in the
167     *    documentation and/or other materials provided with the distribution.
168     *
169     * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
170     * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
171     *
172     * 4. Neither the name of the University nor the names of its contributors
173     *    may be used to endorse or promote products derived from this software
174     *    without specific prior written permission.
175     *
176     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
177     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
178     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
180     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
181     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
182     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
183     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
184     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
185     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
186     * SUCH DAMAGE.
187     *
188     */
189    #else /* FEATURE_WGET_STATUSBAR */
190    
191    static ALWAYS_INLINE void progressmeter(int flag UNUSED_PARAM) { }
192    
193  #endif  #endif
194    
195  /* Read NMEMB elements of SIZE bytes into PTR from STREAM.  Returns the  
196   * number of elements read, and a short count if an eof or non-interrupt  /* Read NMEMB bytes into PTR from STREAM.  Returns the number of bytes read,
197   * error is encountered.  */   * and a short count if an eof or non-interrupt error is encountered.  */
198  static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)  static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
199  {  {
200   size_t ret = 0;   size_t ret;
201     char *p = (char*)ptr;
202    
203   do {   do {
204   clearerr(stream);   clearerr(stream);
205   ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);   ret = fread(p, 1, nmemb, stream);
206   } while (ret < nmemb && ferror(stream) && errno == EINTR);   p += ret;
207     nmemb -= ret;
208     } while (nmemb && ferror(stream) && errno == EINTR);
209    
210   return ret;   return p - (char*)ptr;
211  }  }
212    
213  /* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM.  /* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
214   * Returns S, or NULL if an eof or non-interrupt error is encountered.  */   * Returns S, or NULL if an eof or non-interrupt error is encountered.  */
215  static char *safe_fgets(char *s, int size, FILE *stream)  static char *safe_fgets(char *s, int size, FILE *stream)
216  {  {
# Line 76  static char *safe_fgets(char *s, int siz Line 225  static char *safe_fgets(char *s, int siz
225  }  }
226    
227  #if ENABLE_FEATURE_WGET_AUTHENTICATION  #if ENABLE_FEATURE_WGET_AUTHENTICATION
228  /*  /* Base64-encode character string. buf is assumed to be char buf[512]. */
229   *  Base64-encode character string and return the string.  static char *base64enc_512(char buf[512], const char *str)
  */  
 static char *base64enc(unsigned char *p, char *buf, int len)  
230  {  {
231   bb_uuencode(p, buf, len, bb_uuenc_tbl_base64);   unsigned len = strlen(str);
232     if (len > 512/4*3 - 10) /* paranoia */
233     len = 512/4*3 - 10;
234     bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
235   return buf;   return buf;
236  }  }
237  #endif  #endif
238    
239  int wget_main(int argc, char **argv)  
240    static FILE *open_socket(len_and_sockaddr *lsa)
241    {
242     FILE *fp;
243    
244     /* glibc 2.4 seems to try seeking on it - ??! */
245     /* hopefully it understands what ESPIPE means... */
246     fp = fdopen(xconnect_stream(lsa), "r+");
247     if (fp == NULL)
248     bb_perror_msg_and_die("fdopen");
249    
250     return fp;
251    }
252    
253    
254    static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
255    {
256     int result;
257     if (s1) {
258     if (!s2) s2 = "";
259     fprintf(fp, "%s%s\r\n", s1, s2);
260     fflush(fp);
261     }
262    
263     do {
264     char *buf_ptr;
265    
266     if (fgets(buf, 510, fp) == NULL) {
267     bb_perror_msg_and_die("error getting response");
268     }
269     buf_ptr = strstr(buf, "\r\n");
270     if (buf_ptr) {
271     *buf_ptr = '\0';
272     }
273     } while (!isdigit(buf[0]) || buf[3] != ' ');
274    
275     buf[3] = '\0';
276     result = xatoi_u(buf);
277     buf[3] = ' ';
278     return result;
279    }
280    
281    
282    static void parse_url(char *src_url, struct host_info *h)
283    {
284     char *url, *p, *sp;
285    
286     /* h->allocated = */ url = xstrdup(src_url);
287    
288     if (strncmp(url, "http://", 7) == 0) {
289     h->port = bb_lookup_port("http", "tcp", 80);
290     h->host = url + 7;
291     h->is_ftp = 0;
292     } else if (strncmp(url, "ftp://", 6) == 0) {
293     h->port = bb_lookup_port("ftp", "tcp", 21);
294     h->host = url + 6;
295     h->is_ftp = 1;
296     } else
297     bb_error_msg_and_die("not an http or ftp url: %s", url);
298    
299     // FYI:
300     // "Real" wget 'http://busybox.net?var=a/b' sends this request:
301     //   'GET /?var=a/b HTTP 1.0'
302     //   and saves 'index.html?var=a%2Fb' (we save 'b')
303     // wget 'http://busybox.net?login=john@doe':
304     //   request: 'GET /?login=john@doe HTTP/1.0'
305     //   saves: 'index.html?login=john@doe' (we save '?login=john@doe')
306     // wget 'http://busybox.net#test/test':
307     //   request: 'GET / HTTP/1.0'
308     //   saves: 'index.html' (we save 'test')
309     //
310     // We also don't add unique .N suffix if file exists...
311     sp = strchr(h->host, '/');
312     p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
313     p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
314     if (!sp) {
315     h->path = "";
316     } else if (*sp == '/') {
317     *sp = '\0';
318     h->path = sp + 1;
319     } else { // '#' or '?'
320     // http://busybox.net?login=john@doe is a valid URL
321     // memmove converts to:
322     // http:/busybox.nett?login=john@doe...
323     memmove(h->host - 1, h->host, sp - h->host);
324     h->host--;
325     sp[-1] = '\0';
326     h->path = sp;
327     }
328    
329     sp = strrchr(h->host, '@');
330     h->user = NULL;
331     if (sp != NULL) {
332     h->user = h->host;
333     *sp = '\0';
334     h->host = sp + 1;
335     }
336    
337     sp = h->host;
338    }
339    
340    
341    static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
342    {
343     char *s, *hdrval;
344     int c;
345    
346     /* *istrunc = 0; */
347    
348     /* retrieve header line */
349     if (fgets(buf, bufsiz, fp) == NULL)
350     return NULL;
351    
352     /* see if we are at the end of the headers */
353     for (s = buf; *s == '\r'; ++s)
354     continue;
355     if (*s == '\n')
356     return NULL;
357    
358     /* convert the header name to lower case */
359     for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s)
360     *s = tolower(*s);
361    
362     /* verify we are at the end of the header name */
363     if (*s != ':')
364     bb_error_msg_and_die("bad header line: %s", buf);
365    
366     /* locate the start of the header value */
367     *s++ = '\0';
368     hdrval = skip_whitespace(s);
369    
370     /* locate the end of header */
371     while (*s && *s != '\r' && *s != '\n')
372     ++s;
373    
374     /* end of header found */
375     if (*s) {
376     *s = '\0';
377     return hdrval;
378     }
379    
380     /* Rats! The buffer isn't big enough to hold the entire header value. */
381     while (c = getc(fp), c != EOF && c != '\n')
382     continue;
383     /* *istrunc = 1; */
384     return hdrval;
385    }
386    
387    
388    int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
389    int wget_main(int argc UNUSED_PARAM, char **argv)
390  {  {
391   char buf[512];   char buf[512];
392   struct host_info server, target;   struct host_info server, target;
393   len_and_sockaddr *lsa;   len_and_sockaddr *lsa;
394   int n, status;   int status;
395   int port;   int port;
396   int try = 5;   int try = 5;
397   unsigned opt;   unsigned opt;
398   char *s;   char *str;
399   char *proxy = 0;   char *proxy = 0;
400   char *dir_prefix = NULL;   char *dir_prefix = NULL;
401  #if ENABLE_FEATURE_WGET_LONG_OPTIONS  #if ENABLE_FEATURE_WGET_LONG_OPTIONS
402   char *extra_headers = NULL;   char *extra_headers = NULL;
403   llist_t *headers_llist = NULL;   llist_t *headers_llist = NULL;
404  #endif  #endif
   
  /* server.allocated = target.allocated = NULL; */  
   
405   FILE *sfp = NULL;               /* socket to web/ftp server         */   FILE *sfp = NULL;               /* socket to web/ftp server         */
406   FILE *dfp = NULL;               /* socket to ftp server (data)      */   FILE *dfp;                      /* socket to ftp server (data)      */
407   char *fname_out = NULL;         /* where to direct output (-O)      */   char *fname_out;                /* where to direct output (-O)      */
408   int got_clen = 0;               /* got content-length: from server  */   bool got_clen = 0;              /* got content-length: from server  */
409   int output_fd = -1;   int output_fd = -1;
410   int use_proxy = 1;              /* Use proxies if env vars are set  */   bool use_proxy = 1;             /* Use proxies if env vars are set  */
411   const char *proxy_flag = "on";  /* Use proxies if env vars are set  */   const char *proxy_flag = "on";  /* Use proxies if env vars are set  */
412   const char *user_agent = "Wget";/* Content of the "User-Agent" header field */   const char *user_agent = "Wget";/* "User-Agent" header field        */
413    
414   /*   static const char keywords[] ALIGN1 =
415   * Crack command line.   "content-length\0""transfer-encoding\0""chunked\0""location\0";
  */  
416   enum {   enum {
417   WGET_OPT_CONTINUE   = 0x1,   KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
  WGET_OPT_QUIET      = 0x2,  
  WGET_OPT_OUTNAME    = 0x4,  
  WGET_OPT_PREFIX     = 0x8,  
  WGET_OPT_PROXY      = 0x10,  
  WGET_OPT_USER_AGENT = 0x20,  
  WGET_OPT_PASSIVE    = 0x40,  
  WGET_OPT_HEADER     = 0x80,  
418   };   };
419  #if ENABLE_FEATURE_WGET_LONG_OPTIONS   enum {
420   static const struct option wget_long_options[] = {   WGET_OPT_CONTINUE   = (1 << 0),
421   // name, has_arg, flag, val   WGET_OPT_SPIDER    = (1 << 1),
422   { "continue",         no_argument, NULL, 'c' },   WGET_OPT_QUIET      = (1 << 2),
423   { "quiet",            no_argument, NULL, 'q' },   WGET_OPT_OUTNAME    = (1 << 3),
424   { "output-document",  required_argument, NULL, 'O' },   WGET_OPT_PREFIX     = (1 << 4),
425   { "directory-prefix", required_argument, NULL, 'P' },   WGET_OPT_PROXY      = (1 << 5),
426   { "proxy",            required_argument, NULL, 'Y' },   WGET_OPT_USER_AGENT = (1 << 6),
427   { "user-agent",       required_argument, NULL, 'U' },   WGET_OPT_RETRIES    = (1 << 7),
428   { "passive-ftp",      no_argument, NULL, 0xff },   WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
429   { "header",           required_argument, NULL, 0xfe },   WGET_OPT_PASSIVE    = (1 << 9),
430   { 0, 0, 0, 0 }   WGET_OPT_HEADER     = (1 << 10),
431   };   };
432   applet_long_options = wget_long_options;  #if ENABLE_FEATURE_WGET_LONG_OPTIONS
433     static const char wget_longopts[] ALIGN1 =
434     /* name, has_arg, val */
435     "continue\0"         No_argument       "c"
436     "spider\0"           No_argument       "s"
437     "quiet\0"            No_argument       "q"
438     "output-document\0"  Required_argument "O"
439     "directory-prefix\0" Required_argument "P"
440     "proxy\0"            Required_argument "Y"
441     "user-agent\0"       Required_argument "U"
442     /* Ignored: */
443     // "tries\0"            Required_argument "t"
444     // "timeout\0"          Required_argument "T"
445     /* Ignored (we always use PASV): */
446     "passive-ftp\0"      No_argument       "\xff"
447     "header\0"           Required_argument "\xfe"
448     ;
449  #endif  #endif
450    
451     INIT_G();
452    
453    #if ENABLE_FEATURE_WGET_LONG_OPTIONS
454     applet_long_options = wget_longopts;
455    #endif
456     /* server.allocated = target.allocated = NULL; */
457   opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");   opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
458   opt = getopt32(argc, argv, "cqO:P:Y:U:",   opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
459   &fname_out, &dir_prefix,   &fname_out, &dir_prefix,
460   &proxy_flag, &user_agent   &proxy_flag, &user_agent,
461     NULL, /* -t RETRIES */
462     NULL /* -T NETWORK_READ_TIMEOUT */
463   USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)   USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
464   );   );
465   if (strcmp(proxy_flag, "off") == 0) {   if (strcmp(proxy_flag, "off") == 0) {
466   /* Use the proxy if necessary. */   /* Use the proxy if necessary */
467   use_proxy = 0;   use_proxy = 0;
468   }   }
469  #if ENABLE_FEATURE_WGET_LONG_OPTIONS  #if ENABLE_FEATURE_WGET_LONG_OPTIONS
470   if (headers_llist) {   if (headers_llist) {
471   int size = 1;   int size = 1;
472   char *cp;   char *cp;
473   llist_t *ll = headers_llist = rev_llist(headers_llist);   llist_t *ll = headers_llist;
474   while (ll) {   while (ll) {
475   size += strlen(ll->data) + 2;   size += strlen(ll->data) + 2;
476   ll = ll->link;   ll = ll->link;
477   }   }
478   extra_headers = cp = xmalloc(size);   extra_headers = cp = xmalloc(size);
479   while (headers_llist) {   while (headers_llist) {
480   cp += sprintf(cp, "%s\r\n", headers_llist->data);   cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
  headers_llist = headers_llist->link;  
481   }   }
482   }   }
483  #endif  #endif
# Line 173  int wget_main(int argc, char **argv) Line 486  int wget_main(int argc, char **argv)
486   server.host = target.host;   server.host = target.host;
487   server.port = target.port;   server.port = target.port;
488    
489   /*   /* Use the proxy if necessary */
  * Use the proxy if necessary.  
  */  
490   if (use_proxy) {   if (use_proxy) {
491   proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");   proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
492   if (proxy && *proxy) {   if (proxy && *proxy) {
# Line 185  int wget_main(int argc, char **argv) Line 496  int wget_main(int argc, char **argv)
496   }   }
497   }   }
498    
499   /* Guess an output filename */   /* Guess an output filename, if there was no -O FILE */
500   if (!fname_out) {   if (!(opt & WGET_OPT_OUTNAME)) {
501   // Dirty hack. Needed because bb_get_last_path_component   fname_out = bb_get_last_path_component_nostrip(target.path);
502   // will destroy trailing / by storing '\0' in last byte!   /* handle "wget http://kernel.org//" */
503   if (!last_char_is(target.path, '/')) {   if (fname_out[0] == '/' || !fname_out[0])
504   fname_out =   fname_out = (char*)"index.html";
505  #if ENABLE_FEATURE_WGET_STATUSBAR   /* -P DIR is considered only if there was no -O FILE */
506   curfile =   if (dir_prefix)
 #endif  
  bb_get_last_path_component(target.path);  
  }  
  if (!fname_out || !fname_out[0]) {  
  fname_out =  
 #if ENABLE_FEATURE_WGET_STATUSBAR  
  curfile =  
 #endif  
  "index.html";  
  }  
  if (dir_prefix != NULL)  
507   fname_out = concat_path_file(dir_prefix, fname_out);   fname_out = concat_path_file(dir_prefix, fname_out);
 #if ENABLE_FEATURE_WGET_STATUSBAR  
508   } else {   } else {
509   curfile = bb_get_last_path_component(fname_out);   if (LONE_DASH(fname_out)) {
510  #endif   /* -O - */
511     output_fd = 1;
512     opt &= ~WGET_OPT_CONTINUE;
513     }
514   }   }
515    #if ENABLE_FEATURE_WGET_STATUSBAR
516     curfile = bb_get_last_path_component_nostrip(fname_out);
517    #endif
518    
519   /* Impossible?   /* Impossible?
520   if ((opt & WGET_OPT_CONTINUE) && !fname_out)   if ((opt & WGET_OPT_CONTINUE) && !fname_out)
521   bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */   bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
522    
523   /*   /* Determine where to start transfer */
  * Determine where to start transfer.  
  */  
  if (LONE_DASH(fname_out)) {  
  output_fd = 1;  
  opt &= ~WGET_OPT_CONTINUE;  
  }  
524   if (opt & WGET_OPT_CONTINUE) {   if (opt & WGET_OPT_CONTINUE) {
525   output_fd = open(fname_out, O_WRONLY);   output_fd = open(fname_out, O_WRONLY);
526   if (output_fd >= 0) {   if (output_fd >= 0) {
# Line 233  int wget_main(int argc, char **argv) Line 533  int wget_main(int argc, char **argv)
533   /* We want to do exactly _one_ DNS lookup, since some   /* We want to do exactly _one_ DNS lookup, since some
534   * sites (i.e. ftp.us.debian.org) use round-robin DNS   * sites (i.e. ftp.us.debian.org) use round-robin DNS
535   * and we want to connect to only one IP... */   * and we want to connect to only one IP... */
536   lsa = host2sockaddr(server.host, server.port);   lsa = xhost2sockaddr(server.host, server.port);
537   if (!(opt & WGET_OPT_QUIET)) {   if (!(opt & WGET_OPT_QUIET)) {
538   fprintf(stderr, "Connecting to %s [%s]\n", server.host,   fprintf(stderr, "Connecting to %s (%s)\n", server.host,
539   xmalloc_sockaddr2dotted(&lsa->sa, lsa->len));   xmalloc_sockaddr2dotted(&lsa->u.sa));
540   /* We leak xmalloc_sockaddr2dotted result */   /* We leak result of xmalloc_sockaddr2dotted */
541   }   }
542    
543   if (use_proxy || !target.is_ftp) {   if (use_proxy || !target.is_ftp) {
# Line 245  int wget_main(int argc, char **argv) Line 545  int wget_main(int argc, char **argv)
545   *  HTTP session   *  HTTP session
546   */   */
547   do {   do {
548   got_clen = chunked = 0;   got_clen = 0;
549     chunked = 0;
550    
551   if (!--try)   if (!--try)
552   bb_error_msg_and_die("too many redirections");   bb_error_msg_and_die("too many redirections");
553    
554   /*   /* Open socket to http server */
  * Open socket to http server  
  */  
555   if (sfp) fclose(sfp);   if (sfp) fclose(sfp);
556   sfp = open_socket(lsa);   sfp = open_socket(lsa);
557    
558   /*   /* Send HTTP request.  */
  * Send HTTP request.  
  */  
559   if (use_proxy) {   if (use_proxy) {
 // const char *format = "GET %stp://%s:%d/%s HTTP/1.1\r\n";  
 //#if ENABLE_FEATURE_WGET_IP6_LITERAL  
 // if (strchr(target.host, ':'))  
 // format = "GET %stp://[%s]:%d/%s HTTP/1.1\r\n";  
 //#endif  
 // fprintf(sfp, format,  
 // target.is_ftp ? "f" : "ht", target.host,  
 // ntohs(target.port), target.path);  
560   fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",   fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
561   target.is_ftp ? "f" : "ht", target.host,   target.is_ftp ? "f" : "ht", target.host,
562   target.path);   target.path);
# Line 280  int wget_main(int argc, char **argv) Line 569  int wget_main(int argc, char **argv)
569    
570  #if ENABLE_FEATURE_WGET_AUTHENTICATION  #if ENABLE_FEATURE_WGET_AUTHENTICATION
571   if (target.user) {   if (target.user) {
572   fprintf(sfp, "Authorization: Basic %s\r\n",   fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
573   base64enc((unsigned char*)target.user, buf, sizeof(buf)));   base64enc_512(buf, target.user));
574   }   }
575   if (use_proxy && server.user) {   if (use_proxy && server.user) {
576   fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",   fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
577   base64enc((unsigned char*)server.user, buf, sizeof(buf)));   base64enc_512(buf, server.user));
578   }   }
579  #endif  #endif
580    
# Line 304  int wget_main(int argc, char **argv) Line 593  int wget_main(int argc, char **argv)
593   if (fgets(buf, sizeof(buf), sfp) == NULL)   if (fgets(buf, sizeof(buf), sfp) == NULL)
594   bb_error_msg_and_die("no response from server");   bb_error_msg_and_die("no response from server");
595    
596   s = buf;   str = buf;
597   while (*s != '\0' && !isspace(*s)) ++s;   str = skip_non_whitespace(str);
598   s = skip_whitespace(s);   str = skip_whitespace(str);
599   // FIXME: no error check   // FIXME: no error check
600   // xatou wouldn't work: "200 OK"   // xatou wouldn't work: "200 OK"
601   status = atoi(s);   status = atoi(str);
602   switch (status) {   switch (status) {
603   case 0:   case 0:
604   case 100:   case 100:
605   while (gethdr(buf, sizeof(buf), sfp, &n) != NULL)   while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
606   /* eat all remaining headers */;   /* eat all remaining headers */;
607   goto read_response;   goto read_response;
608   case 200:   case 200:
609    /*
610    Response 204 doesn't say "null file", it says "metadata
611    has changed but data didn't":
612    
613    "10.2.5 204 No Content
614    The server has fulfilled the request but does not need to return
615    an entity-body, and might want to return updated metainformation.
616    The response MAY include new or updated metainformation in the form
617    of entity-headers, which if present SHOULD be associated with
618    the requested variant.
619    
620    If the client is a user agent, it SHOULD NOT change its document
621    view from that which caused the request to be sent. This response
622    is primarily intended to allow input for actions to take place
623    without causing a change to the user agent's active document view,
624    although any new or updated metainformation SHOULD be applied
625    to the document currently in the user agent's active view.
626    
627    The 204 response MUST NOT include a message-body, and thus
628    is always terminated by the first empty line after the header fields."
629    
630    However, in real world it was observed that some web servers
631    (e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
632    */
633     case 204:
634   break;   break;
635   case 300: /* redirection */   case 300: /* redirection */
636   case 301:   case 301:
# Line 326  int wget_main(int argc, char **argv) Line 640  int wget_main(int argc, char **argv)
640   case 206:   case 206:
641   if (beg_range)   if (beg_range)
642   break;   break;
643   /*FALLTHRU*/   /* fall through */
644   default:   default:
645   /* Show first line only and kill any ESC tricks */   /* Show first line only and kill any ESC tricks */
646   buf[strcspn(buf, "\n\r\x1b")] = '\0';   buf[strcspn(buf, "\n\r\x1b")] = '\0';
# Line 336  int wget_main(int argc, char **argv) Line 650  int wget_main(int argc, char **argv)
650   /*   /*
651   * Retrieve HTTP headers.   * Retrieve HTTP headers.
652   */   */
653   while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {   while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
654   if (strcasecmp(buf, "content-length") == 0) {   /* gethdr did already convert the "FOO:" string to lowercase */
655   content_len = BB_STRTOOFF(s, NULL, 10);   smalluint key = index_in_strings(keywords, *&buf) + 1;
656     if (key == KEY_content_length) {
657     content_len = BB_STRTOOFF(str, NULL, 10);
658   if (errno || content_len < 0) {   if (errno || content_len < 0) {
659   bb_error_msg_and_die("content-length %s is garbage", s);   bb_error_msg_and_die("content-length %s is garbage", str);
660   }   }
661   got_clen = 1;   got_clen = 1;
662   continue;   continue;
663   }   }
664   if (strcasecmp(buf, "transfer-encoding") == 0) {   if (key == KEY_transfer_encoding) {
665   if (strcasecmp(s, "chunked") != 0)   if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
666   bb_error_msg_and_die("server wants to do %s transfer encoding", s);   bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
667   chunked = got_clen = 1;   chunked = got_clen = 1;
668   }   }
669   if (strcasecmp(buf, "location") == 0) {   if (key == KEY_location) {
670   if (s[0] == '/')   if (str[0] == '/')
671   /* free(target.allocated); */   /* free(target.allocated); */
672   target.path = /* target.allocated = */ xstrdup(s+1);   target.path = /* target.allocated = */ xstrdup(str+1);
673   else {   else {
674   parse_url(s, &target);   parse_url(str, &target);
675   if (use_proxy == 0) {   if (use_proxy == 0) {
676   server.host = target.host;   server.host = target.host;
677   server.port = target.port;   server.port = target.port;
678   }   }
679   free(lsa);   free(lsa);
680   lsa = host2sockaddr(server.host, server.port);   lsa = xhost2sockaddr(server.host, server.port);
681   break;   break;
682   }   }
683   }   }
# Line 386  int wget_main(int argc, char **argv) Line 702  int wget_main(int argc, char **argv)
702   * Splitting username:password pair,   * Splitting username:password pair,
703   * trying to log in   * trying to log in
704   */   */
705   s = strchr(target.user, ':');   str = strchr(target.user, ':');
706   if (s)   if (str)
707   *(s++) = '\0';   *(str++) = '\0';
708   switch (ftpcmd("USER ", target.user, sfp, buf)) {   switch (ftpcmd("USER ", target.user, sfp, buf)) {
709   case 230:   case 230:
710   break;   break;
711   case 331:   case 331:
712   if (ftpcmd("PASS ", s, sfp, buf) == 230)   if (ftpcmd("PASS ", str, sfp, buf) == 230)
713   break;   break;
714   /* FALLTHRU (failed login) */   /* fall through (failed login) */
715   default:   default:
716   bb_error_msg_and_die("ftp login: %s", buf+4);   bb_error_msg_and_die("ftp login: %s", buf+4);
717   }   }
# Line 423  int wget_main(int argc, char **argv) Line 739  int wget_main(int argc, char **argv)
739   // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]   // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
740   // Server's IP is N1.N2.N3.N4 (we ignore it)   // Server's IP is N1.N2.N3.N4 (we ignore it)
741   // Server's port for data connection is P1*256+P2   // Server's port for data connection is P1*256+P2
742   s = strrchr(buf, ')');   str = strrchr(buf, ')');
743   if (s) s[0] = '\0';   if (str) str[0] = '\0';
744   s = strrchr(buf, ',');   str = strrchr(buf, ',');
745   if (!s) goto pasv_error;   if (!str) goto pasv_error;
746   port = xatou_range(s+1, 0, 255);   port = xatou_range(str+1, 0, 255);
747   *s = '\0';   *str = '\0';
748   s = strrchr(buf, ',');   str = strrchr(buf, ',');
749   if (!s) goto pasv_error;   if (!str) goto pasv_error;
750   port += xatou_range(s+1, 0, 255) * 256;   port += xatou_range(str+1, 0, 255) * 256;
751   set_nport(lsa, htons(port));   set_nport(lsa, htons(port));
752   dfp = open_socket(lsa);   dfp = open_socket(lsa);
753    
# Line 442  int wget_main(int argc, char **argv) Line 758  int wget_main(int argc, char **argv)
758   }   }
759    
760   if (ftpcmd("RETR ", target.path, sfp, buf) > 150)   if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
761   bb_error_msg_and_die("bad response to RETR: %s", buf);   bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
762   }   }
763    
764     if (opt & WGET_OPT_SPIDER) {
765     if (ENABLE_FEATURE_CLEAN_UP)
766     fclose(sfp);
767     return EXIT_SUCCESS;
768     }
769    
770   /*   /*
771   * Retrieve file   * Retrieve file
772   */   */
  if (chunked) {  
  fgets(buf, sizeof(buf), dfp);  
  content_len = STRTOOFF(buf, NULL, 16);  
  /* FIXME: error check?? */  
  }  
773    
774   /* Do it before progressmeter (want to have nice error message) */   /* Do it before progressmeter (want to have nice error message) */
775   if (output_fd < 0)   if (output_fd < 0) {
776   output_fd = xopen(fname_out,   int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
777   O_WRONLY|O_CREAT|O_EXCL|O_TRUNC);   /* compat with wget: -O FILE can overwrite */
778     if (opt & WGET_OPT_OUTNAME)
779     o_flags = O_WRONLY | O_CREAT | O_TRUNC;
780     output_fd = xopen(fname_out, o_flags);
781     }
782    
783   if (!(opt & WGET_OPT_QUIET))   if (!(opt & WGET_OPT_QUIET))
784   progressmeter(-1);   progressmeter(-1);
785    
786   do {   if (chunked)
787     goto get_clen;
788    
789     /* Loops only if chunked */
790     while (1) {
791   while (content_len > 0 || !got_clen) {   while (content_len > 0 || !got_clen) {
792     int n;
793   unsigned rdsz = sizeof(buf);   unsigned rdsz = sizeof(buf);
794    
795   if (content_len < sizeof(buf) && (chunked || got_clen))   if (content_len < sizeof(buf) && (chunked || got_clen))
796   rdsz = (unsigned)content_len;   rdsz = (unsigned)content_len;
797   n = safe_fread(buf, 1, rdsz, dfp);   n = safe_fread(buf, rdsz, dfp);
798   if (n <= 0)   if (n <= 0) {
799     if (ferror(dfp)) {
800     /* perror will not work: ferror doesn't set errno */
801     bb_error_msg_and_die(bb_msg_read_error);
802     }
803   break;   break;
  if (full_write(output_fd, buf, n) != n) {  
  bb_perror_msg_and_die(bb_msg_write_error);  
804   }   }
805     xwrite(output_fd, buf, n);
806  #if ENABLE_FEATURE_WGET_STATUSBAR  #if ENABLE_FEATURE_WGET_STATUSBAR
807   transferred += n;   transferred += n;
808  #endif  #endif
809   if (got_clen) {   if (got_clen)
810   content_len -= n;   content_len -= n;
  }  
811   }   }
812    
813   if (chunked) {   if (!chunked)
814   safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */   break;
  safe_fgets(buf, sizeof(buf), dfp);  
  content_len = STRTOOFF(buf, NULL, 16);  
  /* FIXME: error check? */  
  if (content_len == 0) {  
  chunked = 0; /* all done! */  
  }  
  }  
815    
816   if (n == 0 && ferror(dfp)) {   safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
817   bb_perror_msg_and_die(bb_msg_read_error);   get_clen:
818   }   safe_fgets(buf, sizeof(buf), dfp);
819   } while (chunked);   content_len = STRTOOFF(buf, NULL, 16);
820     /* FIXME: error check? */
821     if (content_len == 0)
822     break; /* all done! */
823     }
824    
825   if (!(opt & WGET_OPT_QUIET))   if (!(opt & WGET_OPT_QUIET))
826   progressmeter(1);   progressmeter(0);
827    
828   if ((use_proxy == 0) && target.is_ftp) {   if ((use_proxy == 0) && target.is_ftp) {
829   fclose(dfp);   fclose(dfp);
# Line 506  int wget_main(int argc, char **argv) Line 831  int wget_main(int argc, char **argv)
831   bb_error_msg_and_die("ftp error: %s", buf+4);   bb_error_msg_and_die("ftp error: %s", buf+4);
832   ftpcmd("QUIT", NULL, sfp, buf);   ftpcmd("QUIT", NULL, sfp, buf);
833   }   }
  exit(EXIT_SUCCESS);  
 }  
   
   
 static void parse_url(char *src_url, struct host_info *h)  
 {  
  char *url, *p, *sp;  
   
  /* h->allocated = */ url = xstrdup(src_url);  
   
  if (strncmp(url, "http://", 7) == 0) {  
  h->port = bb_lookup_port("http", "tcp", 80);  
  h->host = url + 7;  
  h->is_ftp = 0;  
  } else if (strncmp(url, "ftp://", 6) == 0) {  
  h->port = bb_lookup_port("ftp", "tcp", 21);  
  h->host = url + 6;  
  h->is_ftp = 1;  
  } else  
  bb_error_msg_and_die("not an http or ftp url: %s", url);  
   
  // FYI:  
  // "Real" wget 'http://busybox.net?var=a/b' sends this request:  
  //   'GET /?var=a/b HTTP 1.0'  
  //   and saves 'index.html?var=a%2Fb' (we save 'b')  
  // wget 'http://busybox.net?login=john@doe':  
  //   request: 'GET /?login=john@doe HTTP/1.0'  
  //   saves: 'index.html?login=john@doe' (we save '?login=john@doe')  
  // wget 'http://busybox.net#test/test':  
  //   request: 'GET / HTTP/1.0'  
  //   saves: 'index.html' (we save 'test')  
  //  
  // We also don't add unique .N suffix if file exists...  
  sp = strchr(h->host, '/');  
  p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;  
  p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;  
  if (!sp) {  
  /* must be writable because of bb_get_last_path_component() */  
  static char nullstr[] = "";  
  h->path = nullstr;  
  } else if (*sp == '/') {  
  *sp = '\0';  
  h->path = sp + 1;  
  } else { // '#' or '?'  
  // http://busybox.net?login=john@doe is a valid URL  
  // memmove converts to:  
  // http:/busybox.nett?login=john@doe...  
  memmove(h->host-1, h->host, sp - h->host);  
  h->host--;  
  sp[-1] = '\0';  
  h->path = sp;  
  }  
   
  sp = strrchr(h->host, '@');  
  h->user = NULL;  
  if (sp != NULL) {  
  h->user = h->host;  
  *sp = '\0';  
  h->host = sp + 1;  
  }  
   
  sp = h->host;  
   
 //host2sockaddr does this itself  
 //#if ENABLE_FEATURE_WGET_IP6_LITERAL  
 // if (sp[0] == '[') {  
 // char *ep;  
 //  
 // ep = sp + 1;  
 // while (*ep == ':' || isxdigit(*ep))  
 // ep++;  
 // if (*ep == ']') {  
 // h->host++;  
 // *ep = '\0';  
 // sp = ep + 1;  
 // }  
 // }  
 //#endif  
 //  
 // p = strchr(sp, ':');  
 // if (p != NULL) {  
 // *p = '\0';  
 // h->port = htons(xatou16(p + 1));  
 // }  
 }  
   
   
 static FILE *open_socket(len_and_sockaddr *lsa)  
 {  
  FILE *fp;  
   
  /* glibc 2.4 seems to try seeking on it - ??! */  
  /* hopefully it understands what ESPIPE means... */  
  fp = fdopen(xconnect_stream(lsa), "r+");  
  if (fp == NULL)  
  bb_perror_msg_and_die("fdopen");  
   
  return fp;  
 }  
   
   
 static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)  
 {  
  char *s, *hdrval;  
  int c;  
   
  *istrunc = 0;  
   
  /* retrieve header line */  
  if (fgets(buf, bufsiz, fp) == NULL)  
  return NULL;  
   
  /* see if we are at the end of the headers */  
  for (s = buf; *s == '\r'; ++s)  
  ;  
  if (s[0] == '\n')  
  return NULL;  
   
  /* convert the header name to lower case */  
  for (s = buf; isalnum(*s) || *s == '-'; ++s)  
  *s = tolower(*s);  
   
  /* verify we are at the end of the header name */  
  if (*s != ':')  
  bb_error_msg_and_die("bad header line: %s", buf);  
   
  /* locate the start of the header value */  
  for (*s++ = '\0'; *s == ' ' || *s == '\t'; ++s)  
  ;  
  hdrval = s;  
   
  /* locate the end of header */  
  while (*s != '\0' && *s != '\r' && *s != '\n')  
  ++s;  
   
  /* end of header found */  
  if (*s != '\0') {  
  *s = '\0';  
  return hdrval;  
  }  
   
  /* Rats!  The buffer isn't big enough to hold the entire header value. */  
  while (c = getc(fp), c != EOF && c != '\n')  
  ;  
  *istrunc = 1;  
  return hdrval;  
 }  
   
 static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf)  
 {  
  int result;  
  if (s1) {  
  if (!s2) s2 = "";  
  fprintf(fp, "%s%s\r\n", s1, s2);  
  fflush(fp);  
  }  
   
  do {  
  char *buf_ptr;  
   
  if (fgets(buf, 510, fp) == NULL) {  
  bb_perror_msg_and_die("error getting response");  
  }  
  buf_ptr = strstr(buf, "\r\n");  
  if (buf_ptr) {  
  *buf_ptr = '\0';  
  }  
  } while (!isdigit(buf[0]) || buf[3] != ' ');  
   
  buf[3] = '\0';  
  result = xatoi_u(buf);  
  buf[3] = ' ';  
  return result;  
 }  
   
 #if ENABLE_FEATURE_WGET_STATUSBAR  
 /* Stuff below is from BSD rcp util.c, as added to openshh.  
  * Original copyright notice is retained at the end of this file.  
  */  
 static int  
 getttywidth(void)  
 {  
  int width;  
  get_terminal_width_height(0, &width, NULL);  
  return width;  
 }  
   
 static void  
 updateprogressmeter(int ignore)  
 {  
  int save_errno = errno;  
   
  progressmeter(0);  
  errno = save_errno;  
 }  
   
 static void alarmtimer(int iwait)  
 {  
  struct itimerval itv;  
   
  itv.it_value.tv_sec = iwait;  
  itv.it_value.tv_usec = 0;  
  itv.it_interval = itv.it_value;  
  setitimer(ITIMER_REAL, &itv, NULL);  
 }  
   
   
 static void  
 progressmeter(int flag)  
 {  
  static struct timeval lastupdate;  
  static off_t lastsize, totalsize;  
   
  struct timeval now, td, tvwait;  
  off_t abbrevsize;  
  int elapsed, ratio, barlength, i;  
  char buf[256];  
   
  if (flag == -1) { /* first call to progressmeter */  
  gettimeofday(&start, (struct timezone *) 0);  
  lastupdate = start;  
  lastsize = 0;  
  totalsize = content_len + beg_range; /* as content_len changes.. */  
  }  
   
  gettimeofday(&now, (struct timezone *) 0);  
  ratio = 100;  
  if (totalsize != 0 && !chunked) {  
  /* long long helps to have working ETA even if !LFS */  
  ratio = (int) (100 * (unsigned long long)(transferred+beg_range) / totalsize);  
  ratio = MIN(ratio, 100);  
  }  
   
  fprintf(stderr, "\r%-20.20s%4d%% ", curfile, ratio);  
   
  barlength = getttywidth() - 51;  
  if (barlength > 0 && barlength < sizeof(buf)) {  
  i = barlength * ratio / 100;  
  memset(buf, '*', i);  
  memset(buf + i, ' ', barlength - i);  
  buf[barlength] = '\0';  
  fprintf(stderr, "|%s|", buf);  
  }  
  i = 0;  
  abbrevsize = transferred + beg_range;  
  while (abbrevsize >= 100000) {  
  i++;  
  abbrevsize >>= 10;  
  }  
  /* see http://en.wikipedia.org/wiki/Tera */  
  fprintf(stderr, "%6d %c%c ", (int)abbrevsize, " KMGTPEZY"[i], i?'B':' ');  
   
  timersub(&now, &lastupdate, &tvwait);  
  if (transferred > lastsize) {  
  lastupdate = now;  
  lastsize = transferred;  
  if (tvwait.tv_sec >= STALLTIME)  
  timeradd(&start, &tvwait, &start);  
  tvwait.tv_sec = 0;  
  }  
  timersub(&now, &start, &td);  
  elapsed = td.tv_sec;  
   
  if (tvwait.tv_sec >= STALLTIME) {  
  fprintf(stderr, " - stalled -");  
  } else {  
  off_t to_download = totalsize - beg_range;  
  if (transferred <= 0 || elapsed <= 0 || transferred > to_download || chunked) {  
  fprintf(stderr, "--:--:-- ETA");  
  } else {  
  /* to_download / (transferred/elapsed) - elapsed: */  
  int eta = (int) ((unsigned long long)to_download*elapsed/transferred - elapsed);  
  /* (long long helps to have working ETA even if !LFS) */  
  i = eta % 3600;  
  fprintf(stderr, "%02d:%02d:%02d ETA", eta / 3600, i / 60, i % 60);  
  }  
  }  
834    
835   if (flag == -1) { /* first call to progressmeter */   return EXIT_SUCCESS;
  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) { /* last call to progressmeter */  
  alarmtimer(0);  
  transferred = 0;  
  putc('\n', stderr);  
  }  
836  }  }
 #endif  
   
 /* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,  
  * much of which was blatantly stolen from openssh.  */  
   
 /*-  
  * 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. <BSD Advertising Clause omitted per the July 22, 1999 licensing change  
  * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>  
  *  
  * 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.1 2007-09-01 22:43:54 niro Exp $  
  */  

Legend:
Removed from v.815  
changed lines
  Added in v.816