wget: code shrink by splitting main() into easier-to-optimize functions
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 27 Jun 2009 23:02:24 +0000 (01:02 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 27 Jun 2009 23:02:24 +0000 (01:02 +0200)
function                                             old     new   delta
retrieve_file_data                                     -     356    +356
wget_main                                           2793    2326    -467
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/1 up/down: 356/-467)         Total: -111 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/wget.c

index ca3acd0855a045023b83b09b7311b15ffed7c75c..5e387891a4c4c5a0a834a1b64ed01ff07e1f6c94 100644 (file)
@@ -32,7 +32,8 @@ struct globals {
        unsigned lastupdate_sec;
        unsigned start_sec;
 #endif
-       smallint chunked;             /* chunked transfer encoding */
+       smallint chunked;         /* chunked transfer encoding */
+       smallint got_clen;        /* got content-length: from server  */
 };
 #define G (*(struct globals*)&bb_common_bufsiz1)
 struct BUG_G_too_big {
@@ -46,7 +47,6 @@ struct BUG_G_too_big {
 #define curfile         (G.curfile        )
 #define lastupdate_sec  (G.lastupdate_sec )
 #define start_sec       (G.start_sec      )
-#define chunked         (G.chunked        )
 #define INIT_G() do { } while (0)
 
 
@@ -79,7 +79,7 @@ static void progress_meter(int flag)
        }
 
        ratio = 100;
-       if (totalsize != 0 && !chunked) {
+       if (totalsize != 0 && !G.chunked) {
                /* long long helps to have it working even if !LFS */
                ratio = (unsigned) (100ULL * (transferred+beg_range) / totalsize);
                if (ratio > 100) ratio = 100;
@@ -127,7 +127,7 @@ static void progress_meter(int flag)
                fprintf(stderr, " - stalled -");
        } else {
                off_t to_download = totalsize - beg_range;
-               if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || chunked) {
+               if (transferred <= 0 || (int)elapsed <= 0 || transferred > to_download || G.chunked) {
                        fprintf(stderr, "--:--:-- ETA");
                } else {
                        /* to_download / (transferred/elapsed) - elapsed: */
@@ -239,7 +239,6 @@ static char *base64enc_512(char buf[512], const char *str)
 }
 #endif
 
-
 static FILE *open_socket(len_and_sockaddr *lsa)
 {
        FILE *fp;
@@ -253,7 +252,6 @@ static FILE *open_socket(len_and_sockaddr *lsa)
        return fp;
 }
 
-
 static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
 {
        int result;
@@ -281,7 +279,6 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
        return result;
 }
 
-
 static void parse_url(char *src_url, struct host_info *h)
 {
        char *url, *p, *sp;
@@ -340,7 +337,6 @@ static void parse_url(char *src_url, struct host_info *h)
        sp = h->host;
 }
 
-
 static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
 {
        char *s, *hdrval;
@@ -380,7 +376,7 @@ static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
                return hdrval;
        }
 
-       /* Rats! The buffer isn't big enough to hold the entire header value. */
+       /* Rats! The buffer isn't big enough to hold the entire header value */
        while (c = getc(fp), c != EOF && c != '\n')
                continue;
        /* *istrunc = 1; */
@@ -425,30 +421,171 @@ static char *URL_escape(const char *str)
 }
 #endif
 
+static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
+{
+       char buf[512];
+       FILE *sfp;
+       char *str;
+       int port;
+
+       if (!target->user)
+               target->user = xstrdup("anonymous:busybox@");
+
+       sfp = open_socket(lsa);
+       if (ftpcmd(NULL, NULL, sfp, buf) != 220)
+               bb_error_msg_and_die("%s", buf+4);
+
+       /*
+        * Splitting username:password pair,
+        * trying to log in
+        */
+       str = strchr(target->user, ':');
+       if (str)
+               *str++ = '\0';
+       switch (ftpcmd("USER ", target->user, sfp, buf)) {
+       case 230:
+               break;
+       case 331:
+               if (ftpcmd("PASS ", str, sfp, buf) == 230)
+                       break;
+               /* fall through (failed login) */
+       default:
+               bb_error_msg_and_die("ftp login: %s", buf+4);
+       }
+
+       ftpcmd("TYPE I", NULL, sfp, buf);
+
+       /*
+        * Querying file size
+        */
+       if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) {
+               content_len = BB_STRTOOFF(buf+4, NULL, 10);
+               if (errno || content_len < 0) {
+                       bb_error_msg_and_die("SIZE value is garbage");
+               }
+               G.got_clen = 1;
+       }
+
+       /*
+        * Entering passive mode
+        */
+       if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
+ pasv_error:
+               bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
+       }
+       // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
+       // Server's IP is N1.N2.N3.N4 (we ignore it)
+       // Server's port for data connection is P1*256+P2
+       str = strrchr(buf, ')');
+       if (str) str[0] = '\0';
+       str = strrchr(buf, ',');
+       if (!str) goto pasv_error;
+       port = xatou_range(str+1, 0, 255);
+       *str = '\0';
+       str = strrchr(buf, ',');
+       if (!str) goto pasv_error;
+       port += xatou_range(str+1, 0, 255) * 256;
+       set_nport(lsa, htons(port));
+
+       *dfpp = open_socket(lsa);
+
+       if (beg_range) {
+               sprintf(buf, "REST %"OFF_FMT"d", beg_range);
+               if (ftpcmd(buf, NULL, sfp, buf) == 350)
+                       content_len -= beg_range;
+       }
+
+       if (ftpcmd("RETR ", target->path, sfp, buf) > 150)
+               bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
+
+       return sfp;
+}
+
+/* Must match option string! */
+enum {
+       WGET_OPT_CONTINUE   = (1 << 0),
+       WGET_OPT_SPIDER     = (1 << 1),
+       WGET_OPT_QUIET      = (1 << 2),
+       WGET_OPT_OUTNAME    = (1 << 3),
+       WGET_OPT_PREFIX     = (1 << 4),
+       WGET_OPT_PROXY      = (1 << 5),
+       WGET_OPT_USER_AGENT = (1 << 6),
+       WGET_OPT_RETRIES    = (1 << 7),
+       WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
+       WGET_OPT_PASSIVE    = (1 << 9),
+       WGET_OPT_HEADER     = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+       WGET_OPT_POST_DATA  = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+};
+
+static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
+{
+       char buf[512];
+
+       if (!(option_mask32 & WGET_OPT_QUIET))
+               progress_meter(-1);
+
+       if (G.chunked)
+               goto get_clen;
+
+       /* Loops only if chunked */
+       while (1) {
+               while (content_len > 0 || !G.got_clen) {
+                       int n;
+                       unsigned rdsz = sizeof(buf);
+
+                       if (content_len < sizeof(buf) && (G.chunked || G.got_clen))
+                               rdsz = (unsigned)content_len;
+                       n = safe_fread(buf, rdsz, dfp);
+                       if (n <= 0) {
+                               if (ferror(dfp)) {
+                                       /* perror will not work: ferror doesn't set errno */
+                                       bb_error_msg_and_die(bb_msg_read_error);
+                               }
+                               break;
+                       }
+                       xwrite(output_fd, buf, n);
+#if ENABLE_FEATURE_WGET_STATUSBAR
+                       transferred += n;
+#endif
+                       if (G.got_clen)
+                               content_len -= n;
+               }
+
+               if (!G.chunked)
+                       break;
+
+               safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
+ get_clen:
+               safe_fgets(buf, sizeof(buf), dfp);
+               content_len = STRTOOFF(buf, NULL, 16);
+               /* FIXME: error check? */
+               if (content_len == 0)
+                       break; /* all done! */
+       }
+
+       if (!(option_mask32 & WGET_OPT_QUIET))
+               progress_meter(0);
+}
+
 int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int wget_main(int argc UNUSED_PARAM, char **argv)
 {
        char buf[512];
        struct host_info server, target;
        len_and_sockaddr *lsa;
-       int status;
-       int port;
-       int try = 5;
        unsigned opt;
-       char *str;
-       char *proxy = 0;
+       char *proxy = NULL;
        char *dir_prefix = NULL;
 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
        char *post_data;
        char *extra_headers = NULL;
        llist_t *headers_llist = NULL;
 #endif
-       FILE *sfp = NULL;               /* socket to web/ftp server         */
+       FILE *sfp;                      /* socket to web/ftp server         */
        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  */
+       bool use_proxy;                 /* Use proxies if env vars are set  */
        const char *proxy_flag = "on";  /* Use proxies if env vars are set  */
        const char *user_agent = "Wget";/* "User-Agent" header field        */
 
@@ -457,20 +594,6 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
        enum {
                KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
        };
-       enum {
-               WGET_OPT_CONTINUE   = (1 << 0),
-               WGET_OPT_SPIDER     = (1 << 1),
-               WGET_OPT_QUIET      = (1 << 2),
-               WGET_OPT_OUTNAME    = (1 << 3),
-               WGET_OPT_PREFIX     = (1 << 4),
-               WGET_OPT_PROXY      = (1 << 5),
-               WGET_OPT_USER_AGENT = (1 << 6),
-               WGET_OPT_RETRIES    = (1 << 7),
-               WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
-               WGET_OPT_PASSIVE    = (1 << 9),
-               WGET_OPT_HEADER     = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
-               WGET_OPT_POST_DATA  = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
-       };
 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
        static const char wget_longopts[] ALIGN1 =
                /* name, has_arg, val */
@@ -506,10 +629,6 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
                                IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
                                IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
                                );
-       if (strcmp(proxy_flag, "off") == 0) {
-               /* Use the proxy if necessary */
-               use_proxy = 0;
-       }
 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
        if (headers_llist) {
                int size = 1;
@@ -526,11 +645,13 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
        }
 #endif
 
+       /* TODO: compat issue: should handle "wget URL1 URL2..." */
        parse_url(argv[optind], &target);
        server.host = target.host;
        server.port = target.port;
 
        /* Use the proxy if necessary */
+       use_proxy = (strcmp(proxy_flag, "off") != 0);
        if (use_proxy) {
                proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
                if (proxy && *proxy) {
@@ -562,7 +683,8 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
 
        /* Impossible?
        if ((opt & WGET_OPT_CONTINUE) && !fname_out)
-               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)");
+       */
 
        /* Determine where to start transfer */
        if (opt & WGET_OPT_CONTINUE) {
@@ -571,7 +693,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
                        beg_range = xlseek(output_fd, 0, SEEK_END);
                }
                /* File doesn't exist. We do not create file here yet.
-                  We are not sure it exists on remove side */
+                * We are not sure it exists on remove side */
        }
 
        /* We want to do exactly _one_ DNS lookup, since some
@@ -584,13 +706,20 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
                /* We leak result of xmalloc_sockaddr2dotted */
        }
 
+       /* G.got_clen = 0; - already is */
+       sfp = NULL;
        if (use_proxy || !target.is_ftp) {
                /*
                 *  HTTP session
                 */
+               int status;
+               int try = 5;
+
                do {
-                       got_clen = 0;
-                       chunked = 0;
+                       char *str;
+
+                       G.got_clen = 0;
+                       G.chunked = 0;
 
                        if (!--try)
                                bb_error_msg_and_die("too many redirections");
@@ -599,7 +728,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
                        if (sfp) fclose(sfp);
                        sfp = open_socket(lsa);
 
-                       /* Send HTTP request */
+                       /* Send HTTP request */
                        if (use_proxy) {
                                fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
                                        target.is_ftp ? "f" : "ht", target.host,
@@ -710,20 +839,20 @@ However, in real world it was observed that some web servers
                         * Retrieve HTTP headers.
                         */
                        while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
-                               /* gethdr did already convert the "FOO:" string to lowercase */
+                               /* gethdr converted "FOO:" string to lowercase */
                                smalluint key = index_in_strings(keywords, *&buf) + 1;
                                if (key == KEY_content_length) {
                                        content_len = BB_STRTOOFF(str, NULL, 10);
                                        if (errno || content_len < 0) {
                                                bb_error_msg_and_die("content-length %s is garbage", str);
                                        }
-                                       got_clen = 1;
+                                       G.got_clen = 1;
                                        continue;
                                }
                                if (key == KEY_transfer_encoding) {
                                        if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
                                                bb_error_msg_and_die("transfer encoding '%s' is not supported", str);
-                                       chunked = got_clen = 1;
+                                       G.chunked = G.got_clen = 1;
                                }
                                if (key == KEY_location) {
                                        if (str[0] == '/')
@@ -746,78 +875,10 @@ However, in real world it was observed that some web servers
                dfp = sfp;
 
        } else {
-
                /*
                 *  FTP session
                 */
-               if (!target.user)
-                       target.user = xstrdup("anonymous:busybox@");
-
-               sfp = open_socket(lsa);
-               if (ftpcmd(NULL, NULL, sfp, buf) != 220)
-                       bb_error_msg_and_die("%s", buf+4);
-
-               /*
-                * Splitting username:password pair,
-                * trying to log in
-                */
-               str = strchr(target.user, ':');
-               if (str)
-                       *(str++) = '\0';
-               switch (ftpcmd("USER ", target.user, sfp, buf)) {
-               case 230:
-                       break;
-               case 331:
-                       if (ftpcmd("PASS ", str, sfp, buf) == 230)
-                               break;
-                       /* fall through (failed login) */
-               default:
-                       bb_error_msg_and_die("ftp login: %s", buf+4);
-               }
-
-               ftpcmd("TYPE I", NULL, sfp, buf);
-
-               /*
-                * Querying file size
-                */
-               if (ftpcmd("SIZE ", target.path, sfp, buf) == 213) {
-                       content_len = BB_STRTOOFF(buf+4, NULL, 10);
-                       if (errno || content_len < 0) {
-                               bb_error_msg_and_die("SIZE value is garbage");
-                       }
-                       got_clen = 1;
-               }
-
-               /*
-                * Entering passive mode
-                */
-               if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
- pasv_error:
-                       bb_error_msg_and_die("bad response to %s: %s", "PASV", buf);
-               }
-               // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
-               // Server's IP is N1.N2.N3.N4 (we ignore it)
-               // Server's port for data connection is P1*256+P2
-               str = strrchr(buf, ')');
-               if (str) str[0] = '\0';
-               str = strrchr(buf, ',');
-               if (!str) goto pasv_error;
-               port = xatou_range(str+1, 0, 255);
-               *str = '\0';
-               str = strrchr(buf, ',');
-               if (!str) goto pasv_error;
-               port += xatou_range(str+1, 0, 255) * 256;
-               set_nport(lsa, htons(port));
-               dfp = open_socket(lsa);
-
-               if (beg_range) {
-                       sprintf(buf, "REST %"OFF_FMT"d", beg_range);
-                       if (ftpcmd(buf, NULL, sfp, buf) == 350)
-                               content_len -= beg_range;
-               }
-
-               if (ftpcmd("RETR ", target.path, sfp, buf) > 150)
-                       bb_error_msg_and_die("bad response to %s: %s", "RETR", buf);
+               sfp = prepare_ftp_session(&dfp, &target, lsa);
        }
 
        if (opt & WGET_OPT_SPIDER) {
@@ -826,11 +887,6 @@ However, in real world it was observed that some web servers
                return EXIT_SUCCESS;
        }
 
-       /*
-        * Retrieve file
-        */
-
-       /* Do it before progress_meter (want to have nice error message) */
        if (output_fd < 0) {
                int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
                /* compat with wget: -O FILE can overwrite */
@@ -839,50 +895,7 @@ However, in real world it was observed that some web servers
                output_fd = xopen(fname_out, o_flags);
        }
 
-       if (!(opt & WGET_OPT_QUIET))
-               progress_meter(-1);
-
-       if (chunked)
-               goto get_clen;
-
-       /* Loops only if chunked */
-       while (1) {
-               while (content_len > 0 || !got_clen) {
-                       int n;
-                       unsigned rdsz = sizeof(buf);
-
-                       if (content_len < sizeof(buf) && (chunked || got_clen))
-                               rdsz = (unsigned)content_len;
-                       n = safe_fread(buf, rdsz, dfp);
-                       if (n <= 0) {
-                               if (ferror(dfp)) {
-                                       /* perror will not work: ferror doesn't set errno */
-                                       bb_error_msg_and_die(bb_msg_read_error);
-                               }
-                               break;
-                       }
-                       xwrite(output_fd, buf, n);
-#if ENABLE_FEATURE_WGET_STATUSBAR
-                       transferred += n;
-#endif
-                       if (got_clen)
-                               content_len -= n;
-               }
-
-               if (!chunked)
-                       break;
-
-               safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
- get_clen:
-               safe_fgets(buf, sizeof(buf), dfp);
-               content_len = STRTOOFF(buf, NULL, 16);
-               /* FIXME: error check? */
-               if (content_len == 0)
-                       break; /* all done! */
-       }
-
-       if (!(opt & WGET_OPT_QUIET))
-               progress_meter(0);
+       retrieve_file_data(dfp, output_fd);
 
        if ((use_proxy == 0) && target.is_ftp) {
                fclose(dfp);