+fd_read_hunk (int fd, hunk_terminator_t terminator, long sizehint, long maxsize)
+{
+ long bufsize = sizehint;
+ char *hunk = xmalloc (bufsize);
+ int tail = 0; /* tail position in HUNK */
+
+ assert (maxsize >= bufsize);
+
+ while (1)
+ {
+ const char *end;
+ int pklen, rdlen, remain;
+
+ /* First, peek at the available data. */
+
+ pklen = fd_peek (fd, hunk + tail, bufsize - 1 - tail, -1);
+ if (pklen < 0)
+ {
+ xfree (hunk);
+ return NULL;
+ }
+ end = terminator (hunk, hunk + tail, pklen);
+ if (end)
+ {
+ /* The data contains the terminator: we'll drain the data up
+ to the end of the terminator. */
+ remain = end - (hunk + tail);
+ assert (remain >= 0);
+ if (remain == 0)
+ {
+ /* No more data needs to be read. */
+ hunk[tail] = '\0';
+ return hunk;
+ }
+ if (bufsize - 1 < tail + remain)
+ {
+ bufsize = tail + remain + 1;
+ hunk = xrealloc (hunk, bufsize);
+ }
+ }
+ else
+ /* No terminator: simply read the data we know is (or should
+ be) available. */
+ remain = pklen;
+
+ /* Now, read the data. Note that we make no assumptions about
+ how much data we'll get. (Some TCP stacks are notorious for
+ read returning less data than the previous MSG_PEEK.) */
+
+ rdlen = fd_read (fd, hunk + tail, remain, 0);
+ if (rdlen < 0)
+ {
+ xfree_null (hunk);
+ return NULL;
+ }
+ tail += rdlen;
+ hunk[tail] = '\0';
+
+ if (rdlen == 0)
+ {
+ if (tail == 0)
+ {
+ /* EOF without anything having been read */
+ xfree (hunk);
+ errno = 0;
+ return NULL;
+ }
+ else
+ /* EOF seen: return the data we've read. */
+ return hunk;
+ }
+ if (end && rdlen == remain)
+ /* The terminator was seen and the remaining data drained --
+ we got what we came for. */
+ return hunk;
+
+ /* Keep looping until all the data arrives. */
+
+ if (tail == bufsize - 1)
+ {
+ /* Double the buffer size, but refuse to allocate more than
+ MAXSIZE bytes. */
+ if (maxsize && bufsize >= maxsize)
+ {
+ xfree (hunk);
+ errno = ENOMEM;
+ return NULL;
+ }
+ bufsize <<= 1;
+ if (maxsize && bufsize > maxsize)
+ bufsize = maxsize;
+ hunk = xrealloc (hunk, bufsize);
+ }
+ }
+}
+
+static const char *
+line_terminator (const char *start, const char *peeked, int peeklen)
+{
+ const char *p = memchr (peeked, '\n', peeklen);
+ if (p)
+ /* p+1 because the line must include '\n' */
+ return p + 1;
+ return NULL;
+}
+
+/* The maximum size of the single line we agree to accept. This is
+ not meant to impose an arbitrary limit, but to protect the user
+ from Wget slurping up available memory upon encountering malicious
+ or buggy server output. Define it to 0 to remove the limit. */
+#define FD_READ_LINE_MAX 4096
+
+/* Read one line from FD and return it. The line is allocated using
+ malloc, but is never larger than FD_READ_LINE_MAX.
+
+ If an error occurs, or if no data can be read, NULL is returned.
+ In the former case errno indicates the error condition, and in the
+ latter case, errno is NULL. */
+
+char *
+fd_read_line (int fd)
+{
+ return fd_read_hunk (fd, line_terminator, 128, FD_READ_LINE_MAX);
+}
+\f
+/* Return a printed representation of the download rate, along with
+ the units appropriate for the download speed. */
+
+const char *
+retr_rate (wgint bytes, double secs)