summaryrefslogtreecommitdiffstats
path: root/doc/examples/11_file_info.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--doc/examples/11_file_info.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/doc/examples/11_file_info.c b/doc/examples/11_file_info.c
new file mode 100644
index 0000000..caadd98
--- /dev/null
+++ b/doc/examples/11_file_info.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file 11_file_info.c
+/// \brief Get uncompressed size of .xz file(s)
+///
+/// Usage: ./11_file_info INFILE1.xz [INFILEn.xz]...
+///
+/// Example: ./11_file_info foo.xz
+//
+// Author: Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#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;
+}