Home | History | Annotate | Line # | Download | only in examples
      1 // SPDX-License-Identifier: 0BSD
      2 
      3 ///////////////////////////////////////////////////////////////////////////////
      4 //
      5 /// \file       11_file_info.c
      6 /// \brief      Get uncompressed size of .xz file(s)
      7 ///
      8 /// Usage:      ./11_file_info INFILE1.xz [INFILEn.xz]...
      9 ///
     10 /// Example:    ./11_file_info foo.xz
     11 //
     12 //  Author:     Lasse Collin
     13 //
     14 ///////////////////////////////////////////////////////////////////////////////
     15 
     16 #include <stdbool.h>
     17 #include <inttypes.h>
     18 #include <stdlib.h>
     19 #include <stdio.h>
     20 #include <string.h>
     21 #include <errno.h>
     22 #include <lzma.h>
     23 
     24 
     25 static bool
     26 print_file_size(lzma_stream *strm, FILE *infile, const char *filename)
     27 {
     28 	// Get the file size. In standard C it can be done by seeking to
     29 	// the end of the file and then getting the file position.
     30 	// In POSIX one can use fstat() and then st_size from struct stat.
     31 	// Also note that fseek() and ftell() use long and thus don't support
     32 	// large files on 32-bit systems (POSIX versions fseeko() and
     33 	// ftello() can support large files).
     34 	if (fseek(infile, 0, SEEK_END)) {
     35 		fprintf(stderr, "Error seeking the file '%s': %s\n",
     36 				filename, strerror(errno));
     37 		return false;
     38 	}
     39 
     40 	const long file_size = ftell(infile);
     41 
     42 	// The decoder wants to start from the beginning of the .xz file.
     43 	rewind(infile);
     44 
     45 	// Initialize the decoder.
     46 	lzma_index *i;
     47 	lzma_ret ret = lzma_file_info_decoder(strm, &i, UINT64_MAX,
     48 			(uint64_t)file_size);
     49 	switch (ret) {
     50 	case LZMA_OK:
     51 		// Initialization succeeded.
     52 		break;
     53 
     54 	case LZMA_MEM_ERROR:
     55 		fprintf(stderr, "Out of memory when initializing "
     56 				"the .xz file info decoder\n");
     57 		return false;
     58 
     59 	case LZMA_PROG_ERROR:
     60 	default:
     61 		fprintf(stderr, "Unknown error, possibly a bug\n");
     62 		return false;
     63 	}
     64 
     65 	// This example program reuses the same lzma_stream structure
     66 	// for multiple files, so we need to reset this when starting
     67 	// a new file.
     68 	strm->avail_in = 0;
     69 
     70 	// Buffer for input data.
     71 	uint8_t inbuf[BUFSIZ];
     72 
     73 	// Pass data to the decoder and seek when needed.
     74 	while (true) {
     75 		if (strm->avail_in == 0) {
     76 			strm->next_in = inbuf;
     77 			strm->avail_in = fread(inbuf, 1, sizeof(inbuf),
     78 					infile);
     79 
     80 			if (ferror(infile)) {
     81 				fprintf(stderr,
     82 					"Error reading from '%s': %s\n",
     83 					filename, strerror(errno));
     84 				return false;
     85 			}
     86 
     87 			// We don't need to care about hitting the end of
     88 			// the file so no need to check for feof().
     89 		}
     90 
     91 		ret = lzma_code(strm, LZMA_RUN);
     92 
     93 		switch (ret) {
     94 		case LZMA_OK:
     95 			break;
     96 
     97 		case LZMA_SEEK_NEEDED:
     98 			// The cast is safe because liblzma won't ask us to
     99 			// seek past the known size of the input file which
    100 			// did fit into a long.
    101 			//
    102 			// NOTE: Remember to change these to off_t if you
    103 			// switch fseeko() or lseek().
    104 			if (fseek(infile, (long)(strm->seek_pos), SEEK_SET)) {
    105 				fprintf(stderr, "Error seeking the "
    106 						"file '%s': %s\n",
    107 						filename, strerror(errno));
    108 				return false;
    109 			}
    110 
    111 			// The old data in the inbuf is useless now. Set
    112 			// avail_in to zero so that we will read new input
    113 			// from the new file position on the next iteration
    114 			// of this loop.
    115 			strm->avail_in = 0;
    116 			break;
    117 
    118 		case LZMA_STREAM_END:
    119 			// File information was successfully decoded.
    120 			// See <lzma/index.h> for functions that can be
    121 			// used on it. In this example we just print
    122 			// the uncompressed size (in bytes) of
    123 			// the .xz file followed by its file name.
    124 			printf("%10" PRIu64 " %s\n",
    125 					lzma_index_uncompressed_size(i),
    126 					filename);
    127 
    128 			// Free the memory of the lzma_index structure.
    129 			lzma_index_end(i, NULL);
    130 
    131 			return true;
    132 
    133 		case LZMA_FORMAT_ERROR:
    134 			// .xz magic bytes weren't found.
    135 			fprintf(stderr, "The file '%s' is not "
    136 					"in the .xz format\n", filename);
    137 			return false;
    138 
    139 		case LZMA_OPTIONS_ERROR:
    140 			fprintf(stderr, "The file '%s' has .xz headers that "
    141 					"are not supported by this liblzma "
    142 					"version\n", filename);
    143 			return false;
    144 
    145 		case LZMA_DATA_ERROR:
    146 			fprintf(stderr, "The file '%s' is corrupt\n",
    147 					filename);
    148 			return false;
    149 
    150 		case LZMA_MEM_ERROR:
    151 			fprintf(stderr, "Memory allocation failed when "
    152 					"decoding the file '%s'\n", filename);
    153 			return false;
    154 
    155 		// LZMA_MEMLIMIT_ERROR shouldn't happen because we used
    156 		// UINT64_MAX as the limit.
    157 		//
    158 		// LZMA_BUF_ERROR shouldn't happen because we always provide
    159 		// new input when the input buffer is empty. The decoder
    160 		// knows the input file size and thus won't try to read past
    161 		// the end of the file.
    162 		case LZMA_MEMLIMIT_ERROR:
    163 		case LZMA_BUF_ERROR:
    164 		case LZMA_PROG_ERROR:
    165 		default:
    166 			fprintf(stderr, "Unknown error, possibly a bug\n");
    167 			return false;
    168 		}
    169 	}
    170 
    171 	// This line is never reached.
    172 }
    173 
    174 
    175 extern int
    176 main(int argc, char **argv)
    177 {
    178 	bool success = true;
    179 	lzma_stream strm = LZMA_STREAM_INIT;
    180 
    181 	for (int i = 1; i < argc; ++i) {
    182 		FILE *infile = fopen(argv[i], "rb");
    183 
    184 		if (infile == NULL) {
    185 			fprintf(stderr, "Cannot open the file '%s': %s\n",
    186 					argv[i], strerror(errno));
    187 			success = false;
    188 		}
    189 
    190 		success &= print_file_size(&strm, infile, argv[i]);
    191 
    192 		(void)fclose(infile);
    193 	}
    194 
    195 	lzma_end(&strm);
    196 
    197 	// Close stdout to catch possible write errors that can occur
    198 	// when pending data is flushed from the stdio buffers.
    199 	if (fclose(stdout)) {
    200 		fprintf(stderr, "Write error: %s\n", strerror(errno));
    201 		success = false;
    202 	}
    203 
    204 	return success ? EXIT_SUCCESS : EXIT_FAILURE;
    205 }
    206