1 // Copyright 2018 The Wuffs Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // ----------------
16
17 /*
18 zcat decodes gzip'ed data to stdout. It is similar to the standard /bin/zcat
19 program, except that this example program only reads from stdin. On Linux, it
20 also self-imposes a SECCOMP_MODE_STRICT sandbox. To run:
21
22 $CC zcat.c && ./a.out < ../../test/data/romeo.txt.gz; rm -f a.out
23
24 for a C compiler $CC, such as clang or gcc.
25 */
26
27 #include <errno.h>
28 #include <unistd.h>
29
30 // Wuffs ships as a "single file C library" or "header file library" as per
31 // https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
32 //
33 // To use that single file as a "foo.c"-like implementation, instead of a
34 // "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or
35 // compiling it.
36 #define WUFFS_IMPLEMENTATION
37
38 // Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
39 // release/c/etc.c whitelist which parts of Wuffs to build. That file contains
40 // the entire Wuffs standard library, implementing a variety of codecs and file
41 // formats. Without this macro definition, an optimizing compiler or linker may
42 // very well discard Wuffs code for unused codecs, but listing the Wuffs
43 // modules we use makes that process explicit. Preprocessing means that such
44 // code simply isn't compiled.
45 #define WUFFS_CONFIG__MODULES
46 #define WUFFS_CONFIG__MODULE__BASE
47 #define WUFFS_CONFIG__MODULE__CRC32
48 #define WUFFS_CONFIG__MODULE__DEFLATE
49 #define WUFFS_CONFIG__MODULE__GZIP
50
51 // If building this program in an environment that doesn't easily accommodate
52 // relative includes, you can use the script/inline-c-relative-includes.go
53 // program to generate a stand-alone C file.
54 #include "../../release/c/wuffs-unsupported-snapshot.c"
55
56 #if defined(__linux__)
57 #include <linux/prctl.h>
58 #include <linux/seccomp.h>
59 #include <sys/prctl.h>
60 #include <sys/syscall.h>
61 #define WUFFS_EXAMPLE_USE_SECCOMP
62 #endif
63
64 #ifndef DST_BUFFER_SIZE
65 #define DST_BUFFER_SIZE (128 * 1024)
66 #endif
67
68 #ifndef SRC_BUFFER_SIZE
69 #define SRC_BUFFER_SIZE (128 * 1024)
70 #endif
71
72 #define WORK_BUFFER_SIZE WUFFS_GZIP__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE
73
74 uint8_t dst_buffer[DST_BUFFER_SIZE];
75 uint8_t src_buffer[SRC_BUFFER_SIZE];
76 #if WORK_BUFFER_SIZE > 0
77 uint8_t work_buffer[WORK_BUFFER_SIZE];
78 #else
79 // Not all C/C++ compilers support 0-length arrays.
80 uint8_t work_buffer[1];
81 #endif
82
83 // ignore_return_value suppresses errors from -Wall -Werror.
ignore_return_value(int ignored)84 static void ignore_return_value(int ignored) {}
85
decode()86 static const char* decode() {
87 wuffs_gzip__decoder dec;
88 const char* status =
89 wuffs_gzip__decoder__initialize(&dec, sizeof dec, WUFFS_VERSION, 0);
90 if (status) {
91 return status;
92 }
93
94 wuffs_base__io_buffer dst;
95 dst.data.ptr = dst_buffer;
96 dst.data.len = DST_BUFFER_SIZE;
97 dst.meta.wi = 0;
98 dst.meta.ri = 0;
99 dst.meta.pos = 0;
100 dst.meta.closed = false;
101
102 wuffs_base__io_buffer src;
103 src.data.ptr = src_buffer;
104 src.data.len = SRC_BUFFER_SIZE;
105 src.meta.wi = 0;
106 src.meta.ri = 0;
107 src.meta.pos = 0;
108 src.meta.closed = false;
109
110 while (true) {
111 const int stdin_fd = 0;
112 ssize_t n =
113 read(stdin_fd, src.data.ptr + src.meta.wi, src.data.len - src.meta.wi);
114 if (n < 0) {
115 if (errno != EINTR) {
116 return strerror(errno);
117 }
118 continue;
119 }
120 src.meta.wi += n;
121 if (n == 0) {
122 src.meta.closed = true;
123 }
124
125 while (true) {
126 status = wuffs_gzip__decoder__decode_io_writer(
127 &dec, &dst, &src,
128 wuffs_base__make_slice_u8(work_buffer, WORK_BUFFER_SIZE));
129
130 if (dst.meta.wi) {
131 // TODO: handle EINTR and other write errors; see "man 2 write".
132 const int stdout_fd = 1;
133 ignore_return_value(write(stdout_fd, dst_buffer, dst.meta.wi));
134 dst.meta.ri = dst.meta.wi;
135 wuffs_base__io_buffer__compact(&dst);
136 }
137
138 if (status == wuffs_base__suspension__short_read) {
139 break;
140 }
141 if (status == wuffs_base__suspension__short_write) {
142 continue;
143 }
144 return status;
145 }
146
147 wuffs_base__io_buffer__compact(&src);
148 if (src.meta.wi == src.data.len) {
149 return "internal error: no I/O progress possible";
150 }
151 }
152 }
153
fail(const char * msg)154 int fail(const char* msg) {
155 const int stderr_fd = 2;
156 ignore_return_value(write(stderr_fd, msg, strnlen(msg, 4095)));
157 ignore_return_value(write(stderr_fd, "\n", 1));
158 return 1;
159 }
160
main(int argc,char ** argv)161 int main(int argc, char** argv) {
162 #if defined(WUFFS_EXAMPLE_USE_SECCOMP)
163 prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
164 #endif
165
166 const char* status = decode();
167 int status_code = status ? fail(status) : 0;
168
169 #if defined(WUFFS_EXAMPLE_USE_SECCOMP)
170 // Call SYS_exit explicitly instead of SYS_exit_group implicitly.
171 // SECCOMP_MODE_STRICT allows only the former.
172 syscall(SYS_exit, status_code);
173 #endif
174 return status_code;
175 }
176