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