| /////////////////////////////////////////////////////////////////////////////// |
| // |
| /// \file 11_file_info.c |
| /// \brief Get uncmopressed size of .xz file(s) |
| /// |
| /// Usage: ./11_file_info INFILE1.xz [INFILEn.xz]... |
| /// |
| /// Example: ./11_file_info foo.xz |
| // |
| // Author: Lasse Collin |
| // |
| // This file has been put into the public domain. |
| // You can do whatever you want with this file. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include <stdbool.h> |
| #include <inttypes.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <lzma.h> |
| |
| |
| static bool |
| print_file_size(lzma_stream *strm, FILE *infile, const char *filename) |
| { |
| // Get the file size. In standard C it can be done by seeking to |
| // the end of the file and then getting the file position. |
| // In POSIX one can use fstat() and then st_size from struct stat. |
| // Also note that fseek() and ftell() use long and thus don't support |
| // large files on 32-bit systems (POSIX versions fseeko() and |
| // ftello() can support large files). |
| if (fseek(infile, 0, SEEK_END)) { |
| fprintf(stderr, "Error seeking the file `%s': %s\n", |
| filename, strerror(errno)); |
| return false; |
| } |
| |
| const long file_size = ftell(infile); |
| |
| // The decoder wants to start from the beginning of the .xz file. |
| rewind(infile); |
| |
| // Initialize the decoder. |
| lzma_index *i; |
| lzma_ret ret = lzma_file_info_decoder(strm, &i, UINT64_MAX, |
| (uint64_t)file_size); |
| switch (ret) { |
| case LZMA_OK: |
| // Initialization succeeded. |
| break; |
| |
| case LZMA_MEM_ERROR: |
| fprintf(stderr, "Out of memory when initializing " |
| "the .xz file info decoder\n"); |
| return false; |
| |
| case LZMA_PROG_ERROR: |
| default: |
| fprintf(stderr, "Unknown error, possibly a bug\n"); |
| return false; |
| } |
| |
| // This example program reuses the same lzma_stream structure |
| // for multiple files, so we need to reset this when starting |
| // a new file. |
| strm->avail_in = 0; |
| |
| // Buffer for input data. |
| uint8_t inbuf[BUFSIZ]; |
| |
| // Pass data to the decoder and seek when needed. |
| while (true) { |
| if (strm->avail_in == 0) { |
| strm->next_in = inbuf; |
| strm->avail_in = fread(inbuf, 1, sizeof(inbuf), |
| infile); |
| |
| if (ferror(infile)) { |
| fprintf(stderr, |
| "Error reading from `%s': %s\n", |
| filename, strerror(errno)); |
| return false; |
| } |
| |
| // We don't need to care about hitting the end of |
| // the file so no need to check for feof(). |
| } |
| |
| ret = lzma_code(strm, LZMA_RUN); |
| |
| switch (ret) { |
| case LZMA_OK: |
| break; |
| |
| case LZMA_SEEK_NEEDED: |
| // The cast is safe because liblzma won't ask us to |
| // seek past the known size of the input file which |
| // did fit into a long. |
| // |
| // NOTE: Remember to change these to off_t if you |
| // switch fseeko() or lseek(). |
| if (fseek(infile, (long)(strm->seek_pos), SEEK_SET)) { |
| fprintf(stderr, "Error seeking the " |
| "file `%s': %s\n", |
| filename, strerror(errno)); |
| return false; |
| } |
| |
| // The old data in the inbuf is useless now. Set |
| // avail_in to zero so that we will read new input |
| // from the new file position on the next iteration |
| // of this loop. |
| strm->avail_in = 0; |
| break; |
| |
| case LZMA_STREAM_END: |
| // File information was successfully decoded. |
| // See <lzma/index.h> for functions that can be |
| // used on it. In this example we just print |
| // the uncompressed size (in bytes) of |
| // the .xz file followed by its file name. |
| printf("%10" PRIu64 " %s\n", |
| lzma_index_uncompressed_size(i), |
| filename); |
| |
| // Free the memory of the lzma_index structure. |
| lzma_index_end(i, NULL); |
| |
| return true; |
| |
| case LZMA_FORMAT_ERROR: |
| // .xz magic bytes weren't found. |
| fprintf(stderr, "The file `%s' is not " |
| "in the .xz format\n", filename); |
| return false; |
| |
| case LZMA_OPTIONS_ERROR: |
| fprintf(stderr, "The file `%s' has .xz headers that " |
| "are not supported by this liblzma " |
| "version\n", filename); |
| return false; |
| |
| case LZMA_DATA_ERROR: |
| fprintf(stderr, "The file `%s' is corrupt\n", |
| filename); |
| return false; |
| |
| case LZMA_MEM_ERROR: |
| fprintf(stderr, "Memory allocation failed when " |
| "decoding the file `%s'\n", filename); |
| return false; |
| |
| // LZMA_MEMLIMIT_ERROR shouldn't happen because we used |
| // UINT64_MAX as the limit. |
| // |
| // LZMA_BUF_ERROR shouldn't happen because we always provide |
| // new input when the input buffer is empty. The decoder |
| // knows the input file size and thus won't try to read past |
| // the end of the file. |
| case LZMA_MEMLIMIT_ERROR: |
| case LZMA_BUF_ERROR: |
| case LZMA_PROG_ERROR: |
| default: |
| fprintf(stderr, "Unknown error, possibly a bug\n"); |
| return false; |
| } |
| } |
| |
| // This line is never reached. |
| } |
| |
| |
| extern int |
| main(int argc, char **argv) |
| { |
| bool success = true; |
| lzma_stream strm = LZMA_STREAM_INIT; |
| |
| for (int i = 1; i < argc; ++i) { |
| FILE *infile = fopen(argv[i], "rb"); |
| |
| if (infile == NULL) { |
| fprintf(stderr, "Cannot open the file `%s': %s\n", |
| argv[i], strerror(errno)); |
| success = false; |
| } |
| |
| success &= print_file_size(&strm, infile, argv[i]); |
| |
| (void)fclose(infile); |
| } |
| |
| lzma_end(&strm); |
| |
| // Close stdout to catch possible write errors that can occur |
| // when pending data is flushed from the stdio buffers. |
| if (fclose(stdout)) { |
| fprintf(stderr, "Write error: %s\n", strerror(errno)); |
| success = false; |
| } |
| |
| return success ? EXIT_SUCCESS : EXIT_FAILURE; |
| } |