configure.ac: fix trailing newline issue
[wimlib] / examples / compressfile.c
1 /*
2  * compressfile.c - compression 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 compress a file.
23  *
24  * This program does *not* have anything to do with WIM files other than the
25  * fact that this makes use of compression formats that are used in WIM files.
26  * This is purely an example of using the compression API.
27  *
28  * Compile with:
29  *
30  *    $ gcc compressfile.c -o compressfile -lwim
31  *
32  * Run with:
33  *
34  *    $ ./compressfile INFILE OUTFILE [LZX | XPRESS | LZMS] [chunk size]
35  *
36  *
37  * Use the decompressfile.c program to decompress the file.
38  *
39  * For example:
40  *
41  *    $ ./compressfile book.txt book.txt.lzms LZMS 1048576
42  *    $ rm -f book.txt
43  *    $ ./decompressfile book.txt.lzms book.txt
44  *
45  * The compressed file format created here is simply a series of compressed
46  * chunks.  A real format would need to have checksums and other metadata.
47  */
48
49 #define _FILE_OFFSET_BITS 64
50
51 #if defined(_MSC_VER) && _MSC_VER < 1800 /* VS pre-2013? */
52 #  define PRIu64 "I64u"
53 #  define PRIu32 "u"
54 #else
55 #  define __STDC_FORMAT_MACROS 1
56 #  include <inttypes.h>
57 #endif
58
59 #include <wimlib.h>
60
61 #include <errno.h>
62 #include <fcntl.h>
63 #include <stdarg.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #ifdef _WIN32
67 #  include <io.h>
68 #else
69 #  include <unistd.h>
70 #endif
71
72 /*
73  * Windows compatibility defines for string encoding.  Applications using wimlib
74  * that need to run on both UNIX and Windows will need to do something similar
75  * to this, whereas applications that only need to run on one or the other can
76  * just use their platform's convention directly.
77  */
78 #ifdef _WIN32
79 #  define main          wmain
80    typedef wchar_t      tchar;
81 #  define _T(text)      L##text
82 #  define T(text)       _T(text)
83 #  define TS            "ls"
84 #  define topen         _wopen
85 #  define tstrcmp       wcscmp
86 #  define tstrtol       wcstol
87 #else
88    typedef char         tchar;
89 #  define T(text)       text
90 #  define TS            "s"
91 #  define topen         open
92 #  define O_BINARY      0
93 #  define tstrcmp       strcmp
94 #  define tstrtol       strtol
95 #endif
96
97 static void
98 fatal_error(int err, const char *format, ...)
99 {
100         va_list va;
101
102         va_start(va, format);
103         vfprintf(stderr, format, va);
104         if (err != 0)
105                 fprintf(stderr, ": %s\n", strerror(err));
106         else
107                 fputc('\n', stderr);
108         va_end(va);
109         exit(1);
110 }
111
112 static void
113 do_compress(int in_fd, const tchar *in_filename,
114             int out_fd, const tchar *out_filename,
115             uint32_t chunk_size, struct wimlib_compressor *compressor)
116 {
117         char *ubuf = (char *)malloc(chunk_size);
118         char *cbuf = (char *)malloc(chunk_size - 1);
119         uint64_t chunk_num;
120
121         for (chunk_num = 1; ; chunk_num++) {
122                 int32_t bytes_read;
123                 size_t csize;
124                 char *out_buf;
125                 uint32_t out_size;
126                 uint32_t usize;
127
128                 /* Read next chunk of data to compress.  */
129                 bytes_read = read(in_fd, ubuf, chunk_size);
130                 if (bytes_read <= 0) {
131                         if (bytes_read == 0)
132                                 break;
133                         fatal_error(errno, "Error reading \"%" TS"\"",
134                                     in_filename);
135                 }
136
137                 /* Compress the chunk.  */
138                 usize = bytes_read;
139
140                 csize = wimlib_compress(ubuf, usize, cbuf, usize - 1, compressor);
141                 if (csize != 0) {
142                         /* Chunk was compressed; use the compressed data.  */
143                         out_buf = cbuf;
144                         out_size = csize;
145                 } else {
146                         /* Chunk did not compress to less than original size;
147                          * use the uncompressed data.  */
148                         out_buf = ubuf;
149                         out_size = usize;
150                 }
151
152                 printf("Chunk %" PRIu64": %" PRIu32" => %" PRIu32" bytes\n",
153                        chunk_num, usize, out_size);
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, &usize, sizeof(uint32_t)) != sizeof(uint32_t) ||
159                     write(out_fd, &out_size, sizeof(uint32_t)) != sizeof(uint32_t) ||
160                     write(out_fd, out_buf, out_size) != (int32_t)out_size)
161                 {
162                         fatal_error(errno, "Error writing to \"%" TS"\"",
163                                     out_filename);
164                 }
165         }
166         free(ubuf);
167         free(cbuf);
168 }
169
170 int main(int argc, tchar **argv)
171 {
172         const tchar *in_filename;
173         const tchar *out_filename;
174         int in_fd;
175         int out_fd;
176         struct wimlib_compressor *compressor;
177         enum wimlib_compression_type ctype = WIMLIB_COMPRESSION_TYPE_LZX;
178         uint32_t ctype32;
179         uint32_t chunk_size = 32768;
180         int ret;
181
182         if (argc < 3 || argc > 5) {
183                 fprintf(stderr, "Usage: %" TS" INFILE OUTFILE "
184                         "[LZX | XPRESS | LZMS] [chunk size]\n", argv[0]);
185                 return 2;
186         }
187
188         in_filename = argv[1];
189         out_filename = argv[2];
190
191         /* Parse compression type (optional)  */
192         if (argc >= 4) {
193                 if (!tstrcmp(argv[3], T("LZX")))
194                         ctype = WIMLIB_COMPRESSION_TYPE_LZX;
195                 else if (!tstrcmp(argv[3], T("XPRESS")))
196                         ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
197                 else if (!tstrcmp(argv[3], T("LZMS")))
198                         ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
199                 else
200                         fatal_error(0,
201                                     "Unrecognized compression type \"%" TS"\"",
202                                     argv[3]);
203         }
204         /* Parse chunk size (optional).  */
205         if (argc >= 5)
206                 chunk_size = tstrtol(argv[4], NULL, 10);
207
208         /* Open input file and output file.  */
209         in_fd = topen(in_filename, O_RDONLY | O_BINARY);
210         if (in_fd < 0)
211                 fatal_error(errno, "Failed to open \"%" TS"\"", in_filename);
212         out_fd = topen(out_filename, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY,
213                        0644);
214         if (out_fd < 0)
215                 fatal_error(errno, "Failed to open \"%" TS"s\"", out_filename);
216
217         /* Create a compressor for the compression type and chunk size with the
218          * default parameters.  */
219         ret = wimlib_create_compressor(ctype, chunk_size, 0, &compressor);
220         if (ret != 0)
221                 fatal_error(0, "Failed to create compressor: %" TS,
222                             wimlib_get_error_string((enum wimlib_error_code)ret));
223
224         ctype32 = (uint32_t)ctype;
225         /* Write compression type and chunk size to the file.  */
226         if (write(out_fd, &ctype32, sizeof(uint32_t)) != sizeof(uint32_t) ||
227             write(out_fd, &chunk_size, sizeof(uint32_t)) != sizeof(uint32_t))
228         {
229                 fatal_error(errno, "Error writing to \"%" TS"\"", out_filename);
230         }
231
232         /* Compress and write the data.  */
233         do_compress(in_fd, in_filename,
234                     out_fd, out_filename,
235                     chunk_size, compressor);
236
237         /* Cleanup and return.  */
238         if (close(out_fd))
239                 fatal_error(errno, "Error closing \"%" TS"\"", out_filename);
240         wimlib_free_compressor(compressor);
241         return 0;
242 }