207 lines
5.3 KiB
C
207 lines
5.3 KiB
C
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
/// \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;
|
|
}
|