Use more comprehensive public domain dedications
[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 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 _GNU_SOURCE
50 #define _FILE_OFFSET_BITS 64
51
52 #include <wimlib.h>
53
54 #include <errno.h>
55 #include <error.h>
56 #include <fcntl.h>
57 #include <inttypes.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 static void
63 do_compress(int in_fd, const char *in_filename,
64             int out_fd, const char *out_filename,
65             uint32_t chunk_size, struct wimlib_compressor *compressor)
66 {
67         char *ubuf = malloc(chunk_size);
68         char *cbuf = malloc(chunk_size - 1);
69         uint64_t chunk_num;
70
71         for (chunk_num = 1; ; chunk_num++) {
72                 ssize_t bytes_read;
73                 size_t csize;
74                 char *out_buf;
75                 uint32_t out_size;
76                 uint32_t usize;
77
78                 /* Read next chunk of data to compress.  */
79                 bytes_read = read(in_fd, ubuf, chunk_size);
80                 if (bytes_read <= 0) {
81                         if (bytes_read == 0)
82                                 break;
83                         error(1, errno, "Error reading \"%s\"", in_filename);
84                 }
85
86                 /* Compress the chunk.  */
87                 usize = bytes_read;
88
89                 csize = wimlib_compress(ubuf, usize, cbuf, usize - 1, compressor);
90                 if (csize != 0) {
91                         /* Chunk was compressed; use the compressed data.  */
92                         out_buf = cbuf;
93                         out_size = csize;
94                 } else {
95                         /* Chunk did not compress to less than original size;
96                          * use the uncompressed data.  */
97                         out_buf = ubuf;
98                         out_size = usize;
99                 }
100
101                 printf("Chunk %"PRIu64": %"PRIu32" => %"PRIu32" bytes\n",
102                        chunk_num, usize, out_size);
103
104                 /* Output the uncompressed chunk size, the compressed chunk
105                  * size, then the chunk data.  Note: a real program would need
106                  * to output the chunk sizes in consistent endianness.  */
107                 if (write(out_fd, &usize, sizeof(uint32_t)) != sizeof(uint32_t) ||
108                     write(out_fd, &out_size, sizeof(uint32_t)) != sizeof(uint32_t) ||
109                     write(out_fd, out_buf, out_size) != out_size)
110                 {
111                         error(1, errno, "Error writing to \"%s\"",
112                               out_filename);
113                 }
114         }
115         free(ubuf);
116         free(cbuf);
117 }
118
119 int main(int argc, char **argv)
120 {
121         const char *in_filename;
122         const char *out_filename;
123         int in_fd;
124         int out_fd;
125         struct wimlib_compressor *compressor;
126         int ctype = WIMLIB_COMPRESSION_TYPE_LZX;
127         uint32_t chunk_size = 32768;
128         int ret;
129
130         if (argc < 3 || argc > 5) {
131                 fprintf(stderr, "Usage: %s INFILE OUTFILE "
132                         "[LZX | XPRESS | LZMS] [chunk size]\n", argv[0]);
133                 return 2;
134         }
135
136         in_filename = argv[1];
137         out_filename = argv[2];
138
139         /* Parse compression type (optional)  */
140         if (argc >= 4) {
141                 if (!strcmp(argv[3], "LZX"))
142                         ctype = WIMLIB_COMPRESSION_TYPE_LZX;
143                 else if (!strcmp(argv[3], "XPRESS"))
144                         ctype = WIMLIB_COMPRESSION_TYPE_XPRESS;
145                 else if (!strcmp(argv[3], "LZMS"))
146                         ctype = WIMLIB_COMPRESSION_TYPE_LZMS;
147                 else
148                         error(1, 0, "Unrecognized compression type \"%s\"", argv[3]);
149         }
150         /* Parse chunk size (optional).  */
151         if (argc >= 5)
152                 chunk_size = atoi(argv[4]);
153
154         /* Open input file and output file.  */
155         in_fd = open(in_filename, O_RDONLY);
156         if (in_fd < 0)
157                 error(1, errno, "Failed to open \"%s\"", in_filename);
158         out_fd = open(out_filename, O_WRONLY | O_TRUNC | O_CREAT, 0644);
159         if (out_fd < 0)
160                 error(1, errno, "Failed to open \"%s\"", out_filename);
161
162         /* Create a compressor for the compression type and chunk size with the
163          * default parameters.  */
164         ret = wimlib_create_compressor(ctype, chunk_size, 0, &compressor);
165         if (ret != 0)
166                 error(1, 0, "Failed to create compressor: %s",
167                       wimlib_get_error_string(ret));
168
169         uint32_t ctype32 = ctype;
170         /* Write compression type and chunk size to the file.  */
171         if (write(out_fd, &ctype32, sizeof(uint32_t)) != sizeof(uint32_t) ||
172             write(out_fd, &chunk_size, sizeof(uint32_t)) != sizeof(uint32_t))
173         {
174                 error(1, errno, "Error writing to \"%s\"",
175                       out_filename);
176         }
177
178         /* Compress and write the data.  */
179         do_compress(in_fd, in_filename,
180                     out_fd, out_filename,
181                     chunk_size, compressor);
182
183         /* Cleanup and return.  */
184         if (close(out_fd))
185                 error(1, errno, "Error closing \"%s\"", out_filename);
186         wimlib_free_compressor(compressor);
187         return 0;
188 }