examples: C++ and Windows compatibility
[wimlib] / examples / decompressfile.c
1 /*
2  * decompressfile.c - decompression API example
3  *
4  * The following copying information applies to this specific source code file:
5  *
6  * Written in 2014-2016 by Eric Biggers <ebiggers3@gmail.com>
7  *
8  * To the extent possible under law, the author(s) have dedicated all copyright
9  * and related and neighboring rights to this software to the public domain
10  * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
11  * Dedication (the "CC0").
12  *
13  * This software is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the CC0 for more details.
16  *
17  * You should have received a copy of the CC0 along with this software; if not
18  * see <http://creativecommons.org/publicdomain/zero/1.0/>.
19  */
20
21 /*
22  * This an example of using wimlib's compression API to decompress a file
23  * compressed with the compressfile.c program.
24  *
25  * This program does *not* have anything to do with WIM files other than the
26  * fact that this makes use of compression formats that are used in WIM files.
27  * This is purely an example of using the compression API.
28  *
29  * Compile with:
30  *
31  *    $ gcc decompressfile.c -o decompressfile -lwim
32  *
33  * Run with:
34  *
35  *    $ ./decompressfile INFILE OUTFILE
36  *
37  * For example:
38  *
39  *    $ ./compressfile book.txt book.txt.lzms LZMS 1048576
40  *    $ rm -f book.txt
41  *    $ ./decompressfile book.txt.lzms book.txt
42  *
43  * The compressed file format created here is simply a series of compressed
44  * chunks.  A real format would need to have checksums and other metadata.
45  */
46
47 #define _FILE_OFFSET_BITS 64
48
49 #if defined(_MSC_VER) && _MSC_VER < 1800 /* VS pre-2013? */
50 #  define PRIu64 "I64u"
51 #  define PRIu32 "u"
52 #else
53 #  define __STDC_FORMAT_MACROS 1
54 #  include <inttypes.h>
55 #endif
56
57 #include <wimlib.h>
58
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <stdarg.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #ifdef _WIN32
65 #  include <io.h>
66 #else
67 #  include <unistd.h>
68 #endif
69
70 /*
71  * Windows compatibility defines for string encoding.  Applications using wimlib
72  * that need to run on both UNIX and Windows will need to do something similar
73  * to this, whereas applications that only need to run on one or the other can
74  * just use their platform's convention directly.
75  */
76 #ifdef _WIN32
77 #  define main          wmain
78    typedef wchar_t      tchar;
79 #  define TS            "ls"
80 #  define topen         _wopen
81 #else
82    typedef char         tchar;
83 #  define TS            "s"
84 #  define topen         open
85 #  define O_BINARY      0
86 #endif
87
88 static void
89 fatal_error(int err, const char *format, ...)
90 {
91         va_list va;
92
93         va_start(va, format);
94         vfprintf(stderr, format, va);
95         if (err != 0)
96                 fprintf(stderr, ": %s\n", strerror(err));
97         else
98                 fputc('\n', stderr);
99         va_end(va);
100         exit(1);
101 }
102
103 static void
104 do_decompress(int in_fd, const tchar *in_filename,
105               int out_fd, const tchar *out_filename,
106               uint32_t chunk_size, struct wimlib_decompressor *decompressor)
107 {
108         uint64_t chunk_num;
109
110         char *ubuf = (char *)malloc(chunk_size);
111         char *cbuf = (char *)malloc(chunk_size - 1);
112
113         for (chunk_num = 1; ; chunk_num++) {
114                 int32_t bytes_read;
115                 uint32_t usize;
116                 uint32_t csize;
117
118                 /* Read chunk uncompressed and compressed sizes.  */
119                 bytes_read = read(in_fd, &usize, sizeof(uint32_t));
120                 if (bytes_read == 0)
121                         break;
122
123                 if (bytes_read != sizeof(uint32_t) ||
124                     read(in_fd, &csize, sizeof(uint32_t)) != sizeof(uint32_t))
125                 {
126                         fatal_error(errno, "Error reading \"%" TS"\"",
127                                     in_filename);
128                 }
129
130                 if (csize > usize || usize > chunk_size)
131                         fatal_error(0, "The data is invalid!");
132
133                 if (usize == csize) {
134                         if (read(in_fd, ubuf, usize) != (int32_t)usize) {
135                                 fatal_error(errno, "Error reading \"%" TS"\"",
136                                             in_filename);
137                         }
138                 } else {
139                         if (read(in_fd, cbuf, csize) != (int32_t)csize) {
140                                 fatal_error(errno, "Error reading \"%" TS"\"",
141                                             in_filename);
142                         }
143
144                         if (wimlib_decompress(cbuf, csize, ubuf, usize,
145                                               decompressor))
146                         {
147                                 fatal_error(0,
148                                             "The compressed data is invalid!");
149                         }
150                 }
151
152                 printf("Chunk %" PRIu64": %" PRIu32" => %" PRIu32" bytes\n",
153                        chunk_num, csize, usize);
154
155                 /* Output the uncompressed chunk size, the compressed chunk
156                  * size, then the chunk data.  Note: a real program would need
157                  * to output the chunk sizes in consistent endianness.  */
158                 if (write(out_fd, ubuf, usize) != (int32_t)usize) {
159                         fatal_error(errno, "Error writing to \"%" TS"\"",
160                                     out_filename);
161                 }
162         }
163         free(ubuf);
164         free(cbuf);
165 }
166
167 int main(int argc, tchar **argv)
168 {
169         const tchar *in_filename;
170         const tchar *out_filename;
171         int in_fd;
172         int out_fd;
173         uint32_t ctype32;
174         enum wimlib_compression_type ctype;
175         uint32_t chunk_size;
176         int ret;
177         struct wimlib_decompressor *decompressor;
178
179         if (argc != 3) {
180                 fprintf(stderr, "Usage: %" TS" INFILE OUTFILE\n", argv[0]);
181                 return 2;
182         }
183
184         in_filename = argv[1];
185         out_filename = argv[2];
186
187         /* Open input file and output file.  */
188         in_fd = topen(in_filename, O_RDONLY | O_BINARY);
189         if (in_fd < 0)
190                 fatal_error(errno, "Failed to open \"%" TS"\"", in_filename);
191         out_fd = topen(out_filename, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY,
192                        0644);
193         if (out_fd < 0)
194                 fatal_error(errno, "Failed to open \"%" TS"\"", out_filename);
195
196         /* Get compression type and chunk size.  */
197         if (read(in_fd, &ctype32, sizeof(uint32_t)) != sizeof(uint32_t) ||
198             read(in_fd, &chunk_size, sizeof(uint32_t)) != sizeof(uint32_t))
199                 fatal_error(errno, "Error reading from \"%" TS"\"", in_filename);
200         ctype = (enum wimlib_compression_type)ctype32;
201
202         /* Create a decompressor for the compression type and chunk size with
203          * the default parameters.  */
204         ret = wimlib_create_decompressor(ctype, chunk_size, &decompressor);
205         if (ret != 0)
206                 fatal_error(0, "Failed to create decompressor: %" TS,
207                             wimlib_get_error_string((enum wimlib_error_code)ret));
208
209         /* Decompress and write the data.  */
210         do_decompress(in_fd, in_filename,
211                       out_fd, out_filename,
212                       chunk_size, decompressor);
213
214         /* Cleanup and return.  */
215         if (close(out_fd))
216                 fatal_error(errno, "Error closing \"%" TS"\"", out_filename);
217         wimlib_free_decompressor(decompressor);
218         return 0;
219 }