1 /**
2 * makefsdata: Converts a directory structure for use with the lwIP httpd.
3 *
4 * This file is part of the lwIP TCP/IP stack.
5 *
6 * Author: Jim Pettinato
7 * Simon Goldschmidt
8 *
9 * @todo:
10 * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and
11 * PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments
12 */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #ifdef WIN32
17 #define WIN32_LEAN_AND_MEAN
18 #include "windows.h"
19 #else
20 #include <dir.h>
21 #endif
22 #include <dos.h>
23 #include <string.h>
24 #include <time.h>
25 #include <sys/stat.h>
26
27 /** Makefsdata can generate *all* files deflate-compressed (where file size shrinks).
28 * Since nearly all browsers support this, this is a good way to reduce ROM size.
29 * To compress the files, "miniz.c" must be downloaded seperately.
30 */
31 #ifndef MAKEFS_SUPPORT_DEFLATE
32 #define MAKEFS_SUPPORT_DEFLATE 0
33 #endif
34
35 #define COPY_BUFSIZE (1024*1024) /* 1 MByte */
36
37 #if MAKEFS_SUPPORT_DEFLATE
38 #include "../miniz.c"
39
40 typedef unsigned char uint8;
41 typedef unsigned short uint16;
42 typedef unsigned int uint;
43
44 #define my_max(a,b) (((a) > (b)) ? (a) : (b))
45 #define my_min(a,b) (((a) < (b)) ? (a) : (b))
46
47 /* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
48 COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */
49 #define COMP_OUT_BUF_SIZE COPY_BUFSIZE
50
51 /* OUT_BUF_SIZE is the size of the output buffer used during decompression.
52 OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */
53 #define OUT_BUF_SIZE COPY_BUFSIZE
54 static uint8 s_outbuf[OUT_BUF_SIZE];
55 static uint8 s_checkbuf[OUT_BUF_SIZE];
56
57 /* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
58 This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */
59 tdefl_compressor g_deflator;
60 tinfl_decompressor g_inflator;
61
62 int deflate_level = 10; /* default compression level, can be changed via command line */
63 #define USAGE_ARG_DEFLATE " [-defl<:compr_level>]"
64 #else /* MAKEFS_SUPPORT_DEFLATE */
65 #define USAGE_ARG_DEFLATE ""
66 #endif /* MAKEFS_SUPPORT_DEFLATE */
67
68 /* Compatibility defines Win32 vs. DOS */
69 #ifdef WIN32
70
71 #define FIND_T WIN32_FIND_DATAA
72 #define FIND_T_FILENAME(fInfo) (fInfo.cFileName)
73 #define FIND_T_IS_DIR(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
74 #define FIND_T_IS_FILE(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
75 #define FIND_RET_T HANDLE
76 #define FINDFIRST_FILE(path, result) FindFirstFileA(path, result)
77 #define FINDFIRST_DIR(path, result) FindFirstFileA(path, result)
78 #define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result)
79 #define FINDFIRST_SUCCEEDED(ret) (ret != INVALID_HANDLE_VALUE)
80 #define FINDNEXT_SUCCEEDED(ret) (ret == TRUE)
81
82 #define GETCWD(path, len) GetCurrentDirectoryA(len, path)
83 #define CHDIR(path) SetCurrentDirectoryA(path)
84 #define CHDIR_SUCCEEDED(ret) (ret == TRUE)
85
86 #else
87
88 #define FIND_T struct ffblk
89 #define FIND_T_FILENAME(fInfo) (fInfo.ff_name)
90 #define FIND_T_IS_DIR(fInfo) ((fInfo.ff_attrib & FA_DIREC) == FA_DIREC)
91 #define FIND_T_IS_FILE(fInfo) (1)
92 #define FIND_RET_T int
93 #define FINDFIRST_FILE(path, result) findfirst(path, result, FA_ARCH)
94 #define FINDFIRST_DIR(path, result) findfirst(path, result, FA_DIREC)
95 #define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result)
96 #define FINDFIRST_SUCCEEDED(ret) (ret == 0)
97 #define FINDNEXT_SUCCEEDED(ret) (ret == 0)
98
99 #define GETCWD(path, len) getcwd(path, len)
100 #define CHDIR(path) chdir(path)
101 #define CHDIR_SUCCEEDED(ret) (ret == 0)
102
103 #endif
104
105 #define NEWLINE "\r\n"
106 #define NEWLINE_LEN 2
107
108 /* define this to get the header variables we use to build HTTP headers */
109 #define LWIP_HTTPD_DYNAMIC_HEADERS 1
110 #define LWIP_HTTPD_SSI 1
111 #include "lwip/init.h"
112 #include "../httpd_structs.h"
113 #include "lwip/apps/fs.h"
114
115 #include "../core/inet_chksum.c"
116 #include "../core/def.c"
117
118 /** (Your server name here) */
119 const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
120 char serverIDBuffer[1024];
121
122 /* change this to suit your MEM_ALIGNMENT */
123 #define PAYLOAD_ALIGNMENT 4
124 /* set this to 0 to prevent aligning payload */
125 #define ALIGN_PAYLOAD 1
126 /* define this to a type that has the required alignment */
127 #define PAYLOAD_ALIGN_TYPE "unsigned int"
128 static int payload_alingment_dummy_counter = 0;
129
130 #define HEX_BYTES_PER_LINE 16
131
132 #define MAX_PATH_LEN 256
133
134 struct file_entry
135 {
136 struct file_entry* next;
137 const char* filename_c;
138 };
139
140 int process_sub(FILE *data_file, FILE *struct_file);
141 int process_file(FILE *data_file, FILE *struct_file, const char *filename);
142 int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
143 u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed);
144 int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
145 int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
146 void concat_files(const char *file1, const char *file2, const char *targetfile);
147 int check_path(char* path, size_t size);
148
149 /* 5 bytes per char + 3 bytes per line */
150 static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
151
152 char curSubdir[MAX_PATH_LEN];
153 char lastFileVar[MAX_PATH_LEN];
154 char hdr_buf[4096];
155
156 unsigned char processSubs = 1;
157 unsigned char includeHttpHeader = 1;
158 unsigned char useHttp11 = 0;
159 unsigned char supportSsi = 1;
160 unsigned char precalcChksum = 0;
161 unsigned char includeLastModified = 0;
162 #if MAKEFS_SUPPORT_DEFLATE
163 unsigned char deflateNonSsiFiles = 0;
164 size_t deflatedBytesReduced = 0;
165 size_t overallDataBytes = 0;
166 #endif
167
168 struct file_entry* first_file = NULL;
169 struct file_entry* last_file = NULL;
170
print_usage(void)171 static void print_usage(void)
172 {
173 printf(" Usage: htmlgen [targetdir] [-s] [-e] [-i] [-11] [-nossi] [-c] [-f:<filename>] [-m] [-svr:<name>]" USAGE_ARG_DEFLATE NEWLINE NEWLINE);
174 printf(" targetdir: relative or absolute path to files to convert" NEWLINE);
175 printf(" switch -s: toggle processing of subdirectories (default is on)" NEWLINE);
176 printf(" switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE);
177 printf(" switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE);
178 printf(" switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE);
179 printf(" switch -c: precalculate checksums for all pages (default is off)" NEWLINE);
180 printf(" switch -f: target filename (default is \"fsdata.c\")" NEWLINE);
181 printf(" switch -m: include \"Last-Modified\" header based on file time" NEWLINE);
182 printf(" switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE);
183 #if MAKEFS_SUPPORT_DEFLATE
184 printf(" switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE);
185 printf(" ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE);
186 #endif
187 printf(" if targetdir not specified, htmlgen will attempt to" NEWLINE);
188 printf(" process files in subdirectory 'fs'" NEWLINE);
189 }
190
main(int argc,char * argv[])191 int main(int argc, char *argv[])
192 {
193 char path[MAX_PATH_LEN];
194 char appPath[MAX_PATH_LEN];
195 FILE *data_file;
196 FILE *struct_file;
197 int filesProcessed;
198 int i;
199 char targetfile[MAX_PATH_LEN];
200 strcpy(targetfile, "fsdata.c");
201
202 memset(path, 0, sizeof(path));
203 memset(appPath, 0, sizeof(appPath));
204
205 printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE);
206 printf(" by Jim Pettinato - circa 2003 " NEWLINE);
207 printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE);
208
209 strcpy(path, "fs");
210 for (i = 1; i < argc; i++) {
211 if (argv[i] == NULL) {
212 continue;
213 }
214 if (argv[i][0] == '-') {
215 if (strstr(argv[i], "-svr:") == argv[i]) {
216 snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]);
217 serverID = serverIDBuffer;
218 printf("Using Server-ID: \"%s\"\n", serverID);
219 } else if (strstr(argv[i], "-s") == argv[i]) {
220 processSubs = 0;
221 } else if (strstr(argv[i], "-e") == argv[i]) {
222 includeHttpHeader = 0;
223 } else if (strstr(argv[i], "-11") == argv[i]) {
224 useHttp11 = 1;
225 } else if (strstr(argv[i], "-nossi") == argv[i]) {
226 supportSsi = 0;
227 } else if (strstr(argv[i], "-c") == argv[i]) {
228 precalcChksum = 1;
229 } else if (strstr(argv[i], "-f:") == argv[i]) {
230 strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1);
231 targetfile[sizeof(targetfile) - 1] = 0;
232 printf("Writing to file \"%s\"\n", targetfile);
233 } else if (strstr(argv[i], "-m") == argv[i]) {
234 includeLastModified = 1;
235 } else if (strstr(argv[i], "-defl") == argv[i]) {
236 #if MAKEFS_SUPPORT_DEFLATE
237 char* colon = strstr(argv[i], ":");
238 if (colon) {
239 if (colon[1] != 0) {
240 int defl_level = atoi(&colon[1]);
241 if ((defl_level >= 0) && (defl_level <= 10)) {
242 deflate_level = defl_level;
243 } else {
244 printf("ERROR: deflate level must be [0..10]" NEWLINE);
245 exit(0);
246 }
247 }
248 }
249 deflateNonSsiFiles = 1;
250 printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level);
251 #else
252 printf("WARNING: Deflate support is disabled\n");
253 #endif
254 } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) {
255 print_usage();
256 exit(0);
257 }
258 } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) {
259 print_usage();
260 exit(0);
261 } else {
262 strncpy(path, argv[i], sizeof(path)-1);
263 path[sizeof(path)-1] = 0;
264 }
265 }
266
267 if (!check_path(path, sizeof(path))) {
268 printf("Invalid path: \"%s\"." NEWLINE, path);
269 exit(-1);
270 }
271
272 GETCWD(appPath, MAX_PATH_LEN);
273 /* if command line param or subdir named 'fs' not found spout usage verbiage */
274 if (!CHDIR_SUCCEEDED(CHDIR(path))) {
275 /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */
276 printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path);
277 print_usage();
278 exit(-1);
279 }
280 CHDIR(appPath);
281
282 printf("HTTP %sheader will %s statically included." NEWLINE,
283 (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""),
284 (includeHttpHeader ? "be" : "not be"));
285
286 sprintf(curSubdir, ""); /* start off in web page's root directory - relative paths */
287 printf(" Processing all files in directory %s", path);
288 if (processSubs) {
289 printf(" and subdirectories..." NEWLINE NEWLINE);
290 } else {
291 printf("..." NEWLINE NEWLINE);
292 }
293
294 data_file = fopen("fsdata.tmp", "wb");
295 if (data_file == NULL) {
296 printf("Failed to create file \"fsdata.tmp\"\n");
297 exit(-1);
298 }
299 struct_file = fopen("fshdr.tmp", "wb");
300 if (struct_file == NULL) {
301 printf("Failed to create file \"fshdr.tmp\"\n");
302 fclose(data_file);
303 exit(-1);
304 }
305
306 CHDIR(path);
307
308 fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
309 fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE);
310 fprintf(data_file, "#include \"fsdata.h\"" NEWLINE NEWLINE NEWLINE);
311
312 fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
313 /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
314 fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE);
315 /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */
316 fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE);
317
318 /* define alignment defines */
319 #if ALIGN_PAYLOAD
320 fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE);
321 #endif
322 fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE" NEWLINE "#define FSDATA_ALIGN_PRE" NEWLINE "#endif" NEWLINE);
323 fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE);
324 #if ALIGN_PAYLOAD
325 fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE);
326 #endif
327
328 sprintf(lastFileVar, "NULL");
329
330 filesProcessed = process_sub(data_file, struct_file);
331
332 /* data_file now contains all of the raw data.. now append linked list of
333 * file header structs to allow embedded app to search for a file name */
334 fprintf(data_file, NEWLINE NEWLINE);
335 fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar);
336 fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed);
337
338 fclose(data_file);
339 fclose(struct_file);
340
341 CHDIR(appPath);
342 /* append struct_file to data_file */
343 printf(NEWLINE "Creating target file..." NEWLINE NEWLINE);
344 concat_files("fsdata.tmp", "fshdr.tmp", targetfile);
345
346 /* if succeeded, delete the temporary files */
347 if (remove("fsdata.tmp") != 0) {
348 printf("Warning: failed to delete fsdata.tmp\n");
349 }
350 if (remove("fshdr.tmp") != 0) {
351 printf("Warning: failed to delete fshdr.tmp\n");
352 }
353
354 printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed);
355 #if MAKEFS_SUPPORT_DEFLATE
356 if (deflateNonSsiFiles) {
357 printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE,
358 (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced*100.0)/overallDataBytes));
359 }
360 #endif
361 printf(NEWLINE);
362
363 while (first_file != NULL) {
364 struct file_entry* fe = first_file;
365 first_file = fe->next;
366 free(fe);
367 }
368
369 return 0;
370 }
371
check_path(char * path,size_t size)372 int check_path(char* path, size_t size)
373 {
374 size_t slen;
375 if (path[0] == 0) {
376 /* empty */
377 return 0;
378 }
379 slen = strlen(path);
380 if (slen >= size) {
381 /* not NULL-terminated */
382 return 0;
383 }
384 while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) {
385 /* path should not end with trailing backslash */
386 path[slen] = 0;
387 slen--;
388 }
389 if (slen == 0) {
390 return 0;
391 }
392 return 1;
393 }
394
copy_file(const char * filename_in,FILE * fout)395 static void copy_file(const char *filename_in, FILE *fout)
396 {
397 FILE *fin;
398 size_t len;
399 void* buf;
400 fin = fopen(filename_in, "rb");
401 if (fin == NULL) {
402 printf("Failed to open file \"%s\"\n", filename_in);
403 exit(-1);
404 }
405 buf = malloc(COPY_BUFSIZE);
406 while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) {
407 fwrite(buf, 1, len, fout);
408 }
409 free(buf);
410 fclose(fin);
411 }
412
concat_files(const char * file1,const char * file2,const char * targetfile)413 void concat_files(const char *file1, const char *file2, const char *targetfile)
414 {
415 FILE *fout;
416 fout = fopen(targetfile, "wb");
417 if (fout == NULL) {
418 printf("Failed to open file \"%s\"\n", targetfile);
419 exit(-1);
420 }
421 copy_file(file1, fout);
422 copy_file(file2, fout);
423 fclose(fout);
424 }
425
process_sub(FILE * data_file,FILE * struct_file)426 int process_sub(FILE *data_file, FILE *struct_file)
427 {
428 FIND_T fInfo;
429 FIND_RET_T fret;
430 int filesProcessed = 0;
431
432 if (processSubs) {
433 /* process subs recursively */
434 size_t sublen = strlen(curSubdir);
435 size_t freelen = sizeof(curSubdir) - sublen - 1;
436 LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
437 fret = FINDFIRST_DIR("*", &fInfo);
438 if (FINDFIRST_SUCCEEDED(fret)) {
439 do {
440 const char *curName = FIND_T_FILENAME(fInfo);
441 if ((curName[0] == '.') || (strcmp(curName, "CVS") == 0)) {
442 continue;
443 }
444 if (!FIND_T_IS_DIR(fInfo)) {
445 continue;
446 }
447 if (freelen > 0) {
448 CHDIR(curName);
449 strncat(curSubdir, "/", freelen);
450 strncat(curSubdir, curName, freelen - 1);
451 curSubdir[sizeof(curSubdir) - 1] = 0;
452 printf("processing subdirectory %s/..." NEWLINE, curSubdir);
453 filesProcessed += process_sub(data_file, struct_file);
454 CHDIR("..");
455 curSubdir[sublen] = 0;
456 } else {
457 printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, curName);
458 }
459 } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo)));
460 }
461 }
462
463 fret = FINDFIRST_FILE("*.*", &fInfo);
464 if (FINDFIRST_SUCCEEDED(fret)) {
465 /* at least one file in directory */
466 do {
467 if (FIND_T_IS_FILE(fInfo)) {
468 const char *curName = FIND_T_FILENAME(fInfo);
469 printf("processing %s/%s..." NEWLINE, curSubdir, curName);
470 if (process_file(data_file, struct_file, curName) < 0) {
471 printf(NEWLINE "Error... aborting" NEWLINE);
472 return -1;
473 }
474 filesProcessed++;
475 }
476 } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo)));
477 }
478 return filesProcessed;
479 }
480
get_file_data(const char * filename,int * file_size,int can_be_compressed,int * is_compressed)481 u8_t* get_file_data(const char* filename, int* file_size, int can_be_compressed, int* is_compressed)
482 {
483 FILE *inFile;
484 size_t fsize = 0;
485 u8_t* buf;
486 size_t r;
487 int rs;
488 inFile = fopen(filename, "rb");
489 if (inFile == NULL) {
490 printf("Failed to open file \"%s\"\n", filename);
491 exit(-1);
492 }
493 fseek(inFile, 0, SEEK_END);
494 rs = ftell(inFile);
495 if (rs < 0) {
496 printf("ftell failed with %d\n", errno);
497 exit(-1);
498 }
499 fsize = (size_t)rs;
500 fseek(inFile, 0, SEEK_SET);
501 buf = (u8_t*)malloc(fsize);
502 LWIP_ASSERT("buf != NULL", buf != NULL);
503 r = fread(buf, 1, fsize, inFile);
504 *file_size = fsize;
505 *is_compressed = 0;
506 #if MAKEFS_SUPPORT_DEFLATE
507 overallDataBytes += fsize;
508 if (deflateNonSsiFiles) {
509 if (can_be_compressed) {
510 if (fsize < OUT_BUF_SIZE) {
511 u8_t* ret_buf;
512 tdefl_status status;
513 size_t in_bytes = fsize;
514 size_t out_bytes = OUT_BUF_SIZE;
515 const void *next_in = buf;
516 void *next_out = s_outbuf;
517 /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */
518 mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
519 if (!deflate_level) {
520 comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
521 }
522 status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
523 if (status != TDEFL_STATUS_OKAY) {
524 printf("tdefl_init() failed!\n");
525 exit(-1);
526 }
527 memset(s_outbuf, 0, sizeof(s_outbuf));
528 status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH);
529 if (status != TDEFL_STATUS_DONE) {
530 printf("deflate failed: %d\n", status);
531 exit(-1);
532 }
533 LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE);
534 if (out_bytes < fsize) {
535 ret_buf = (u8_t*)malloc(out_bytes);
536 LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL);
537 memcpy(ret_buf, s_outbuf, out_bytes);
538 {
539 /* sanity-check compression be inflating and comparing to the original */
540 tinfl_status dec_status;
541 tinfl_decompressor inflator;
542 size_t dec_in_bytes = out_bytes;
543 size_t dec_out_bytes = OUT_BUF_SIZE;
544 next_out = s_checkbuf;
545
546 tinfl_init(&inflator);
547 memset(s_checkbuf, 0, sizeof(s_checkbuf));
548 dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0);
549 LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE);
550 LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes);
551 LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize));
552 }
553 /* free original buffer, use compressed data + size */
554 free(buf);
555 buf = ret_buf;
556 *file_size = out_bytes;
557 printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes*100.0)/fsize));
558 deflatedBytesReduced += (size_t)(fsize - out_bytes);
559 *is_compressed = 1;
560 } else {
561 printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize));
562 }
563 } else {
564 printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE);
565 }
566 } else {
567 printf(" - SSI file, cannot be compressed" NEWLINE);
568 }
569 }
570 #else
571 LWIP_UNUSED_ARG(can_be_compressed);
572 #endif
573 fclose(inFile);
574 return buf;
575 }
576
process_file_data(FILE * data_file,u8_t * file_data,size_t file_size)577 void process_file_data(FILE* data_file, u8_t* file_data, size_t file_size)
578 {
579 size_t written, i, src_off=0;
580
581 size_t off = 0;
582 for (i = 0; i < file_size; i++) {
583 LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5);
584 sprintf(&file_buffer_c[off], "0x%02.2x,", file_data[i]);
585 off += 5;
586 if ((++src_off % HEX_BYTES_PER_LINE) == 0) {
587 LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN);
588 memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN);
589 off += NEWLINE_LEN;
590 }
591 if (off + 20 >= sizeof(file_buffer_c)) {
592 written = fwrite(file_buffer_c, 1, off, data_file);
593 LWIP_ASSERT("written == off", written == off);
594 off = 0;
595 }
596 }
597 written = fwrite(file_buffer_c, 1, off, data_file);
598 LWIP_ASSERT("written == off", written == off);
599 }
600
write_checksums(FILE * struct_file,const char * varname,u16_t hdr_len,u16_t hdr_chksum,const u8_t * file_data,size_t file_size)601 int write_checksums(FILE *struct_file, const char *varname,
602 u16_t hdr_len, u16_t hdr_chksum, const u8_t* file_data, size_t file_size)
603 {
604 int chunk_size = TCP_MSS;
605 int offset, src_offset;
606 size_t len;
607 int i = 0;
608 #if LWIP_TCP_TIMESTAMPS
609 /* when timestamps are used, usable space is 12 bytes less per segment */
610 chunk_size -= 12;
611 #endif
612
613 fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
614 fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname);
615
616 if (hdr_len > 0) {
617 /* add checksum for HTTP header */
618 fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len);
619 i++;
620 }
621 src_offset = 0;
622 for (offset = hdr_len; ; offset += len) {
623 unsigned short chksum;
624 void* data = (void*)&file_data[src_offset];
625 len = LWIP_MIN(chunk_size, (int)file_size - src_offset);
626 if (len == 0) {
627 break;
628 }
629 chksum = ~inet_chksum(data, (u16_t)len);
630 /* add checksum for data */
631 fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, offset, chksum, len);
632 i++;
633 }
634 fprintf(struct_file, "};" NEWLINE);
635 fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
636 return i;
637 }
638
is_valid_char_for_c_var(char x)639 static int is_valid_char_for_c_var(char x)
640 {
641 if (((x >= 'A') && (x <= 'Z')) ||
642 ((x >= 'a') && (x <= 'z')) ||
643 ((x >= '0') && (x <= '9')) ||
644 (x == '_')) {
645 return 1;
646 }
647 return 0;
648 }
649
fix_filename_for_c(char * qualifiedName,size_t max_len)650 static void fix_filename_for_c(char* qualifiedName, size_t max_len)
651 {
652 struct file_entry* f;
653 size_t len = strlen(qualifiedName);
654 char *new_name = (char*)malloc(len + 2);
655 int filename_ok;
656 int cnt = 0;
657 size_t i;
658 if (len + 3 == max_len) {
659 printf("File name too long: \"%s\"\n", qualifiedName);
660 exit(-1);
661 }
662 strcpy(new_name, qualifiedName);
663 for (i = 0; i < len; i++) {
664 if (!is_valid_char_for_c_var(new_name[i])) {
665 new_name[i] = '_';
666 }
667 }
668 do {
669 filename_ok = 1;
670 for (f = first_file; f != NULL; f = f->next) {
671 if (!strcmp(f->filename_c, new_name)) {
672 filename_ok = 0;
673 cnt++;
674 /* try next unique file name */
675 sprintf(&new_name[len], "%d", cnt);
676 break;
677 }
678 }
679 } while (!filename_ok && (cnt < 999));
680 if (!filename_ok) {
681 printf("Failed to get unique file name: \"%s\"\n", qualifiedName);
682 exit(-1);
683 }
684 strcpy(qualifiedName, new_name);
685 free(new_name);
686 }
687
register_filename(const char * qualifiedName)688 static void register_filename(const char* qualifiedName)
689 {
690 struct file_entry* fe = (struct file_entry*)malloc(sizeof(struct file_entry));
691 fe->filename_c = strdup(qualifiedName);
692 fe->next = NULL;
693 if (first_file == NULL) {
694 first_file = last_file = fe;
695 } else {
696 last_file->next = fe;
697 last_file = fe;
698 }
699 }
700
is_ssi_file(const char * filename)701 int is_ssi_file(const char* filename)
702 {
703 size_t loop;
704 for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
705 if (strstr(filename, g_pcSSIExtensions[loop])) {
706 return 1;
707 }
708 }
709 return 0;
710 }
711
process_file(FILE * data_file,FILE * struct_file,const char * filename)712 int process_file(FILE *data_file, FILE *struct_file, const char *filename)
713 {
714 char varname[MAX_PATH_LEN];
715 int i = 0;
716 char qualifiedName[MAX_PATH_LEN];
717 int file_size;
718 u16_t http_hdr_chksum = 0;
719 u16_t http_hdr_len = 0;
720 int chksum_count = 0;
721 u8_t flags = 0;
722 const char* flags_str;
723 u8_t has_content_len;
724 u8_t* file_data;
725 int is_compressed = 0;
726
727 /* create qualified name (@todo: prepend slash or not?) */
728 sprintf(qualifiedName,"%s/%s", curSubdir, filename);
729 /* create C variable name */
730 strcpy(varname, qualifiedName);
731 /* convert slashes & dots to underscores */
732 fix_filename_for_c(varname, MAX_PATH_LEN);
733 register_filename(varname);
734 #if ALIGN_PAYLOAD
735 /* to force even alignment of array, type 1 */
736 fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE);
737 fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++);
738 fprintf(data_file, "#endif" NEWLINE);
739 #endif /* ALIGN_PAYLOAD */
740 fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname);
741 /* encode source file name (used by file system, not returned to browser) */
742 fprintf(data_file, "/* %s (%d chars) */" NEWLINE, qualifiedName, strlen(qualifiedName)+1);
743 file_put_ascii(data_file, qualifiedName, strlen(qualifiedName)+1, &i);
744 #if ALIGN_PAYLOAD
745 /* pad to even number of bytes to assure payload is on aligned boundary */
746 while(i % PAYLOAD_ALIGNMENT != 0) {
747 fprintf(data_file, "0x%02.2x,", 0);
748 i++;
749 }
750 #endif /* ALIGN_PAYLOAD */
751 fprintf(data_file, NEWLINE);
752
753 has_content_len = !is_ssi_file(filename);
754 file_data = get_file_data(filename, &file_size, includeHttpHeader && has_content_len, &is_compressed);
755 if (includeHttpHeader) {
756 file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed);
757 flags = FS_FILE_FLAGS_HEADER_INCLUDED;
758 if (has_content_len) {
759 flags |= FS_FILE_FLAGS_HEADER_PERSISTENT;
760 }
761 }
762 if (precalcChksum) {
763 chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size);
764 }
765
766 /* build declaration of struct fsdata_file in temp file */
767 fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname);
768 fprintf(struct_file, "file_%s," NEWLINE, lastFileVar);
769 fprintf(struct_file, "data_%s," NEWLINE, varname);
770 fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i);
771 fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i);
772 switch(flags)
773 {
774 case(FS_FILE_FLAGS_HEADER_INCLUDED):
775 flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED";
776 break;
777 case(FS_FILE_FLAGS_HEADER_PERSISTENT):
778 flags_str = "FS_FILE_FLAGS_HEADER_PERSISTENT";
779 break;
780 case(FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT):
781 flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT";
782 break;
783 default:
784 flags_str = "0";
785 break;
786 }
787 fprintf(struct_file, "%s," NEWLINE, flags_str);
788 if (precalcChksum) {
789 fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
790 fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname);
791 fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
792 }
793 fprintf(struct_file, "}};" NEWLINE NEWLINE);
794 strcpy(lastFileVar, varname);
795
796 /* write actual file contents */
797 i = 0;
798 fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size);
799 process_file_data(data_file, file_data, file_size);
800 fprintf(data_file, "};" NEWLINE NEWLINE);
801 free(file_data);
802 return 0;
803 }
804
file_write_http_header(FILE * data_file,const char * filename,int file_size,u16_t * http_hdr_len,u16_t * http_hdr_chksum,u8_t provide_content_len,int is_compressed)805 int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
806 u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed)
807 {
808 int i = 0;
809 int response_type = HTTP_HDR_OK;
810 const char* file_type;
811 const char *cur_string;
812 size_t cur_len;
813 int written = 0;
814 size_t hdr_len = 0;
815 u16_t acc;
816 const char *file_ext;
817 int j;
818 u8_t provide_last_modified = includeLastModified;
819
820 memset(hdr_buf, 0, sizeof(hdr_buf));
821
822 if (useHttp11) {
823 response_type = HTTP_HDR_OK_11;
824 }
825
826 fprintf(data_file, NEWLINE "/* HTTP header */");
827 if (strstr(filename, "404") == filename) {
828 response_type = HTTP_HDR_NOT_FOUND;
829 if (useHttp11) {
830 response_type = HTTP_HDR_NOT_FOUND_11;
831 }
832 } else if (strstr(filename, "400") == filename) {
833 response_type = HTTP_HDR_BAD_REQUEST;
834 if (useHttp11) {
835 response_type = HTTP_HDR_BAD_REQUEST_11;
836 }
837 } else if (strstr(filename, "501") == filename) {
838 response_type = HTTP_HDR_NOT_IMPL;
839 if (useHttp11) {
840 response_type = HTTP_HDR_NOT_IMPL_11;
841 }
842 }
843 cur_string = g_psHTTPHeaderStrings[response_type];
844 cur_len = strlen(cur_string);
845 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
846 written += file_put_ascii(data_file, cur_string, cur_len, &i);
847 i = 0;
848 if (precalcChksum) {
849 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
850 hdr_len += cur_len;
851 }
852
853 cur_string = serverID;
854 cur_len = strlen(cur_string);
855 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
856 written += file_put_ascii(data_file, cur_string, cur_len, &i);
857 i = 0;
858 if (precalcChksum) {
859 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
860 hdr_len += cur_len;
861 }
862
863 file_ext = filename;
864 if (file_ext != NULL) {
865 while(strstr(file_ext, ".") != NULL) {
866 file_ext = strstr(file_ext, ".");
867 file_ext++;
868 }
869 }
870 if ((file_ext == NULL) || (*file_ext == 0)) {
871 printf("failed to get extension for file \"%s\", using default.\n", filename);
872 file_type = HTTP_HDR_DEFAULT_TYPE;
873 } else {
874 file_type = NULL;
875 for (j = 0; j < NUM_HTTP_HEADERS; j++) {
876 if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) {
877 file_type = g_psHTTPHeaders[j].content_type;
878 break;
879 }
880 }
881 if (file_type == NULL) {
882 printf("failed to get file type for extension \"%s\", using default.\n", file_ext);
883 file_type = HTTP_HDR_DEFAULT_TYPE;
884 }
885 }
886
887 /* Content-Length is used for persistent connections in HTTP/1.1 but also for
888 download progress in older versions
889 @todo: just use a big-enough buffer and let the HTTPD send spaces? */
890 if (provide_content_len) {
891 char intbuf[MAX_PATH_LEN];
892 int content_len = file_size;
893 memset(intbuf, 0, sizeof(intbuf));
894 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
895 cur_len = strlen(cur_string);
896 fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%d+ bytes) */" NEWLINE, cur_string, content_len, cur_len+2);
897 written += file_put_ascii(data_file, cur_string, cur_len, &i);
898 if (precalcChksum) {
899 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
900 hdr_len += cur_len;
901 }
902
903 _itoa(content_len, intbuf, 10);
904 strcat(intbuf, "\r\n");
905 cur_len = strlen(intbuf);
906 written += file_put_ascii(data_file, intbuf, cur_len, &i);
907 i = 0;
908 if (precalcChksum) {
909 memcpy(&hdr_buf[hdr_len], intbuf, cur_len);
910 hdr_len += cur_len;
911 }
912 }
913 if (provide_last_modified) {
914 char modbuf[256];
915 struct stat stat_data;
916 struct tm* t;
917 memset(modbuf, 0, sizeof(modbuf));
918 memset(&stat_data, 0, sizeof(stat_data));
919 cur_string = modbuf;
920 strcpy(modbuf, "Last-Modified: ");
921 if (stat(filename, &stat_data) != 0) {
922 printf("stat(%s) failed with error %d\n", filename, errno);
923 exit(-1);
924 }
925 t = gmtime(&stat_data.st_mtime);
926 if (t == NULL) {
927 printf("gmtime() failed with error %d\n", errno);
928 exit(-1);
929 }
930 strftime(&modbuf[15], sizeof(modbuf)-15, "%a, %d %b %Y %H:%M:%S GMT", t);
931 cur_len = strlen(cur_string);
932 fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%d+ bytes) */" NEWLINE, cur_string, cur_len+2);
933 written += file_put_ascii(data_file, cur_string, cur_len, &i);
934 if (precalcChksum) {
935 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
936 hdr_len += cur_len;
937 }
938
939 modbuf[0] = 0;
940 strcat(modbuf, "\r\n");
941 cur_len = strlen(modbuf);
942 written += file_put_ascii(data_file, modbuf, cur_len, &i);
943 i = 0;
944 if (precalcChksum) {
945 memcpy(&hdr_buf[hdr_len], modbuf, cur_len);
946 hdr_len += cur_len;
947 }
948 }
949
950 /* HTTP/1.1 implements persistent connections */
951 if (useHttp11) {
952 if (provide_content_len) {
953 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE];
954 } else {
955 /* no Content-Length available, so a persistent connection is no possible
956 because the client does not know the data length */
957 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
958 }
959 cur_len = strlen(cur_string);
960 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
961 written += file_put_ascii(data_file, cur_string, cur_len, &i);
962 i = 0;
963 if (precalcChksum) {
964 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
965 hdr_len += cur_len;
966 }
967 }
968
969 #if MAKEFS_SUPPORT_DEFLATE
970 if (is_compressed) {
971 /* tell the client about the deflate encoding */
972 LWIP_ASSERT("error", deflateNonSsiFiles);
973 cur_string = "Content-Encoding: deflate\r\n";
974 cur_len = strlen(cur_string);
975 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
976 written += file_put_ascii(data_file, cur_string, cur_len, &i);
977 i = 0;
978 }
979 #else
980 LWIP_UNUSED_ARG(is_compressed);
981 #endif
982
983 /* write content-type, ATTENTION: this includes the double-CRLF! */
984 cur_string = file_type;
985 cur_len = strlen(cur_string);
986 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
987 written += file_put_ascii(data_file, cur_string, cur_len, &i);
988 i = 0;
989
990 /* ATTENTION: headers are done now (double-CRLF has been written!) */
991
992 if (precalcChksum) {
993 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
994 hdr_len += cur_len;
995
996 LWIP_ASSERT("hdr_len <= 0xffff", hdr_len <= 0xffff);
997 LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
998 acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
999 *http_hdr_len = (u16_t)hdr_len;
1000 *http_hdr_chksum = acc;
1001 }
1002
1003 return written;
1004 }
1005
file_put_ascii(FILE * file,const char * ascii_string,int len,int * i)1006 int file_put_ascii(FILE *file, const char* ascii_string, int len, int *i)
1007 {
1008 int x;
1009 for (x = 0; x < len; x++) {
1010 unsigned char cur = ascii_string[x];
1011 fprintf(file, "0x%02.2x,", cur);
1012 if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
1013 fprintf(file, NEWLINE);
1014 }
1015 }
1016 return len;
1017 }
1018
s_put_ascii(char * buf,const char * ascii_string,int len,int * i)1019 int s_put_ascii(char *buf, const char *ascii_string, int len, int *i)
1020 {
1021 int x;
1022 int idx = 0;
1023 for (x = 0; x < len; x++) {
1024 unsigned char cur = ascii_string[x];
1025 sprintf(&buf[idx], "0x%02.2x,", cur);
1026 idx += 5;
1027 if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
1028 sprintf(&buf[idx], NEWLINE);
1029 idx += NEWLINE_LEN;
1030 }
1031 }
1032 return len;
1033 }
1034