]> wimlib.net Git - wimlib/blob - examples/compressfile.c
v1.14.4
[wimlib] / examples / compressfile.c
1 /*
2  * compressfile.c - compression API example
3  *
4  * Copyright 2022 Eric Biggers
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 /*
29  * This an example of using wimlib's compression API to compress a file.
30  *
31  * This program does *not* have anything to do with WIM files other than the
32  * fact that this makes use of compression formats that are used in WIM files.
33  * This is purely an example of using the compression API.
34  *
35  * Compile with:
36  *
37  *    $ gcc compressfile.c -o compressfile -lwim
38  *
39  * Run with:
40  *
41  *    $ ./compressfile INFILE OUTFILE [LZX | XPRESS | LZMS] [chunk size]
42  *
43  *
44  * Use the decompressfile.c program to decompress the file.
45  *
46  * For example:
47  *
48  *    $ ./compressfile book.txt book.txt.lzms LZMS 1048576
49  *    $ rm -f book.txt
50  *    $ ./decompressfile book.txt.lzms book.txt
51  *
52  * The compressed file format created here is simply a series of compressed
53  * chunks.  A real format would need to have checksums and other metadata.
54  */
55
56 #define _FILE_OFFSET_BITS 64
57
58 #if defined(_MSC_VER) && _MSC_VER < 1800 /* VS pre-2013? */
59 #  define PRIu64 "I64u"
60 #  define PRIu32 "u"
61 #else
62 #  define __STDC_FORMAT_MACROS 1
63 #  include <inttypes.h>
64 #endif
65
66 #include <wimlib.h>
67
68 #include <errno.h>
69 #include <fcntl.h>
70 #include <stdarg.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #ifdef _WIN32
74 #  include <io.h>
75 #else
76 #  include <unistd.h>
77 #endif
78
79 /*
80  * Windows compatibility defines for string encoding.  Applications using wimlib
81  * that need to run on both UNIX and Windows will need to do something similar
82  * to this, whereas applications that only need to run on one or the other can
83  * just use their platform's convention directly.
84  */
85 #ifdef _WIN32
86 #  define main          wmain
87    typedef wchar_t      tchar;
88 #  define _T(text)      L##text
89 #  define T(text)       _T(text)
90 #  define TS            "ls"
91 #  define topen         _wopen
92 #  define tstrcmp       wcscmp
93 #  define tstrtol       wcstol
94 #else
95    typedef char         tchar;
96 #  define T(text)       text
97 #  define TS            "s"
98 #  define topen         open
99 #  define O_BINARY      0
100 #  define tstrcmp       strcmp
101 #  define tstrtol       strtol
102 #endif
103
104 static void
105 fatal_error(int err, const char *format, ...)
106 {
107         va_list va;
108
109         va_start(va, format);
110         vfprintf(stderr, format, va);
111         if (err != 0)
112                 fprintf(stderr, ": %s\n", strerror(err));
113         else
114                 fputc('\n', stderr);
115         va_end(va);
116         exit(1);
117 }
118
119 static void
120 do_compress(int in_fd, const tchar *in_filename,
121             int out_fd, const tchar *out_filename,
122             uint32_t chunk_size, struct wimlib_compressor *compressor)
123 {
124         char *ubuf = (char *)malloc(chunk_size);
125         char *cbuf = (char *)malloc(chunk_size - 1);
126         uint64_t chunk_num;
127
128         for (chunk_num = 1; ; chunk_num++) {
129                 int32_t bytes_read;
130                 size_t csize;
131                 char *out_buf;
132                 uint32_t out_size;
133                 uint32_t usize;
134
135                 /* Read next chunk of data to compress.  */
136                 bytes_read = read(in_fd, ubuf, chunk_size);
137                 if (bytes_read <= 0) {
138                         if (bytes_read == 0)
139                                 break;
140                         fatal_error(errno, "Error reading \"%" TS"\"",
141                                     in_filename);
142                 }
143
144                 /* Compress the chunk.  */
145                 usize = bytes_read;
146
147                 csize = wimlib_compress(ubuf, usize, cbuf, usize - 1, compressor);
148                 if (csize != 0) {
149                         /* Chunk was compressed; use the compressed data.  */
150                         out_buf = cbuf;
151                         out_size = csize;
152                 } else {
153                         /* Chunk did not compress to less than original size;
154                          * use the uncompressed data.  */
155                         out_buf = ubuf;
156                         out_size = usize;
157                 }
158
159                 printf("Chunk %" PRIu64": %" PRIu32" => %" PRIu32" bytes\n",
160                        chunk_num, usize, out_size);
161
162                 /* Output the uncompressed chunk size, the compressed chunk
163                  * size, then the chunk data.  Note: a real program would need
164                  * to output the chunk sizes in consistent endianness.  */
165                 if (write(out_fd, &usize, sizeof(uint32_t)) != sizeof(uint32_t) ||
166                     write(out_fd, &out_size, sizeof(uint32_t)) != sizeof(uint32_t) ||
167                     write(out_fd, out_buf, out_size) != (int32_t)out_size)
168                 {
169                         fatal_error(errno, "Error writing to \"%" TS"\"",
170                                     out_filename);
171                 }
172         }
173         free(ubuf);
174         free(cbuf);
175 }
176
177 int main(int argc, tchar **argv)
178 {
179         const tchar *in_filename;
180         const tchar *out_filename;
181         int in_fd;
182         int out_fd;
183         struct wimlib_compressor *compressor;
184         enum wimlib_compression_type ctype = WIMLIB_COMPRESSION_TYPE_LZX;
185         uint32_t ctype32;
186         uint32_t chunk_size = 32768;
187         int ret;
188
189         if (argc < 3 || argc > 5) {
190                 fprintf(stderr, "Usage: %" TS" INFILE OUTFILE "
191                         "[LZX | XPRESS | LZMS] [chunk size]\n", argv[0]);
192                 return 2;
193         }
194
195         in_filename = argv[1];
196         out_filename = argv[2];
197
198         /* Parse compression type (optional)  */
199         if (argc >= 4) {
200                 if (!tstrcmp(argv[3], T("LZX")))
201                         ctype = WIMLIB_COMPRESSION_TYPE_LZX;
202                 else if (!tstrcmp(argv[3], T("XPRESS")))
203                         ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
204                 else if (!tstrcmp(argv[3], T("LZMS")))
205                         ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
206                 else
207                         fatal_error(0,
208                                     "Unrecognized compression type \"%" TS"\"",
209                                     argv[3]);
210         }
211         /* Parse chunk size (optional).  */
212         if (argc >= 5)
213                 chunk_size = tstrtol(argv[4], NULL, 10);
214
215         /* Open input file and output file.  */
216         in_fd = topen(in_filename, O_RDONLY | O_BINARY);
217         if (in_fd < 0)
218                 fatal_error(errno, "Failed to open \"%" TS"\"", in_filename);
219         out_fd = topen(out_filename, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY,
220                        0644);
221         if (out_fd < 0)
222                 fatal_error(errno, "Failed to open \"%" TS"s\"", out_filename);
223
224         /* Create a compressor for the compression type and chunk size with the
225          * default parameters.  */
226         ret = wimlib_create_compressor(ctype, chunk_size, 0, &compressor);
227         if (ret != 0)
228                 fatal_error(0, "Failed to create compressor: %" TS,
229                             wimlib_get_error_string((enum wimlib_error_code)ret));
230
231         ctype32 = (uint32_t)ctype;
232         /* Write compression type and chunk size to the file.  */
233         if (write(out_fd, &ctype32, sizeof(uint32_t)) != sizeof(uint32_t) ||
234             write(out_fd, &chunk_size, sizeof(uint32_t)) != sizeof(uint32_t))
235         {
236                 fatal_error(errno, "Error writing to \"%" TS"\"", out_filename);
237         }
238
239         /* Compress and write the data.  */
240         do_compress(in_fd, in_filename,
241                     out_fd, out_filename,
242                     chunk_size, compressor);
243
244         /* Cleanup and return.  */
245         if (close(out_fd))
246                 fatal_error(errno, "Error closing \"%" TS"\"", out_filename);
247         wimlib_free_compressor(compressor);
248         return 0;
249 }