6887e0e4cae389c676bb023a1a46eab269ac6327
[wimlib] / examples / compressfile.c
1 /*
2  * compressfile.c
3  *
4  * An example of using wimlib's compression API to compress a file.
5  *
6  * This program does *not* have anything to do with WIM files other than the
7  * fact that this makes use of compression formats that are used in WIM files.
8  * This is purely an example of using the compression API.
9  *
10  * Compile with:
11  *
12  *    $ gcc compressfile.c -o compressfile -lwim
13  *
14  * Run with:
15  *
16  *    $ ./compressfile INFILE OUTFILE [LZX | XPRESS | LZMS] [chunk size]
17  *
18  *
19  * Use the decompressfile.c program to decompress the file.
20  *
21  * For example:
22  *
23  *    $ ./compressfile book.txt book.txt.lzms LZMS 1048576
24  *    $ rm -f book.txt
25  *    $ ./decompressfile book.txt.lzms book.txt
26  *
27  * The compressed file format created here is simply a series of compressed
28  * chunks.  A real format would need to have checksums and other metadata.
29  *
30  * The author dedicates this file to the public domain.
31  * You can do whatever you want with this file.
32  */
33
34 #define _GNU_SOURCE
35 #define _FILE_OFFSET_BITS 64
36
37 #include <wimlib.h>
38
39 #include <errno.h>
40 #include <error.h>
41 #include <fcntl.h>
42 #include <inttypes.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 static void
48 do_compress(int in_fd, const char *in_filename,
49             int out_fd, const char *out_filename,
50             uint32_t chunk_size, struct wimlib_compressor *compressor)
51 {
52         char *ubuf = malloc(chunk_size);
53         char *cbuf = malloc(chunk_size - 1);
54         uint64_t chunk_num;
55
56         for (chunk_num = 1; ; chunk_num++) {
57                 ssize_t bytes_read;
58                 size_t csize;
59                 char *out_buf;
60                 uint32_t out_size;
61                 uint32_t usize;
62
63                 /* Read next chunk of data to compress.  */
64                 bytes_read = read(in_fd, ubuf, chunk_size);
65                 if (bytes_read <= 0) {
66                         if (bytes_read == 0)
67                                 break;
68                         error(1, errno, "Error reading \"%s\"", in_filename);
69                 }
70
71                 /* Compress the chunk.  */
72                 usize = bytes_read;
73
74                 csize = wimlib_compress(ubuf, usize, cbuf, usize - 1, compressor);
75                 if (csize != 0) {
76                         /* Chunk was compressed; use the compressed data.  */
77                         out_buf = cbuf;
78                         out_size = csize;
79                 } else {
80                         /* Chunk did not compress to less than original size;
81                          * use the uncompressed data.  */
82                         out_buf = ubuf;
83                         out_size = usize;
84                 }
85
86                 printf("Chunk %"PRIu64": %"PRIu32" => %"PRIu32" bytes\n",
87                        chunk_num, usize, out_size);
88
89                 /* Output the uncompressed chunk size, the compressed chunk
90                  * size, then the chunk data.  Note: a real program would need
91                  * to output the chunk sizes in consistent endianness.  */
92                 if (write(out_fd, &usize, sizeof(uint32_t)) != sizeof(uint32_t) ||
93                     write(out_fd, &out_size, sizeof(uint32_t)) != sizeof(uint32_t) ||
94                     write(out_fd, out_buf, out_size) != out_size)
95                 {
96                         error(1, errno, "Error writing to \"%s\"",
97                               out_filename);
98                 }
99         }
100         free(ubuf);
101         free(cbuf);
102 }
103
104 int main(int argc, char **argv)
105 {
106         const char *in_filename;
107         const char *out_filename;
108         int in_fd;
109         int out_fd;
110         struct wimlib_compressor *compressor;
111         int ctype = WIMLIB_COMPRESSION_TYPE_LZX;
112         uint32_t chunk_size = 32768;
113         int ret;
114
115         if (argc < 3 || argc > 5) {
116                 fprintf(stderr, "Usage: %s INFILE OUTFILE "
117                         "[LZX | XPRESS | LZMS] [chunk size]\n", argv[0]);
118                 return 2;
119         }
120
121         in_filename = argv[1];
122         out_filename = argv[2];
123
124         /* Parse compression type (optional)  */
125         if (argc >= 4) {
126                 if (!strcmp(argv[3], "LZX"))
127                         ctype = WIMLIB_COMPRESSION_TYPE_LZX;
128                 else if (!strcmp(argv[3], "XPRESS"))
129                         ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
130                 else if (!strcmp(argv[3], "LZMS"))
131                         ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
132                 else
133                         error(1, 0, "Unrecognized compression type \"%s\"", argv[3]);
134         }
135         /* Parse chunk size (optional).  */
136         if (argc >= 5)
137                 chunk_size = atoi(argv[4]);
138
139         /* Open input file and output file.  */
140         in_fd = open(in_filename, O_RDONLY);
141         if (in_fd < 0)
142                 error(1, errno, "Failed to open \"%s\"", in_filename);
143         out_fd = open(out_filename, O_WRONLY | O_TRUNC | O_CREAT, 0644);
144         if (out_fd < 0)
145                 error(1, errno, "Failed to open \"%s\"", out_filename);
146
147         /* Create a compressor for the compression type and chunk size with the
148          * default parameters.  */
149         ret = wimlib_create_compressor(ctype, chunk_size, 0, &compressor);
150         if (ret != 0)
151                 error(1, 0, "Failed to create compressor: %s",
152                       wimlib_get_error_string(ret));
153
154         uint32_t ctype32 = ctype;
155         /* Write compression type and chunk size to the file.  */
156         if (write(out_fd, &ctype32, sizeof(uint32_t)) != sizeof(uint32_t) ||
157             write(out_fd, &chunk_size, sizeof(uint32_t)) != sizeof(uint32_t))
158         {
159                 error(1, errno, "Error writing to \"%s\"",
160                       out_filename);
161         }
162
163         /* Compress and write the data.  */
164         do_compress(in_fd, in_filename,
165                     out_fd, out_filename,
166                     chunk_size, compressor);
167
168         /* Cleanup and return.  */
169         if (close(out_fd))
170                 error(1, errno, "Error closing \"%s\"", out_filename);
171         wimlib_free_compressor(compressor);
172         return 0;
173 }