aboutsummaryrefslogtreecommitdiff
path: root/src/xz/list.c
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2008-11-19 23:52:24 +0200
committerLasse Collin <lasse.collin@tukaani.org>2008-11-19 23:52:24 +0200
commit1880a3927b23f265f63b2adb86fbdb81ea09eb06 (patch)
tree2fe1b65d21f81b28f46eb707378d97f553e99ee1 /src/xz/list.c
parentOh well, big messy commit again. Some highlights: (diff)
downloadxz-1880a3927b23f265f63b2adb86fbdb81ea09eb06.tar.xz
Renamed lzma to xz and lzmadec to xzdec. We create symlinks
lzma, unlzma, and lzcat in "make install" for backwards compatibility with LZMA Utils 4.32.x; I'm not sure if this should be the default though.
Diffstat (limited to 'src/xz/list.c')
-rw-r--r--src/xz/list.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/src/xz/list.c b/src/xz/list.c
new file mode 100644
index 00000000..8728d47b
--- /dev/null
+++ b/src/xz/list.c
@@ -0,0 +1,477 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file list.c
+/// \brief Listing information about .lzma files
+//
+// Copyright (C) 2007 Lasse Collin
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "private.h"
+
+
+/*
+
+1. Check the file type: native, alone, unknown
+
+Alone:
+1. Show info about header. Don't look for concatenated parts.
+
+Native:
+1. Check that Stream Header is valid.
+2. Seek to the end of the file.
+3. Skip padding.
+4. Reverse decode Stream Footer.
+5. Seek Backward Size bytes.
+6.
+
+*/
+
+
+static void
+unsupported_file(file_handle *handle)
+{
+ errmsg(V_ERROR, "%s: Unsupported file type", handle->name);
+ set_exit_status(ERROR);
+ (void)io_close(handle);
+ return;
+}
+
+
+/// Primitive escaping function, that escapes only ASCII control characters.
+static void
+print_escaped(const uint8_t *str)
+{
+ while (*str != '\0') {
+ if (*str <= 0x1F || *str == 0x7F)
+ printf("\\x%02X", *str);
+ else
+ putchar(*str);
+
+ ++str;
+ }
+
+ return;
+}
+
+
+static void
+list_native(file_handle *handle)
+{
+ lzma_stream strm = LZMA_STREAM_INIT;
+ lzma_stream_flags flags;
+ lzma_ret ret = lzma_stream_header_decoder(&strm, &flags);
+
+}
+
+
+static void
+list_alone(const listing_handle *handle)
+{
+ if (handle->buffer[0] > (4 * 5 + 4) * 9 + 8) {
+ unsupported_file(handle);
+ return;
+ }
+
+ const unsigned int pb = handle->buffer[0] / (9 * 5);
+ handle->buffer[0] -= pb * 9 * 5;
+ const unsigned int lp = handle->buffer[0] / 9;
+ const unsigned int lc = handle->buffer[0] - lp * 9;
+
+ uint32_t dict = 0;
+ for (size_t i = 1; i < 5; ++i) {
+ dict <<= 8;
+ dict |= header[i];
+ }
+
+ if (dict > LZMA_DICTIONARY_SIZE_MAX) {
+ unsupported_file(handle);
+ return;
+ }
+
+ uint64_t uncompressed_size = 0;
+ for (size_t i = 5; i < 13; ++i) {
+ uncompressed_size <<= 8;
+ uncompressed_size |= header[i];
+ }
+
+ // Reject files with uncompressed size of 256 GiB or more. It's
+ // an arbitrary limit trying to avoid at least some false positives.
+ if (uncompressed_size != UINT64_MAX
+ && uncompressed_size >= (UINT64_C(1) << 38)) {
+ unsupported_file(handle);
+ return;
+ }
+
+ if (verbosity < V_WARNING) {
+ printf("name=");
+ print_escaped(handle->name);
+ printf("\nformat=alone\n");
+
+ if (uncompressed_size == UINT64_MAX)
+ printf("uncompressed_size=unknown\n");
+ else
+ printf("uncompressed_size=%" PRIu64 "\n",
+ uncompressed_size);
+
+ printf("dict=%" PRIu32 "\n", dict);
+
+ printf("lc=%u\nlp=%u\npb=%u\n\n", lc, lp, pb);
+
+ } else {
+ printf("File name: ");
+ print_escaped(handle->name);
+ printf("\nFile format: LZMA_Alone\n")
+
+ printf("Uncompressed size: ");
+ if (uncompressed_size == UINT64_MAX)
+ printf("unknown\n");
+ else
+ printf("%," PRIu64 " bytes (%" PRIu64 " MiB)\n",
+ uncompressed_size,
+ (uncompressed_size + 1024 * 512)
+ / (1024 * 1024));
+
+ printf("Dictionary size: %," PRIu32 " bytes "
+ "(%" PRIu32 " MiB)\n",
+ dict, (dict + 1024 * 512) / (1024 * 1024));
+
+ printf("Literal context bits (lc): %u\n", lc);
+ printf("Literal position bits (lc): %u\n", lp);
+ printf("Position bits (pb): %u\n", pb);
+ }
+
+ return;
+}
+
+
+
+
+typedef struct {
+ const char *filename;
+ struct stat st;
+ int fd;
+
+ lzma_stream strm;
+ lzma_stream_flags stream_flags;
+ lzma_info *info;
+
+ lzma_vli backward_size;
+ lzma_vli uncompressed_size;
+
+ size_t buffer_size;
+ uint8_t buffer[IO_BUFFER_SIZE];
+} listing_handle;
+
+
+static bool
+listing_pread(listing_handle *handle, uint64_t offset)
+{
+ if (offset >= (uint64_t)(handle->st.st_size)) {
+ errmsg(V_ERROR, "%s: Trying to read past the end of "
+ "the file.", handle->filename);
+ return true;
+ }
+
+#ifdef HAVE_PREAD
+ const ssize_t ret = pread(handle->fd, handle->buffer, IO_BUFFER_SIZE,
+ (off_t)(offset));
+#else
+ // Use lseek() + read() since we don't have pread(). We don't care
+ // to which offset the reading position is left.
+ if (lseek(handle->fd, (off_t)(offset), SEEK_SET) == -1) {
+ errmsg(V_ERROR, "%s: %s", handle->filename, strerror(errno));
+ return true;
+ }
+
+ const ssize_t ret = read(handle->fd, handle->buffer, IO_BUFFER_SIZE);
+#endif
+
+ if (ret == -1) {
+ errmsg(V_ERROR, "%s: %s", handle->filename, strerror(errno));
+ return true;
+ }
+
+ if (ret == 0) {
+ errmsg(V_ERROR, "%s: Trying to read past the end of "
+ "the file.", handle->filename);
+ return true;
+ }
+
+ handle->buffer_size = (size_t)(ret);
+ return false;
+}
+
+
+
+static bool
+parse_stream_header(listing_handle *handle)
+{
+ if (listing_pread(handle, 0))
+ return true;
+
+ // TODO Got enough input?
+
+ lzma_ret ret = lzma_stream_header_decoder(
+ &handle->strm, &handle->stream_flags);
+ if (ret != LZMA_OK) {
+ errmsg(V_ERROR, "%s: %s", handle->name, str_strm_error(ret));
+ return true;
+ }
+
+ handle->strm.next_in = handle->buffer;
+ handle->strm.avail_in = handle->buffer_size;
+ ret = lzma_code(&handle->strm, LZMA_RUN);
+ if (ret != LZMA_STREAM_END) {
+ assert(ret != LZMA_OK);
+ errmsg(V_ERROR, "%s: %s", handle->name, str_strm_error(ret));
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool
+parse_stream_tail(listing_handle *handle)
+{
+ uint64_t offset = (uint64_t)(handle->st.st_size);
+
+ // Skip padding
+ do {
+ if (offset == 0) {
+ errmsg(V_ERROR, "%s: %s", handle->name,
+ str_strm_error(LZMA_DATA_ERROR));
+ return true;
+ }
+
+ if (offset < IO_BUFFER_SIZE)
+ offset = 0;
+ else
+ offset -= IO_BUFFER_SIZE;
+
+ if (listing_pread(handle, offset))
+ return true;
+
+ while (handle->buffer_size > 0
+ && handle->buffer[handle->buffer_size - 1]
+ == '\0')
+ --handle->buffer_size;
+
+ } while (handle->buffer_size == 0);
+
+ if (handle->buffer_size < LZMA_STREAM_TAIL_SIZE) {
+ // TODO
+ }
+
+ lzma_stream_flags stream_flags;
+ lzma_ret ret = lzma_stream_tail_decoder(&handle->strm, &stream_flags);
+ if (ret != LZMA_OK) {
+ errmsg(V_ERROR, "%s: %s", handle->name, str_strm_error(ret));
+ return true;
+ }
+
+ handle->strm.next_in = handle->buffer + handle->buffer_size
+ - LZMA_STREAM_TAIL_SIZE;
+ handle->strm.avail_in = LZMA_STREAM_TAIL_SIZE;
+ handle->buffer_size -= LZMA_STREAM_TAIL_SIZE;
+ ret = lzma_code(&handle->strm, LZMA_RUN);
+ if (ret != LZMA_OK) {
+ assert(ret != LZMA_OK);
+ errmsg(V_ERROR, "%s: %s", handle->name, str_strm_error(ret));
+ return true;
+ }
+
+ if (!lzma_stream_flags_is_equal(handle->stream_flags, stream_flags)) {
+ // TODO
+ // Possibly corrupt, possibly concatenated file.
+ }
+
+ handle->backward_size = 0;
+ ret = lzma_vli_reverse_decode(&handle->backward_size, handle->buffer,
+ &handle->buffer_size);
+ if (ret != LZMA_OK) {
+ // It may be LZMA_BUF_ERROR too, but it doesn't make sense
+ // as an error message displayed to the user.
+ errmsg(V_ERROR, "%s: %s", handle->name,
+ str_strm_error(LZMA_DATA_ERROR));
+ return true;
+ }
+
+ if (!stream_flags.is_multi) {
+ handle->uncompressed_size = 0;
+ size_t tmp = handle->buffer_size;
+ ret = lzma_vli_reverse_decode(&handle->uncompressed_size,
+ handle->buffer, &tmp);
+ if (ret != LZMA_OK)
+ handle->uncompressed_size = LZMA_VLI_UNKNOWN;
+ }
+
+ // Calculate the Header Metadata Block start offset.
+
+
+ return false;
+}
+
+
+
+static void
+list_native(listing_handle *handle)
+{
+ lzma_memory_limiter *limiter
+ = lzma_memory_limiter_create(opt_memory);
+ if (limiter == NULL) {
+ errmsg(V_ERROR,
+ }
+ lzma_info *info =
+
+
+ // Parse Stream Header
+ //
+ // Single-Block Stream:
+ // - Parse Block Header
+ // - Parse Stream Footer
+ // - If Backward Size doesn't match, error out
+ //
+ // Multi-Block Stream:
+ // - Parse Header Metadata Block, if any
+ // - Parse Footer Metadata Block
+ // - Parse Stream Footer
+ // - If Footer Metadata Block doesn't match the Stream, error out
+ //
+ // In other words, we don't support concatened files.
+ if (parse_stream_header(handle))
+ return;
+
+ if (parse_block_header(handle))
+ return;
+
+ if (handle->stream_flags.is_multi) {
+ if (handle->block_options.is_metadata) {
+ if (parse_metadata(handle)
+ return;
+ }
+
+ if (my_seek(handle,
+
+ } else {
+ if (handle->block_options.is_metadata) {
+ FILE_IS_CORRUPT();
+ return;
+ }
+
+ if (parse_stream_footer(handle))
+ return;
+
+ // If Uncompressed Size isn't present in Block Header,
+ // it must be present in Stream Footer.
+ if (handle->block_options.uncompressed_size
+ == LZMA_VLI_UNKNOWN
+ && handle->stream_flags.uncompressed_size
+ == LZMA_VLI_UNKNOWN) {
+ FILE_IS_CORRUPT();
+ return;
+ }
+
+ // Construct a single-Record Index.
+ lzma_index *index = malloc(sizeof(lzma_index));
+ if (index == NULL) {
+ out_of_memory();
+ return;
+ }
+
+ // Pohdintaa:
+ // Jos Block coder hoitaisi Uncompressed ja Backward Sizet,
+ // voisi index->total_sizeksi laittaa suoraan Backward Sizen.
+ index->total_size =
+
+ if () {
+
+ }
+ }
+
+
+ if (handle->block_options.is_metadata) {
+ if (!handle->stream_flags.is_multi) {
+ FILE_IS_CORRUPT();
+ return;
+ }
+
+ if (parse_metadata(handle))
+ return;
+
+ }
+}
+
+
+
+extern void
+list(const char *filename)
+{
+ if (strcmp(filename, "-") == 0) {
+ errmsg(V_ERROR, "%s: --list does not support reading from "
+ "standard input", filename);
+ return;
+ }
+
+ if (is_empty_filename(filename))
+ return;
+
+ listing_handle handle;
+ handle.filename = filename;
+
+ handle.fd = open(filename, O_RDONLY | O_NOCTTY);
+ if (handle.fd == -1) {
+ errmsg(V_ERROR, "%s: %s", filename, strerror(errno));
+ return;
+ }
+
+ if (fstat(handle.fd, &handle.st)) {
+ errmsg(V_ERROR, "%s: %s", filename, strerror(errno));
+ goto out;
+ }
+
+ if (!S_ISREG(handle.st.st_mode)) {
+ errmsg(V_WARNING, _("%s: Not a regular file, skipping"),
+ filename);
+ goto out;
+ }
+
+ if (handle.st.st_size <= 0) {
+ errmsg(V_ERROR, _("%s: File is empty"), filename);
+ goto out;
+ }
+
+ if (listing_pread(&handle, 0))
+ goto out;
+
+ if (handle.buffer[0] == 0xFF) {
+ if (opt_header == HEADER_ALONE) {
+ errmsg(V_ERROR, "%s: FIXME", filename); // FIXME
+ goto out;
+ }
+
+ list_native(&handle);
+ } else {
+ if (opt_header != HEADER_AUTO && opt_header != HEADER_ALONE) {
+ errmsg(V_ERROR, "%s: FIXME", filename); // FIXME
+ goto out;
+ }
+
+ list_alone(&handle);
+ }
+
+out:
+ (void)close(fd);
+ return;
+}