*: introduce and use xvfork()
[oweals/busybox.git] / networking / wget.c
index 5feb539d5174fc053abb222307c9c2dd16dc1e3f..8606b9b38fab87d9c5d180a9378b8ef4e29bfb2f 100644 (file)
@@ -6,17 +6,16 @@
  *
  */
 
-#include <getopt.h>    /* for struct option */
 #include "libbb.h"
 
 struct host_info {
        // May be used if we ever will want to free() all xstrdup()s...
        /* char *allocated; */
-       char    *path;
-       char    *user;
-       char    *host;
-       int      port;
-       smallint is_ftp;
+       const char *path;
+       const char *user;
+       char       *host;
+       int         port;
+       smallint    is_ftp;
 };
 
 
@@ -36,7 +35,7 @@ struct globals {
 };
 #define G (*(struct globals*)&bb_common_bufsiz1)
 struct BUG_G_too_big {
-        char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+       char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
 };
 #define content_len     (G.content_len    )
 #define beg_range       (G.beg_range      )
@@ -55,9 +54,9 @@ enum {
        STALLTIME = 5                   /* Seconds when xfer considered "stalled" */
 };
 
-static int getttywidth(void)
+static unsigned int getttywidth(void)
 {
-       int width;
+       unsigned width;
        get_terminal_width_height(0, &width, NULL);
        return width;
 }
@@ -142,15 +141,10 @@ static void progressmeter(int flag)
                /* last call to progressmeter */
                alarm(0);
                transferred = 0;
-               putc('\n', stderr);
+               fputc('\n', stderr);
        } else {
-               if (flag == -1) {
-                       /* first call to progressmeter */
-                       struct sigaction sa;
-                       sa.sa_handler = progressmeter;
-                       sigemptyset(&sa.sa_mask);
-                       sa.sa_flags = SA_RESTART;
-                       sigaction(SIGALRM, &sa, NULL);
+               if (flag == -1) { /* first call to progressmeter */
+                       signal_SA_RESTART_empty_mask(SIGALRM, progressmeter);
                }
                alarm(1);
        }
@@ -194,7 +188,7 @@ static void progressmeter(int flag)
  */
 #else /* FEATURE_WGET_STATUSBAR */
 
-static ALWAYS_INLINE void progressmeter(int flag) { }
+static ALWAYS_INLINE void progressmeter(int flag ATTRIBUTE_UNUSED) { }
 
 #endif
 
@@ -318,9 +312,7 @@ static void parse_url(char *src_url, struct host_info *h)
        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[] ALIGN1 = "";
-               h->path = nullstr;
+               h->path = "";
        } else if (*sp == '/') {
                *sp = '\0';
                h->path = sp + 1;
@@ -328,7 +320,7 @@ static void parse_url(char *src_url, struct host_info *h)
                // 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);
+               memmove(h->host - 1, h->host, sp - h->host);
                h->host--;
                sp[-1] = '\0';
                h->path = sp;
@@ -393,8 +385,8 @@ static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
 }
 
 
-int wget_main(int argc, char **argv);
-int wget_main(int argc, char **argv)
+int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int wget_main(int argc ATTRIBUTE_UNUSED, char **argv)
 {
        char buf[512];
        struct host_info server, target;
@@ -411,8 +403,8 @@ int wget_main(int argc, char **argv)
        llist_t *headers_llist = NULL;
 #endif
        FILE *sfp = NULL;               /* socket to web/ftp server         */
-       FILE *dfp = NULL;               /* socket to ftp server (data)      */
-       char *fname_out = NULL;         /* where to direct output (-O)      */
+       FILE *dfp;                      /* socket to ftp server (data)      */
+       char *fname_out;                /* where to direct output (-O)      */
        bool got_clen = 0;              /* got content-length: from server  */
        int output_fd = -1;
        bool use_proxy = 1;             /* Use proxies if env vars are set  */
@@ -457,9 +449,11 @@ int wget_main(int argc, char **argv)
 #endif
        /* server.allocated = target.allocated = NULL; */
        opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
-       opt = getopt32(argv, "csqO:P:Y:U:",
+       opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
                                &fname_out, &dir_prefix,
-                               &proxy_flag, &user_agent
+                               &proxy_flag, &user_agent,
+                               NULL, /* -t RETRIES */
+                               NULL /* -T NETWORK_READ_TIMEOUT */
                                USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
                                );
        if (strcmp(proxy_flag, "off") == 0) {
@@ -477,8 +471,7 @@ int wget_main(int argc, char **argv)
                }
                extra_headers = cp = xmalloc(size);
                while (headers_llist) {
-                       cp += sprintf(cp, "%s\r\n", headers_llist->data);
-                       headers_llist = headers_llist->link;
+                       cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
                }
        }
 #endif
@@ -497,40 +490,31 @@ int wget_main(int argc, char **argv)
                }
        }
 
-       /* Guess an output filename */
-       if (!fname_out) {
-               // Dirty hack. Needed because bb_get_last_path_component
-               // will destroy trailing / by storing '\0' in last byte!
-               if (!last_char_is(target.path, '/')) {
-                       fname_out = bb_get_last_path_component(target.path);
-#if ENABLE_FEATURE_WGET_STATUSBAR
-                       curfile = fname_out;
-#endif
-               }
-               if (!fname_out || !fname_out[0]) {
-                       /* bb_get_last_path_component writes
-                        * to last '/' only. We don't have one here... */
+       /* Guess an output filename, if there was no -O FILE */
+       if (!(opt & WGET_OPT_OUTNAME)) {
+               fname_out = bb_get_last_path_component_nostrip(target.path);
+               /* handle "wget http://kernel.org//" */
+               if (fname_out[0] == '/' || !fname_out[0])
                        fname_out = (char*)"index.html";
-#if ENABLE_FEATURE_WGET_STATUSBAR
-                       curfile = fname_out;
-#endif
-               }
-               if (dir_prefix != NULL)
+               /* -P DIR is considered only if there was no -O FILE */
+               if (dir_prefix)
                        fname_out = concat_path_file(dir_prefix, fname_out);
-#if ENABLE_FEATURE_WGET_STATUSBAR
        } else {
-               curfile = bb_get_last_path_component(fname_out);
-#endif
+               if (LONE_DASH(fname_out)) {
+                       /* -O - */
+                       output_fd = 1;
+                       opt &= ~WGET_OPT_CONTINUE;
+               }
        }
+#if ENABLE_FEATURE_WGET_STATUSBAR
+       curfile = bb_get_last_path_component_nostrip(fname_out);
+#endif
+
        /* Impossible?
        if ((opt & WGET_OPT_CONTINUE) && !fname_out)
                bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
 
        /* Determine where to start transfer */
-       if (LONE_DASH(fname_out)) {
-               output_fd = 1;
-               opt &= ~WGET_OPT_CONTINUE;
-       }
        if (opt & WGET_OPT_CONTINUE) {
                output_fd = open(fname_out, O_WRONLY);
                if (output_fd >= 0) {
@@ -546,7 +530,7 @@ int wget_main(int argc, char **argv)
        lsa = xhost2sockaddr(server.host, server.port);
        if (!(opt & WGET_OPT_QUIET)) {
                fprintf(stderr, "Connecting to %s (%s)\n", server.host,
-                               xmalloc_sockaddr2dotted(&lsa->sa));
+                               xmalloc_sockaddr2dotted(&lsa->u.sa));
                /* We leak result of xmalloc_sockaddr2dotted */
        }
 
@@ -616,6 +600,31 @@ int wget_main(int argc, char **argv)
                                        /* eat all remaining headers */;
                                goto read_response;
                        case 200:
+/*
+Response 204 doesn't say "null file", it says "metadata
+has changed but data didn't":
+
+"10.2.5 204 No Content
+The server has fulfilled the request but does not need to return
+an entity-body, and might want to return updated metainformation.
+The response MAY include new or updated metainformation in the form
+of entity-headers, which if present SHOULD be associated with
+the requested variant.
+
+If the client is a user agent, it SHOULD NOT change its document
+view from that which caused the request to be sent. This response
+is primarily intended to allow input for actions to take place
+without causing a change to the user agent's active document view,
+although any new or updated metainformation SHOULD be applied
+to the document currently in the user agent's active view.
+
+The 204 response MUST NOT include a message-body, and thus
+is always terminated by the first empty line after the header fields."
+
+However, in real world it was observed that some web servers
+(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
+*/
+                       case 204:
                                break;
                        case 300:       /* redirection */
                        case 301:
@@ -625,7 +634,7 @@ int wget_main(int argc, char **argv)
                        case 206:
                                if (beg_range)
                                        break;
-                               /*FALLTHRU*/
+                               /* fall through */
                        default:
                                /* Show first line only and kill any ESC tricks */
                                buf[strcspn(buf, "\n\r\x1b")] = '\0';
@@ -696,7 +705,7 @@ int wget_main(int argc, char **argv)
                case 331:
                        if (ftpcmd("PASS ", str, sfp, buf) == 230)
                                break;
-                       /* FALLTHRU (failed login) */
+                       /* fall through (failed login) */
                default:
                        bb_error_msg_and_die("ftp login: %s", buf+4);
                }
@@ -757,9 +766,13 @@ int wget_main(int argc, char **argv)
         */
 
        /* Do it before progressmeter (want to have nice error message) */
-       if (output_fd < 0)
-               output_fd = xopen(fname_out,
-                       O_WRONLY|O_CREAT|O_EXCL|O_TRUNC);
+       if (output_fd < 0) {
+               int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
+               /* compat with wget: -O FILE can overwrite */
+               if (opt & WGET_OPT_OUTNAME)
+                       o_flags = O_WRONLY | O_CREAT | O_TRUNC;
+               output_fd = xopen(fname_out, o_flags);
+       }
 
        if (!(opt & WGET_OPT_QUIET))
                progressmeter(-1);