+static const char *
+line_terminator (const char *buf, int tail, int peeklen)
+{
+ const char *p = memchr (buf + tail, '\n', peeklen);
+ if (p)
+ /* p+1 because we want the line to include '\n' */
+ return p + 1;
+ return NULL;
+}
+
+/* Read one line from FD and return it. The line is allocated using
+ malloc.
+
+ 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_until (fd, line_terminator, 128);
+}
+
+static const char *
+head_terminator (const char *buf, int tail, int peeklen)
+{
+ const char *start, *end;
+ if (tail < 4)
+ start = buf;
+ else
+ start = buf + tail - 4;
+ end = buf + tail + peeklen;
+
+ for (; start < end - 1; start++)
+ if (*start == '\n')
+ {
+ if (start < end - 2
+ && start[1] == '\r'
+ && start[2] == '\n')
+ return start + 3;
+ if (start[1] == '\n')
+ return start + 2;
+ }
+ return NULL;
+}
+
+/* Read the request head from FD and return it. The chunk of data is
+ allocated using malloc.
+
+ 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_head (int fd)
+{
+ return fd_read_until (fd, head_terminator, 512);
+}
+\f
+/* Return a printed representation of the download rate, as
+ appropriate for the speed. If PAD is non-zero, strings will be
+ padded to the width of 7 characters (xxxx.xx). */