File: | mimegen.c |
Warning: | line 266, column 17 Potential leak of memory pointed to by 'k' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * lwan - simple web server | |||
3 | * Copyright (c) 2016 L. A. F. Pereira <l@tia.mat.br> | |||
4 | * | |||
5 | * This program is free software; you can redistribute it and/or | |||
6 | * modify it under the terms of the GNU General Public License | |||
7 | * as published by the Free Software Foundation; either version 2 | |||
8 | * of the License, or any later version. | |||
9 | * | |||
10 | * This program is distributed in the hope that it will be useful, | |||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
13 | * GNU General Public License for more details. | |||
14 | * | |||
15 | * You should have received a copy of the GNU General Public License | |||
16 | * along with this program; if not, write to the Free Software | |||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
18 | */ | |||
19 | ||||
20 | #include <assert.h> | |||
21 | #include <ctype.h> | |||
22 | #include <errno(*__errno_location ()).h> | |||
23 | #include <fcntl.h> | |||
24 | #include <stdint.h> | |||
25 | #include <stdio.h> | |||
26 | #include <stdlib.h> | |||
27 | #include <string.h> | |||
28 | ||||
29 | #if defined(HAVE_BROTLI) | |||
30 | #include <brotli/encode.h> | |||
31 | #elif defined(HAVE_ZSTD) | |||
32 | #include <zstd.h> | |||
33 | #elif defined(HAVE_ZOPFLI) | |||
34 | #include <zopfli/zopfli.h> | |||
35 | #else | |||
36 | #include <zlib.h> | |||
37 | #endif | |||
38 | ||||
39 | #include "../../lib/hash.h" | |||
40 | ||||
41 | struct output { | |||
42 | char *ptr; | |||
43 | size_t used, capacity; | |||
44 | }; | |||
45 | ||||
46 | static int | |||
47 | output_append_full(struct output *output, const char *str, size_t str_len) | |||
48 | { | |||
49 | size_t total_size = output->used + str_len; | |||
50 | ||||
51 | if (total_size >= output->capacity) { | |||
52 | char *tmp; | |||
53 | ||||
54 | while (total_size >= output->capacity) | |||
55 | output->capacity *= 2; | |||
56 | ||||
57 | tmp = realloc(output->ptr, output->capacity); | |||
58 | if (!tmp) | |||
59 | return -errno(*__errno_location ()); | |||
60 | ||||
61 | output->ptr = tmp; | |||
62 | } | |||
63 | ||||
64 | memcpy(output->ptr + output->used, str, str_len); | |||
65 | output->used = total_size; | |||
66 | ||||
67 | return 0; | |||
68 | } | |||
69 | ||||
70 | static int output_append_padded(struct output *output, const char *str) | |||
71 | { | |||
72 | size_t str_len = strlen(str); | |||
73 | ||||
74 | assert(str_len <= 8)((void) sizeof ((str_len <= 8) ? 1 : 0), __extension__ ({ if (str_len <= 8) ; else __assert_fail ("str_len <= 8", "/home/buildbot/lwan-worker/clang-analyze/build/src/bin/tools/mimegen.c" , 74, __extension__ __PRETTY_FUNCTION__); })); | |||
75 | ||||
76 | int r = output_append_full(output, str, str_len); | |||
77 | if (r < 0) | |||
78 | return r; | |||
79 | ||||
80 | if (str_len != 8) | |||
81 | return output_append_full(output, "\0\0\0\0\0\0\0\0", 8 - str_len); | |||
82 | ||||
83 | return 0; | |||
84 | } | |||
85 | ||||
86 | static int output_append(struct output *output, const char *str) | |||
87 | { | |||
88 | return output_append_full(output, str, strlen(str) + 1); | |||
89 | } | |||
90 | ||||
91 | static int compare_ext(const void *a, const void *b) | |||
92 | { | |||
93 | const char **exta = (const char **)a; | |||
94 | const char **extb = (const char **)b; | |||
95 | ||||
96 | return strcasecmp(*exta, *extb); | |||
97 | } | |||
98 | ||||
99 | static char *strend(char *str, char ch) | |||
100 | { | |||
101 | str = strchr(str, ch); | |||
102 | if (str) { | |||
103 | *str = '\0'; | |||
104 | return str + 1; | |||
105 | } | |||
106 | return NULL((void*)0); | |||
107 | } | |||
108 | ||||
109 | static char *compress_output(const struct output *output, size_t *outlen) | |||
110 | { | |||
111 | char *compressed; | |||
112 | ||||
113 | #if defined(HAVE_BROTLI) | |||
114 | *outlen = BrotliEncoderMaxCompressedSize(output->used); | |||
115 | ||||
116 | compressed = malloc(*outlen); | |||
117 | if (!compressed) { | |||
118 | fprintf(stderrstderr, "Could not allocate memory for compressed data\n"); | |||
119 | exit(1); | |||
120 | } | |||
121 | ||||
122 | if (BrotliEncoderCompress(BROTLI_MAX_QUALITY11, BROTLI_MAX_WINDOW_BITS24, | |||
123 | BROTLI_MODE_TEXT, output->used, | |||
124 | (const unsigned char *)output->ptr, outlen, | |||
125 | (unsigned char *)compressed) != BROTLI_TRUE1) { | |||
126 | fprintf(stderrstderr, "Could not compress mime type table with Brotli\n"); | |||
127 | exit(1); | |||
128 | } | |||
129 | #elif defined(HAVE_ZSTD) | |||
130 | *outlen = ZSTD_compressBound(output->used); | |||
131 | ||||
132 | compressed = malloc(*outlen); | |||
133 | if (!compressed) { | |||
134 | fprintf(stderrstderr, "Could not allocate memory for compressed data\n"); | |||
135 | exit(1); | |||
136 | } | |||
137 | ||||
138 | *outlen = ZSTD_compress(compressed, *outlen, output->ptr, output->used, | |||
139 | ZSTD_maxCLevel()); | |||
140 | if (ZSTD_isError(*outlen)) { | |||
141 | fprintf(stderrstderr, "Could not compress mime type table with ZSTD\n"); | |||
142 | exit(1); | |||
143 | } | |||
144 | #elif defined(HAVE_ZOPFLI) | |||
145 | ZopfliOptions opts; | |||
146 | ||||
147 | *outlen = 0; | |||
148 | ||||
149 | ZopfliInitOptions(&opts); | |||
150 | ZopfliCompress(&opts, ZOPFLI_FORMAT_ZLIB, | |||
151 | (const unsigned char *)output->ptr, output->used, | |||
152 | (unsigned char **)&compressed, outlen); | |||
153 | #else | |||
154 | *outlen = compressBound((uLong)output->used); | |||
155 | compressed = malloc(*outlen); | |||
156 | if (!compressed) { | |||
157 | fprintf(stderrstderr, "Could not allocate memory for compressed data\n"); | |||
158 | exit(1); | |||
159 | } | |||
160 | if (compress2((Bytef *)compressed, outlen, (const Bytef *)output->ptr, | |||
161 | output->used, 9) != Z_OK) { | |||
162 | fprintf(stderrstderr, "Could not compress data with zlib\n"); | |||
163 | exit(1); | |||
164 | } | |||
165 | #endif | |||
166 | if (!*outlen) { | |||
167 | free(compressed); | |||
168 | return NULL((void*)0); | |||
169 | } | |||
170 | ||||
171 | return compressed; | |||
172 | } | |||
173 | ||||
174 | static bool_Bool is_builtin_mime_type(const char *mime) | |||
175 | { | |||
176 | /* These are the mime types supported by Lwan without having to perform | |||
177 | * a bsearch(). application/octet-stream is the fallback. */ | |||
178 | if (streq(mime, "application/octet-stream")) | |||
179 | return true1; | |||
180 | if (streq(mime, "application/javascript")) | |||
181 | return true1; | |||
182 | if (streq(mime, "image/jpeg")) | |||
183 | return true1; | |||
184 | if (streq(mime, "image/gif")) | |||
185 | return true1; | |||
186 | if (streq(mime, "image/png")) | |||
187 | return true1; | |||
188 | if (streq(mime, "text/html")) | |||
189 | return true1; | |||
190 | if (streq(mime, "text/css")) | |||
191 | return true1; | |||
192 | if (streq(mime, "text/plain")) | |||
193 | return true1; | |||
194 | return false0; | |||
195 | } | |||
196 | ||||
197 | int main(int argc, char *argv[]) | |||
198 | { | |||
199 | FILE *fp; | |||
200 | char buffer[256]; | |||
201 | struct output output = { .capacity = 1024 }; | |||
202 | size_t compressed_size; | |||
203 | char *compressed, *ext; | |||
204 | struct hash *ext_mime; | |||
205 | struct hash_iter iter; | |||
206 | const char **exts, *key; | |||
207 | size_t i; | |||
208 | ||||
209 | if (argc < 2) { | |||
| ||||
210 | fprintf(stderrstderr, "Usage: %s /path/to/mime.types\n", argv[0]); | |||
211 | return 1; | |||
212 | } | |||
213 | ||||
214 | fp = fopen(argv[1], "re"); | |||
215 | if (!fp) { | |||
216 | fprintf(stderrstderr, "Could not open %s: %s\n", argv[1], strerror(errno(*__errno_location ()))); | |||
217 | return 1; | |||
218 | } | |||
219 | ||||
220 | ext_mime = hash_str_new(free, free); | |||
221 | if (!ext_mime) { | |||
222 | fprintf(stderrstderr, "Could not allocate hash table\n"); | |||
223 | fclose(fp); | |||
224 | return 1; | |||
225 | } | |||
226 | ||||
227 | while (fgets(buffer, sizeof(buffer), fp)) { | |||
228 | char *start = buffer, *end, *tab, *mime_type; | |||
229 | ||||
230 | while (*start && isspace(*start)((*__ctype_b_loc ())[(int) ((*start))] & (unsigned short int ) _ISspace)) /* Strip spaces at the start. */ | |||
231 | start++; | |||
232 | if (*start == '#') /* Ignore commented-out lines. */ | |||
233 | continue; | |||
234 | ||||
235 | strend(start, '\n'); /* Strip line endings. */ | |||
236 | strend(start, '#'); /* Strip comments from the middle. */ | |||
237 | tab = strend(start, '\t'); | |||
238 | if (!tab
| |||
239 | continue; | |||
240 | ||||
241 | mime_type = start; | |||
242 | if (is_builtin_mime_type(mime_type)) | |||
243 | continue; | |||
244 | ||||
245 | while (*tab && *tab == '\t') /* Find first extension. */ | |||
246 | tab++; | |||
247 | ||||
248 | for (ext = tab; *ext; ext += end - ext + 1) { | |||
249 | char *k, *v; | |||
250 | int r; | |||
251 | ||||
252 | end = strchr(ext, ' '); /* Stop at next extension. */ | |||
253 | if (!end) | |||
254 | end = strchr(ext, '\0'); /* If not found, find last extension. */ | |||
255 | *end = '\0'; | |||
256 | ||||
257 | if (end - ext > 8) { | |||
258 | /* Truncate extensions over 8 characters. See commit 2050759297. */ | |||
259 | ext[8] = '\0'; | |||
260 | } | |||
261 | ||||
262 | k = strdup(ext); | |||
263 | v = strdup(mime_type); | |||
264 | ||||
265 | if (!k || !v) { | |||
266 | fprintf(stderrstderr, "Could not allocate memory\n"); | |||
| ||||
267 | fclose(fp); | |||
268 | return 1; | |||
269 | } | |||
270 | ||||
271 | r = hash_add_unique(ext_mime, k, v); | |||
272 | if (r < 0) { | |||
273 | free(k); | |||
274 | free(v); | |||
275 | ||||
276 | if (r != -EEXIST17) { | |||
277 | fprintf(stderrstderr, "Could not add extension to hash table\n"); | |||
278 | fclose(fp); | |||
279 | return 1; | |||
280 | } | |||
281 | } | |||
282 | } | |||
283 | } | |||
284 | ||||
285 | /* Get sorted list of extensions. */ | |||
286 | exts = calloc(hash_get_count(ext_mime), sizeof(char *)); | |||
287 | if (!exts) { | |||
288 | fprintf(stderrstderr, "Could not allocate extension array\n"); | |||
289 | fclose(fp); | |||
290 | return 1; | |||
291 | } | |||
292 | hash_iter_init(ext_mime, &iter); | |||
293 | for (i = 0; hash_iter_next(&iter, (const void **)&key, NULL((void*)0)); i++) | |||
294 | exts[i] = key; | |||
295 | qsort(exts, hash_get_count(ext_mime), sizeof(char *), compare_ext); | |||
296 | ||||
297 | /* Generate uncompressed blob. */ | |||
298 | output.ptr = malloc(output.capacity); | |||
299 | if (!output.ptr) { | |||
300 | fprintf(stderrstderr, "Could not allocate temporary memory\n"); | |||
301 | fclose(fp); | |||
302 | return 1; | |||
303 | } | |||
304 | for (i = 0; i < hash_get_count(ext_mime); i++) { | |||
305 | char ext_lower[9] = {0}; | |||
306 | ||||
307 | strncpy(ext_lower, exts[i], 8); | |||
308 | ||||
309 | for (char *p = ext_lower; *p; p++) | |||
310 | *p |= 0x20; | |||
311 | ||||
312 | if (output_append_padded(&output, ext_lower) < 0) { | |||
313 | fprintf(stderrstderr, "Could not append to output\n"); | |||
314 | fclose(fp); | |||
315 | return 1; | |||
316 | } | |||
317 | } | |||
318 | for (i = 0; i < hash_get_count(ext_mime); i++) { | |||
319 | if (output_append(&output, hash_find(ext_mime, exts[i])) < 0) { | |||
320 | fprintf(stderrstderr, "Could not append to output\n"); | |||
321 | fclose(fp); | |||
322 | return 1; | |||
323 | } | |||
324 | } | |||
325 | ||||
326 | /* Compress blob. */ | |||
327 | compressed = compress_output(&output, &compressed_size); | |||
328 | if (!compressed) { | |||
329 | fprintf(stderrstderr, "Could not compress data\n"); | |||
330 | fclose(fp); | |||
331 | return 1; | |||
332 | } | |||
333 | ||||
334 | /* Print output. */ | |||
335 | #if defined(HAVE_BROTLI) | |||
336 | printf("/* Compressed with brotli */\n"); | |||
337 | #elif defined(HAVE_ZSTD) | |||
338 | printf("/* Compressed with zstd */\n"); | |||
339 | #elif defined(HAVE_ZOPFLI) | |||
340 | printf("/* Compressed with zopfli (deflate) */\n"); | |||
341 | #else | |||
342 | printf("/* Compressed with zlib (deflate) */\n"); | |||
343 | #endif | |||
344 | printf("#pragma once\n"); | |||
345 | printf("#define MIME_UNCOMPRESSED_LEN %zu\n", output.used); | |||
346 | printf("#define MIME_COMPRESSED_LEN %lu\n", compressed_size); | |||
347 | printf("#define MIME_ENTRIES %d\n", hash_get_count(ext_mime)); | |||
348 | printf("static const unsigned char mime_entries_compressed[] = {\n"); | |||
349 | for (i = 1; compressed_size; compressed_size--, i++) | |||
350 | printf("0x%02x,%c", compressed[i - 1] & 0xff, " \n"[i % 13 == 0]); | |||
351 | printf("};\n"); | |||
352 | ||||
353 | free(compressed); | |||
354 | free(output.ptr); | |||
355 | free(exts); | |||
356 | hash_free(ext_mime); | |||
357 | fclose(fp); | |||
358 | ||||
359 | return 0; | |||
360 | } |