After posting the patch I noticed some statements are superfluous.
The modified patch is attached.
diff --git a/gzip.c b/gzip.c
index cceb420..43cb9ea 100644
--- a/gzip.c
+++ b/gzip.c
@@ -1514,6 +1514,7 @@ local void do_list(ifd, method)
int ifd; /* input file descriptor */
int method; /* compression method */
{
+ const off_t save_bytes_in = bytes_in;
ulg crc; /* original crc */
static int first_time = 1;
static char const *const methods[MAX_METHODS] = {
@@ -1564,11 +1565,14 @@ local void do_list(ifd, method)
if (!RECORD_IO && method == DEFLATED && !last_member) {
/* Get the crc and uncompressed size for gzip'ed (not zip'ed) files.
- * If the lseek fails, we could use read() to get to the end, but
- * --list is used to get quick results.
- * Use "gunzip < foo.gz | wc -c" to get the uncompressed size if
- * you are not concerned about speed.
*/
+ if (insize != INBUFSIZ) {
+ /* eof: no need to lseek */
+ /* assert( insize >= 8 ) */
+ bytes_in = save_bytes_in;
+ crc = LG(inbuf + insize - 8);
+ bytes_out = LG(inbuf + insize - 4);
+ } else {
bytes_in = lseek(ifd, (off_t)(-8), SEEK_END);
if (bytes_in != -1L) {
uch buf[8];
@@ -1578,6 +1582,62 @@ local void do_list(ifd, method)
}
crc = LG(buf);
bytes_out = LG(buf+4);
+ } else {
+ /* assert(insize == INBUFSIZ) */
+ /* assert((INBUFSIZ % 2) == 0) */
+ bytes_in = save_bytes_in;
+ const int half_buf_size = INBUFSIZ / 2;
+ /* If present (possibly partially), the last 8 bytes can only
+ * be in the second half of the inbuf buffer,
+ * so the next block to read is the first half. */
+ ssize_t nread;
+ uch *buf;
+ size_t buf_to_read = half_buf_size;
+ int half_idx = 0;
+ errno = 0; /* reset lseek error */
+ while (1) {
+ nread = read_buffer(ifd, inbuf + half_idx * half_buf_size, buf_to_read);
+ if (nread == 0) {
+ break;
+ }
+ if (nread < 0) {
+ read_error();
+ }
+ bytes_in += nread;
+ buf_to_read -= nread;
+ if (buf_to_read == 0) {
+ buf_to_read = half_buf_size;
+ half_idx = 1 - half_idx;
+ }
+ }
+ insize = half_buf_size - buf_to_read;
+ if (insize >= 8) {
+ /* All 8 bytes fit in the current half buffer */
+ buf = inbuf + half_idx * half_buf_size + insize - 8;
+ } else if (insize == 0) {
+ /* All 8 bytes are in the other half buffer */
+ buf = inbuf + (1 - half_idx) * half_buf_size + half_buf_size - 8;
+ } else {
+ /* The 8 bytes are partially on the other half buffer */
+ if (half_idx == 1) {
+ /* The 8 bytes are contiguous */
+ buf = inbuf + half_buf_size + insize - 8;
+ } else {
+ /* The end of the 8 bytes is at the beginning of the first half,
+ * the start of the 8 bytes is at the end of the second half.
+ * Let's move them both at the start of the first half. */
+ const size_t start_size = 8 - insize;
+ memmove(inbuf + start_size, inbuf, insize);
+ memcpy(inbuf, inbuf + half_buf_size + half_buf_size - start_size, start_size);
+ buf = inbuf;
+ }
+ }
+ crc = LG(buf);
+ bytes_out = LG(buf+4);
+ }
}
}