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); + } } }