xz-utils (5.4.5)
       1  ///////////////////////////////////////////////////////////////////////////////
       2  //
       3  /// \file       03_compress_custom.c
       4  /// \brief      Compress in multi-call mode using x86 BCJ and LZMA2
       5  ///
       6  /// Usage:      ./03_compress_custom < INFILE > OUTFILE
       7  ///
       8  /// Example:    ./03_compress_custom < foo > foo.xz
       9  //
      10  //  Author:     Lasse Collin
      11  //
      12  //  This file has been put into the public domain.
      13  //  You can do whatever you want with this file.
      14  //
      15  ///////////////////////////////////////////////////////////////////////////////
      16  
      17  #include <stdbool.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  init_encoder(lzma_stream *strm)
      27  {
      28  	// Use the default preset (6) for LZMA2.
      29  	//
      30  	// The lzma_options_lzma structure and the lzma_lzma_preset() function
      31  	// are declared in lzma/lzma12.h (src/liblzma/api/lzma/lzma12.h in the
      32  	// source package or e.g. /usr/include/lzma/lzma12.h depending on
      33  	// the install prefix).
      34  	lzma_options_lzma opt_lzma2;
      35  	if (lzma_lzma_preset(&opt_lzma2, LZMA_PRESET_DEFAULT)) {
      36  		// It should never fail because the default preset
      37  		// (and presets 0-9 optionally with LZMA_PRESET_EXTREME)
      38  		// are supported by all stable liblzma versions.
      39  		//
      40  		// (The encoder initialization later in this function may
      41  		// still fail due to unsupported preset *if* the features
      42  		// required by the preset have been disabled at build time,
      43  		// but no-one does such things except on embedded systems.)
      44  		fprintf(stderr, "Unsupported preset, possibly a bug\n");
      45  		return false;
      46  	}
      47  
      48  	// Now we could customize the LZMA2 options if we wanted. For example,
      49  	// we could set the the dictionary size (opt_lzma2.dict_size) to
      50  	// something else than the default (8 MiB) of the default preset.
      51  	// See lzma/lzma12.h for details of all LZMA2 options.
      52  	//
      53  	// The x86 BCJ filter will try to modify the x86 instruction stream so
      54  	// that LZMA2 can compress it better. The x86 BCJ filter doesn't need
      55  	// any options so it will be set to NULL below.
      56  	//
      57  	// Construct the filter chain. The uncompressed data goes first to
      58  	// the first filter in the array, in this case the x86 BCJ filter.
      59  	// The array is always terminated by setting .id = LZMA_VLI_UNKNOWN.
      60  	//
      61  	// See lzma/filter.h for more information about the lzma_filter
      62  	// structure.
      63  	lzma_filter filters[] = {
      64  		{ .id = LZMA_FILTER_X86, .options = NULL },
      65  		{ .id = LZMA_FILTER_LZMA2, .options = &opt_lzma2 },
      66  		{ .id = LZMA_VLI_UNKNOWN, .options = NULL },
      67  	};
      68  
      69  	// Initialize the encoder using the custom filter chain.
      70  	lzma_ret ret = lzma_stream_encoder(strm, filters, LZMA_CHECK_CRC64);
      71  
      72  	if (ret == LZMA_OK)
      73  		return true;
      74  
      75  	const char *msg;
      76  	switch (ret) {
      77  	case LZMA_MEM_ERROR:
      78  		msg = "Memory allocation failed";
      79  		break;
      80  
      81  	case LZMA_OPTIONS_ERROR:
      82  		// We are no longer using a plain preset so this error
      83  		// message has been edited accordingly compared to
      84  		// 01_compress_easy.c.
      85  		msg = "Specified filter chain is not supported";
      86  		break;
      87  
      88  	case LZMA_UNSUPPORTED_CHECK:
      89  		msg = "Specified integrity check is not supported";
      90  		break;
      91  
      92  	default:
      93  		msg = "Unknown error, possibly a bug";
      94  		break;
      95  	}
      96  
      97  	fprintf(stderr, "Error initializing the encoder: %s (error code %u)\n",
      98  			msg, ret);
      99  	return false;
     100  }
     101  
     102  
     103  // This function is identical to the one in 01_compress_easy.c.
     104  static bool
     105  compress(lzma_stream *strm, FILE *infile, FILE *outfile)
     106  {
     107  	lzma_action action = LZMA_RUN;
     108  
     109  	uint8_t inbuf[BUFSIZ];
     110  	uint8_t outbuf[BUFSIZ];
     111  
     112  	strm->next_in = NULL;
     113  	strm->avail_in = 0;
     114  	strm->next_out = outbuf;
     115  	strm->avail_out = sizeof(outbuf);
     116  
     117  	while (true) {
     118  		if (strm->avail_in == 0 && !feof(infile)) {
     119  			strm->next_in = inbuf;
     120  			strm->avail_in = fread(inbuf, 1, sizeof(inbuf),
     121  					infile);
     122  
     123  			if (ferror(infile)) {
     124  				fprintf(stderr, "Read error: %s\n",
     125  						strerror(errno));
     126  				return false;
     127  			}
     128  
     129  			if (feof(infile))
     130  				action = LZMA_FINISH;
     131  		}
     132  
     133  		lzma_ret ret = lzma_code(strm, action);
     134  
     135  		if (strm->avail_out == 0 || ret == LZMA_STREAM_END) {
     136  			size_t write_size = sizeof(outbuf) - strm->avail_out;
     137  
     138  			if (fwrite(outbuf, 1, write_size, outfile)
     139  					!= write_size) {
     140  				fprintf(stderr, "Write error: %s\n",
     141  						strerror(errno));
     142  				return false;
     143  			}
     144  
     145  			strm->next_out = outbuf;
     146  			strm->avail_out = sizeof(outbuf);
     147  		}
     148  
     149  		if (ret != LZMA_OK) {
     150  			if (ret == LZMA_STREAM_END)
     151  				return true;
     152  
     153  			const char *msg;
     154  			switch (ret) {
     155  			case LZMA_MEM_ERROR:
     156  				msg = "Memory allocation failed";
     157  				break;
     158  
     159  			case LZMA_DATA_ERROR:
     160  				msg = "File size limits exceeded";
     161  				break;
     162  
     163  			default:
     164  				msg = "Unknown error, possibly a bug";
     165  				break;
     166  			}
     167  
     168  			fprintf(stderr, "Encoder error: %s (error code %u)\n",
     169  					msg, ret);
     170  			return false;
     171  		}
     172  	}
     173  }
     174  
     175  
     176  extern int
     177  main(void)
     178  {
     179  	lzma_stream strm = LZMA_STREAM_INIT;
     180  
     181  	bool success = init_encoder(&strm);
     182  	if (success)
     183  		success = compress(&strm, stdin, stdout);
     184  
     185  	lzma_end(&strm);
     186  
     187  	if (fclose(stdout)) {
     188  		fprintf(stderr, "Write error: %s\n", strerror(errno));
     189  		success = false;
     190  	}
     191  
     192  	return success ? EXIT_SUCCESS : EXIT_FAILURE;
     193  }