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 #include <string.h>
17 #include <time.h>
18 #include <sys/stat.h>
19
20 #include "tinydir.h"
21
22 /** Makefsdata can generate *all* files deflate-compressed (where file size shrinks).
23 * Since nearly all browsers support this, this is a good way to reduce ROM size.
24 * To compress the files, "miniz.c" must be downloaded seperately.
25 */
26 #ifndef MAKEFS_SUPPORT_DEFLATE
27 #define MAKEFS_SUPPORT_DEFLATE 0
28 #endif
29
30 #define COPY_BUFSIZE (1024*1024) /* 1 MByte */
31
32 #if MAKEFS_SUPPORT_DEFLATE
33 #include "../miniz.c"
34
35 typedef unsigned char uint8;
36 typedef unsigned short uint16;
37 typedef unsigned int uint;
38
39 #define my_max(a,b) (((a) > (b)) ? (a) : (b))
40 #define my_min(a,b) (((a) < (b)) ? (a) : (b))
41
42 /* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
43 COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */
44 #define COMP_OUT_BUF_SIZE COPY_BUFSIZE
45
46 /* OUT_BUF_SIZE is the size of the output buffer used during decompression.
47 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) */
48 #define OUT_BUF_SIZE COPY_BUFSIZE
49 static uint8 s_outbuf[OUT_BUF_SIZE];
50 static uint8 s_checkbuf[OUT_BUF_SIZE];
51
52 /* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
53 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. */
54 tdefl_compressor g_deflator;
55 tinfl_decompressor g_inflator;
56
57 int deflate_level = 10; /* default compression level, can be changed via command line */
58 #define USAGE_ARG_DEFLATE " [-defl<:compr_level>]"
59 #else /* MAKEFS_SUPPORT_DEFLATE */
60 #define USAGE_ARG_DEFLATE ""
61 #endif /* MAKEFS_SUPPORT_DEFLATE */
62
63 #ifdef WIN32
64
65 #define GETCWD(path, len) GetCurrentDirectoryA(len, path)
66 #define GETCWD_SUCCEEDED(ret) (ret != 0)
67 #define CHDIR(path) SetCurrentDirectoryA(path)
68 #define CHDIR_SUCCEEDED(ret) (ret == TRUE)
69
70 #elif __linux__
71
72 #define GETCWD(path, len) getcwd(path, len)
73 #define GETCWD_SUCCEEDED(ret) (ret != NULL)
74 #define CHDIR(path) chdir(path)
75 #define CHDIR_SUCCEEDED(ret) (ret == 0)
76
77 #else
78
79 #error makefsdata not supported on this platform
80
81 #endif
82
83 #define NEWLINE "\r\n"
84 #define NEWLINE_LEN 2
85
86 /* Define this here since we don't include any external C files and ports might override it */
87 #define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
88 x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)
89
90 /* define this to get the header variables we use to build HTTP headers */
91 #define LWIP_HTTPD_DYNAMIC_HEADERS 1
92 #define LWIP_HTTPD_SSI 1
93 #include "lwip/init.h"
94 #include "../httpd_structs.h"
95 #include "lwip/apps/fs.h"
96
97 #include "../core/inet_chksum.c"
98 #include "../core/def.c"
99
100 /** (Your server name here) */
101 const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
102 char serverIDBuffer[1024];
103
104 /* change this to suit your MEM_ALIGNMENT */
105 #define PAYLOAD_ALIGNMENT 4
106 /* set this to 0 to prevent aligning payload */
107 #define ALIGN_PAYLOAD 1
108 /* define this to a type that has the required alignment */
109 #define PAYLOAD_ALIGN_TYPE "unsigned int"
110 static int payload_alingment_dummy_counter = 0;
111
112 #define HEX_BYTES_PER_LINE 16
113
114 #define MAX_PATH_LEN 256
115
116 struct file_entry {
117 struct file_entry *next;
118 const char *filename_c;
119 };
120
121 int process_sub(FILE *data_file, FILE *struct_file);
122 int process_file(FILE *data_file, FILE *struct_file, const char *filename);
123 int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
124 u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed);
125 int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
126 int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
127 void concat_files(const char *file1, const char *file2, const char *targetfile);
128 int check_path(char *path, size_t size);
129 static int checkSsiByFilelist(const char* filename_listfile);
130 static int ext_in_list(const char* filename, const char *ext_list);
131 static int file_to_exclude(const char* filename);
132 static int file_can_be_compressed(const char* filename);
133
134 /* 5 bytes per char + 3 bytes per line */
135 static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
136
137 char curSubdir[MAX_PATH_LEN-3];
138 char lastFileVar[MAX_PATH_LEN];
139 char hdr_buf[4096];
140
141 unsigned char processSubs = 1;
142 unsigned char includeHttpHeader = 1;
143 unsigned char useHttp11 = 0;
144 unsigned char supportSsi = 1;
145 unsigned char precalcChksum = 0;
146 unsigned char includeLastModified = 0;
147 #if MAKEFS_SUPPORT_DEFLATE
148 unsigned char deflateNonSsiFiles = 0;
149 size_t deflatedBytesReduced = 0;
150 size_t overallDataBytes = 0;
151 #endif
152 const char *exclude_list = NULL;
153 const char *ncompress_list = NULL;
154
155 struct file_entry *first_file = NULL;
156 struct file_entry *last_file = NULL;
157
158 static char *ssi_file_buffer;
159 static char **ssi_file_lines;
160 static size_t ssi_file_num_lines;
161
print_usage(void)162 static void print_usage(void)
163 {
164 printf(" Usage: htmlgen [targetdir] [-s] [-e] [-11] [-nossi] [-ssi:<filename>] [-c] [-f:<filename>] [-m] [-svr:<name>] [-x:<ext_list>] [-xc:<ext_list>" USAGE_ARG_DEFLATE NEWLINE NEWLINE);
165 printf(" targetdir: relative or absolute path to files to convert" NEWLINE);
166 printf(" switch -s: toggle processing of subdirectories (default is on)" NEWLINE);
167 printf(" switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE);
168 printf(" switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE);
169 printf(" switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE);
170 printf(" switch -ssi: ssi filename (ssi support controlled by file list, not by extension)" NEWLINE);
171 printf(" switch -c: precalculate checksums for all pages (default is off)" NEWLINE);
172 printf(" switch -f: target filename (default is \"fsdata.c\")" NEWLINE);
173 printf(" switch -m: include \"Last-Modified\" header based on file time" NEWLINE);
174 printf(" switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE);
175 printf(" switch -x: comma separated list of extensions of files to exclude (e.g., -x:json,txt)" NEWLINE);
176 printf(" switch -xc: comma separated list of extensions of files to not compress (e.g., -xc:mp3,jpg)" NEWLINE);
177 #if MAKEFS_SUPPORT_DEFLATE
178 printf(" switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE);
179 printf(" ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE);
180 #endif
181 printf(" if targetdir not specified, htmlgen will attempt to" NEWLINE);
182 printf(" process files in subdirectory 'fs'" NEWLINE);
183 }
184
main(int argc,char * argv[])185 int main(int argc, char *argv[])
186 {
187 char path[MAX_PATH_LEN];
188 char appPath[MAX_PATH_LEN];
189 FILE *data_file;
190 FILE *struct_file;
191 int filesProcessed;
192 int i;
193 char targetfile[MAX_PATH_LEN];
194 strcpy(targetfile, "fsdata.c");
195
196 memset(path, 0, sizeof(path));
197 memset(appPath, 0, sizeof(appPath));
198
199 printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE);
200 printf(" by Jim Pettinato - circa 2003 " NEWLINE);
201 printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE);
202
203 LWIP_ASSERT("sizeof(hdr_buf) must fit into an u16_t", sizeof(hdr_buf) <= 0xffff);
204
205 strcpy(path, "fs");
206 for (i = 1; i < argc; i++) {
207 if (argv[i] == NULL) {
208 continue;
209 }
210 if (argv[i][0] == '-') {
211 if (strstr(argv[i], "-svr:") == argv[i]) {
212 snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]);
213 serverID = serverIDBuffer;
214 printf("Using Server-ID: \"%s\"\n", serverID);
215 } else if (!strcmp(argv[i], "-s")) {
216 processSubs = 0;
217 } else if (!strcmp(argv[i], "-e")) {
218 includeHttpHeader = 0;
219 } else if (!strcmp(argv[i], "-11")) {
220 useHttp11 = 1;
221 } else if (!strcmp(argv[i], "-nossi")) {
222 supportSsi = 0;
223 } else if (strstr(argv[i], "-ssi:") == argv[i]) {
224 const char* ssi_list_filename = &argv[i][5];
225 if (checkSsiByFilelist(ssi_list_filename)) {
226 printf("Reading list of SSI files from \"%s\"\n", ssi_list_filename);
227 } else {
228 printf("Failed to load list of SSI files from \"%s\"\n", ssi_list_filename);
229 }
230 } else if (!strcmp(argv[i], "-c")) {
231 precalcChksum = 1;
232 } else if (strstr(argv[i], "-f:") == argv[i]) {
233 strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1);
234 targetfile[sizeof(targetfile) - 1] = 0;
235 printf("Writing to file \"%s\"\n", targetfile);
236 } else if (!strcmp(argv[i], "-m")) {
237 includeLastModified = 1;
238 } else if (!strcmp(argv[i], "-defl")) {
239 #if MAKEFS_SUPPORT_DEFLATE
240 char *colon = strstr(argv[i], ":");
241 if (colon) {
242 if (colon[1] != 0) {
243 int defl_level = atoi(&colon[1]);
244 if ((defl_level >= 0) && (defl_level <= 10)) {
245 deflate_level = defl_level;
246 } else {
247 printf("ERROR: deflate level must be [0..10]" NEWLINE);
248 exit(0);
249 }
250 }
251 }
252 deflateNonSsiFiles = 1;
253 printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level);
254 #else
255 printf("WARNING: Deflate support is disabled\n");
256 #endif
257 } else if (strstr(argv[i], "-x:") == argv[i]) {
258 exclude_list = &argv[i][3];
259 printf("Excluding files with extensions %s" NEWLINE, exclude_list);
260 } else if (strstr(argv[i], "-xc:") == argv[i]) {
261 ncompress_list = &argv[i][4];
262 printf("Skipping compresion for files with extensions %s" NEWLINE, ncompress_list);
263 } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) {
264 print_usage();
265 exit(0);
266 }
267 } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) {
268 print_usage();
269 exit(0);
270 } else {
271 strncpy(path, argv[i], sizeof(path) - 1);
272 path[sizeof(path) - 1] = 0;
273 }
274 }
275
276 if (!check_path(path, sizeof(path))) {
277 printf("Invalid path: \"%s\"." NEWLINE, path);
278 exit(-1);
279 }
280
281 if(!GETCWD_SUCCEEDED(GETCWD(appPath, MAX_PATH_LEN))) {
282 printf("Unable to get current dir." NEWLINE);
283 exit(-1);
284 }
285 /* if command line param or subdir named 'fs' not found spout usage verbiage */
286 if (!CHDIR_SUCCEEDED(CHDIR(path))) {
287 /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */
288 printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path);
289 print_usage();
290 exit(-1);
291 }
292 if(!CHDIR_SUCCEEDED(CHDIR(appPath))) {
293 printf("Invalid path: \"%s\"." NEWLINE, appPath);
294 exit(-1);
295 }
296
297 printf("HTTP %sheader will %s statically included." NEWLINE,
298 (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""),
299 (includeHttpHeader ? "be" : "not be"));
300
301 curSubdir[0] = '\0'; /* start off in web page's root directory - relative paths */
302 printf(" Processing all files in directory %s", path);
303 if (processSubs) {
304 printf(" and subdirectories..." NEWLINE NEWLINE);
305 } else {
306 printf("..." NEWLINE NEWLINE);
307 }
308
309 data_file = fopen("fsdata.tmp", "wb");
310 if (data_file == NULL) {
311 printf("Failed to create file \"fsdata.tmp\"\n");
312 exit(-1);
313 }
314 struct_file = fopen("fshdr.tmp", "wb");
315 if (struct_file == NULL) {
316 printf("Failed to create file \"fshdr.tmp\"\n");
317 fclose(data_file);
318 exit(-1);
319 }
320
321 if(!CHDIR_SUCCEEDED(CHDIR(path))) {
322 printf("Invalid path: \"%s\"." NEWLINE, path);
323 exit(-1);
324 }
325
326 fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
327 fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE NEWLINE NEWLINE);
328
329 fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
330 /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
331 fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE);
332 /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */
333 fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE);
334
335 /* define alignment defines */
336 #if ALIGN_PAYLOAD
337 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);
338 #endif
339 fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE" NEWLINE "#define FSDATA_ALIGN_PRE" NEWLINE "#endif" NEWLINE);
340 fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE);
341 #if ALIGN_PAYLOAD
342 fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE);
343 #endif
344
345 sprintf(lastFileVar, "NULL");
346
347 filesProcessed = process_sub(data_file, struct_file);
348
349 /* data_file now contains all of the raw data.. now append linked list of
350 * file header structs to allow embedded app to search for a file name */
351 fprintf(data_file, NEWLINE NEWLINE);
352 fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar);
353 fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed);
354
355 fclose(data_file);
356 fclose(struct_file);
357
358 if(!CHDIR_SUCCEEDED(CHDIR(appPath))) {
359 printf("Invalid path: \"%s\"." NEWLINE, appPath);
360 exit(-1);
361 }
362
363 /* append struct_file to data_file */
364 printf(NEWLINE "Creating target file..." NEWLINE NEWLINE);
365 concat_files("fsdata.tmp", "fshdr.tmp", targetfile);
366
367 /* if succeeded, delete the temporary files */
368 if (remove("fsdata.tmp") != 0) {
369 printf("Warning: failed to delete fsdata.tmp\n");
370 }
371 if (remove("fshdr.tmp") != 0) {
372 printf("Warning: failed to delete fshdr.tmp\n");
373 }
374
375 printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed);
376 #if MAKEFS_SUPPORT_DEFLATE
377 if (deflateNonSsiFiles) {
378 printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE,
379 (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced * 100.0) / overallDataBytes));
380 }
381 #endif
382 printf(NEWLINE);
383
384 while (first_file != NULL) {
385 struct file_entry *fe = first_file;
386 first_file = fe->next;
387 free(fe);
388 }
389
390 if (ssi_file_buffer) {
391 free(ssi_file_buffer);
392 }
393 if (ssi_file_lines) {
394 free(ssi_file_lines);
395 }
396
397 return 0;
398 }
399
check_path(char * path,size_t size)400 int check_path(char *path, size_t size)
401 {
402 size_t slen;
403 if (path[0] == 0) {
404 /* empty */
405 return 0;
406 }
407 slen = strlen(path);
408 if (slen >= size) {
409 /* not NULL-terminated */
410 return 0;
411 }
412 while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) {
413 /* path should not end with trailing backslash */
414 path[slen] = 0;
415 slen--;
416 }
417 if (slen == 0) {
418 return 0;
419 }
420 return 1;
421 }
422
copy_file(const char * filename_in,FILE * fout)423 static void copy_file(const char *filename_in, FILE *fout)
424 {
425 FILE *fin;
426 size_t len;
427 void *buf;
428 fin = fopen(filename_in, "rb");
429 if (fin == NULL) {
430 printf("Failed to open file \"%s\"\n", filename_in);
431 exit(-1);
432 }
433 buf = malloc(COPY_BUFSIZE);
434 while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) {
435 fwrite(buf, 1, len, fout);
436 }
437 free(buf);
438 fclose(fin);
439 }
440
concat_files(const char * file1,const char * file2,const char * targetfile)441 void concat_files(const char *file1, const char *file2, const char *targetfile)
442 {
443 FILE *fout;
444 fout = fopen(targetfile, "wb");
445 if (fout == NULL) {
446 printf("Failed to open file \"%s\"\n", targetfile);
447 exit(-1);
448 }
449 copy_file(file1, fout);
450 copy_file(file2, fout);
451 fclose(fout);
452 }
453
process_sub(FILE * data_file,FILE * struct_file)454 int process_sub(FILE *data_file, FILE *struct_file)
455 {
456 tinydir_dir dir;
457 int filesProcessed = 0;
458
459 if (processSubs) {
460 /* process subs recursively */
461 size_t sublen = strlen(curSubdir);
462 size_t freelen = sizeof(curSubdir) - sublen - 1;
463 int ret;
464 LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
465
466 ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
467
468 if (ret == 0) {
469 unsigned int i;
470 for (i = 0; i < dir.n_files; i++) {
471 tinydir_file file;
472
473 ret = tinydir_readfile_n(&dir, &file, i);
474
475 if (ret == 0) {
476 #if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
477 size_t num_char_converted;
478 char currName[256];
479 wcstombs_s(&num_char_converted, currName, sizeof(currName), file.name, sizeof(currName));
480 #else
481 const char *currName = file.name;
482 #endif
483
484 if (currName[0] == '.') {
485 continue;
486 }
487 if (!file.is_dir) {
488 continue;
489 }
490 if (freelen > 0) {
491 if(!CHDIR_SUCCEEDED(CHDIR(currName))) {
492 printf("Invalid path: \"%s\"." NEWLINE, currName);
493 exit(-1);
494 }
495 strncat(curSubdir, "/", freelen);
496 strncat(curSubdir, currName, freelen - 1);
497 curSubdir[sizeof(curSubdir) - 1] = 0;
498 printf("processing subdirectory %s/..." NEWLINE, curSubdir);
499 filesProcessed += process_sub(data_file, struct_file);
500 if(!CHDIR_SUCCEEDED(CHDIR(".."))) {
501 printf("Unable to get back to parent dir of: \"%s\"." NEWLINE, currName);
502 exit(-1);
503 }
504 curSubdir[sublen] = 0;
505 } else {
506 printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, currName);
507 }
508 }
509 }
510 }
511
512 ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
513 if (ret == 0) {
514 unsigned int i;
515 for (i = 0; i < dir.n_files; i++) {
516 tinydir_file file;
517
518 ret = tinydir_readfile_n(&dir, &file, i);
519
520 if (ret == 0) {
521 if (!file.is_dir) {
522 #if (defined _MSC_VER || defined __MINGW32__) && (defined _UNICODE)
523 size_t num_char_converted;
524 char curName[256];
525 wcstombs_s(&num_char_converted, curName, sizeof(curName), file.name, sizeof(curName));
526 #else
527 const char *curName = file.name;
528 #endif
529
530 if (strcmp(curName, "fsdata.tmp") == 0) {
531 continue;
532 }
533 if (strcmp(curName, "fshdr.tmp") == 0) {
534 continue;
535 }
536 if (file_to_exclude(curName)) {
537 printf("skipping %s/%s by exclude list (-x option)..." NEWLINE, curSubdir, curName);
538 continue;
539 }
540
541 printf("processing %s/%s..." NEWLINE, curSubdir, curName);
542
543 if (process_file(data_file, struct_file, curName) < 0) {
544 printf(NEWLINE "Error... aborting" NEWLINE);
545 return -1;
546 }
547 filesProcessed++;
548 }
549 }
550 }
551 }
552 }
553
554 return filesProcessed;
555 }
556
get_file_data(const char * filename,int * file_size,int can_be_compressed,int * is_compressed)557 static u8_t *get_file_data(const char *filename, int *file_size, int can_be_compressed, int *is_compressed)
558 {
559 FILE *inFile;
560 size_t fsize = 0;
561 u8_t *buf;
562 size_t r;
563 int rs;
564 LWIP_UNUSED_ARG(r); /* for LWIP_NOASSERT */
565 inFile = fopen(filename, "rb");
566 if (inFile == NULL) {
567 printf("Failed to open file \"%s\"\n", filename);
568 exit(-1);
569 }
570 fseek(inFile, 0, SEEK_END);
571 rs = ftell(inFile);
572 if (rs < 0) {
573 printf("ftell failed with %d\n", errno);
574 exit(-1);
575 }
576 fsize = (size_t)rs;
577 fseek(inFile, 0, SEEK_SET);
578 buf = (u8_t *)malloc(fsize);
579 LWIP_ASSERT("buf != NULL", buf != NULL);
580 r = fread(buf, 1, fsize, inFile);
581 LWIP_ASSERT("r == fsize", r == fsize);
582 *file_size = fsize;
583 *is_compressed = 0;
584 #if MAKEFS_SUPPORT_DEFLATE
585 overallDataBytes += fsize;
586 if (deflateNonSsiFiles) {
587 if (can_be_compressed) {
588 if (fsize < OUT_BUF_SIZE) {
589 u8_t *ret_buf;
590 tdefl_status status;
591 size_t in_bytes = fsize;
592 size_t out_bytes = OUT_BUF_SIZE;
593 const void *next_in = buf;
594 void *next_out = s_outbuf;
595 /* 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). */
596 mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
597 if (!deflate_level) {
598 comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
599 }
600 status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
601 if (status != TDEFL_STATUS_OKAY) {
602 printf("tdefl_init() failed!\n");
603 exit(-1);
604 }
605 memset(s_outbuf, 0, sizeof(s_outbuf));
606 status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH);
607 if (status != TDEFL_STATUS_DONE) {
608 printf("deflate failed: %d\n", status);
609 exit(-1);
610 }
611 LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE);
612 if (out_bytes < fsize) {
613 ret_buf = (u8_t *)malloc(out_bytes);
614 LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL);
615 memcpy(ret_buf, s_outbuf, out_bytes);
616 {
617 /* sanity-check compression be inflating and comparing to the original */
618 tinfl_status dec_status;
619 tinfl_decompressor inflator;
620 size_t dec_in_bytes = out_bytes;
621 size_t dec_out_bytes = OUT_BUF_SIZE;
622 next_out = s_checkbuf;
623
624 tinfl_init(&inflator);
625 memset(s_checkbuf, 0, sizeof(s_checkbuf));
626 dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0);
627 LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE);
628 LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes);
629 LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize));
630 }
631 /* free original buffer, use compressed data + size */
632 free(buf);
633 buf = ret_buf;
634 *file_size = out_bytes;
635 printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes * 100.0) / fsize));
636 deflatedBytesReduced += (size_t)(fsize - out_bytes);
637 *is_compressed = 1;
638 } else {
639 printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize));
640 }
641 } else {
642 printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE);
643 }
644 } else {
645 printf(" - cannot be compressed" NEWLINE);
646 }
647 }
648 #else
649 LWIP_UNUSED_ARG(can_be_compressed);
650 #endif
651 fclose(inFile);
652 return buf;
653 }
654
process_file_data(FILE * data_file,u8_t * file_data,size_t file_size)655 static void process_file_data(FILE *data_file, u8_t *file_data, size_t file_size)
656 {
657 size_t written, i, src_off = 0;
658 size_t off = 0;
659 LWIP_UNUSED_ARG(written); /* for LWIP_NOASSERT */
660 for (i = 0; i < file_size; i++) {
661 LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5);
662 sprintf(&file_buffer_c[off], "0x%02x,", file_data[i]);
663 off += 5;
664 if ((++src_off % HEX_BYTES_PER_LINE) == 0) {
665 LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN);
666 memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN);
667 off += NEWLINE_LEN;
668 }
669 if (off + 20 >= sizeof(file_buffer_c)) {
670 written = fwrite(file_buffer_c, 1, off, data_file);
671 LWIP_ASSERT("written == off", written == off);
672 off = 0;
673 }
674 }
675 written = fwrite(file_buffer_c, 1, off, data_file);
676 LWIP_ASSERT("written == off", written == off);
677 }
678
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)679 static int write_checksums(FILE *struct_file, const char *varname,
680 u16_t hdr_len, u16_t hdr_chksum, const u8_t *file_data, size_t file_size)
681 {
682 int chunk_size = TCP_MSS;
683 int offset, src_offset;
684 size_t len;
685 int i = 0;
686 #if LWIP_TCP_TIMESTAMPS
687 /* when timestamps are used, usable space is 12 bytes less per segment */
688 chunk_size -= 12;
689 #endif
690
691 fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
692 fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname);
693
694 if (hdr_len > 0) {
695 /* add checksum for HTTP header */
696 fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len);
697 i++;
698 }
699 src_offset = 0;
700 for (offset = hdr_len; ; offset += len) {
701 unsigned short chksum;
702 const void *data = (const void *)&file_data[src_offset];
703 len = LWIP_MIN(chunk_size, (int)file_size - src_offset);
704 if (len == 0) {
705 break;
706 }
707 chksum = ~inet_chksum(data, (u16_t)len);
708 /* add checksum for data */
709 fprintf(struct_file, "{%d, 0x%04x, %"SZT_F"}," NEWLINE, offset, chksum, len);
710 i++;
711 }
712 fprintf(struct_file, "};" NEWLINE);
713 fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
714 return i;
715 }
716
is_valid_char_for_c_var(char x)717 static int is_valid_char_for_c_var(char x)
718 {
719 if (((x >= 'A') && (x <= 'Z')) ||
720 ((x >= 'a') && (x <= 'z')) ||
721 ((x >= '0') && (x <= '9')) ||
722 (x == '_')) {
723 return 1;
724 }
725 return 0;
726 }
727
fix_filename_for_c(char * qualifiedName,size_t max_len)728 static void fix_filename_for_c(char *qualifiedName, size_t max_len)
729 {
730 struct file_entry *f;
731 size_t len = strlen(qualifiedName);
732 char *new_name = (char *)malloc(len + 2);
733 int filename_ok;
734 int cnt = 0;
735 size_t i;
736 if (len + 3 == max_len) {
737 printf("File name too long: \"%s\"\n", qualifiedName);
738 exit(-1);
739 }
740 strcpy(new_name, qualifiedName);
741 for (i = 0; i < len; i++) {
742 if (!is_valid_char_for_c_var(new_name[i])) {
743 new_name[i] = '_';
744 }
745 }
746 do {
747 filename_ok = 1;
748 for (f = first_file; f != NULL; f = f->next) {
749 if (!strcmp(f->filename_c, new_name)) {
750 filename_ok = 0;
751 cnt++;
752 /* try next unique file name */
753 sprintf(&new_name[len], "%d", cnt);
754 break;
755 }
756 }
757 } while (!filename_ok && (cnt < 999));
758 if (!filename_ok) {
759 printf("Failed to get unique file name: \"%s\"\n", qualifiedName);
760 exit(-1);
761 }
762 strcpy(qualifiedName, new_name);
763 free(new_name);
764 }
765
register_filename(const char * qualifiedName)766 static void register_filename(const char *qualifiedName)
767 {
768 struct file_entry *fe = (struct file_entry *)malloc(sizeof(struct file_entry));
769 fe->filename_c = strdup(qualifiedName);
770 fe->next = NULL;
771 if (first_file == NULL) {
772 first_file = last_file = fe;
773 } else {
774 last_file->next = fe;
775 last_file = fe;
776 }
777 }
778
checkSsiByFilelist(const char * filename_listfile)779 static int checkSsiByFilelist(const char* filename_listfile)
780 {
781 FILE *f = fopen(filename_listfile, "r");
782 if (f != NULL) {
783 char *buf;
784 long rs;
785 size_t fsize, readcount;
786 size_t i, l, num_lines;
787 char **lines;
788 int state;
789
790 fseek(f, 0, SEEK_END);
791 rs = ftell(f);
792 if (rs < 0) {
793 printf("ftell failed with %d\n", errno);
794 fclose(f);
795 return 0;
796 }
797 fsize = (size_t)rs;
798 fseek(f, 0, SEEK_SET);
799 buf = (char*)malloc(fsize);
800 if (!buf) {
801 printf("failed to allocate ssi file buffer\n");
802 fclose(f);
803 return 0;
804 }
805 memset(buf, 0, fsize);
806 readcount = fread(buf, 1, fsize, f);
807 fclose(f);
808 if ((readcount > fsize) || !readcount) {
809 printf("failed to read data from ssi file\n");
810 free(buf);
811 return 0;
812 }
813
814 /* first pass: get the number of lines (and convert newlines to '0') */
815 num_lines = 1;
816 for (i = 0; i < readcount; i++) {
817 if (buf[i] == '\n') {
818 num_lines++;
819 buf[i] = 0;
820 } else if (buf[i] == '\r') {
821 buf[i] = 0;
822 }
823 }
824 /* allocate the line pointer array */
825 lines = (char**)malloc(sizeof(char*) * num_lines);
826 if (!lines) {
827 printf("failed to allocate ssi line buffer\n");
828 free(buf);
829 return 0;
830 }
831 memset(lines, 0, sizeof(char*) * num_lines);
832 l = 0;
833 state = 0;
834 for (i = 0; i < readcount; i++) {
835 if (state) {
836 /* waiting for null */
837 if (buf[i] == 0) {
838 state = 0;
839 }
840 } else {
841 /* waiting for beginning of new string */
842 if (buf[i] != 0) {
843 LWIP_ASSERT("lines array overflow", l < num_lines);
844 lines[l] = &buf[i];
845 state = 1;
846 l++;
847 }
848 }
849 }
850 LWIP_ASSERT("lines array overflow", l < num_lines);
851
852 ssi_file_buffer = buf;
853 ssi_file_lines = lines;
854 ssi_file_num_lines = l;
855 }
856 return 0;
857 }
858
is_ssi_file(const char * filename)859 static int is_ssi_file(const char *filename)
860 {
861 if (supportSsi) {
862 if (ssi_file_buffer) {
863 /* compare by list */
864 size_t i;
865 int ret = 0;
866 /* build up the relative path to this file */
867 size_t sublen = strlen(curSubdir);
868 size_t freelen = sizeof(curSubdir) - sublen - 1;
869 strncat(curSubdir, "/", freelen);
870 strncat(curSubdir, filename, freelen - 1);
871 curSubdir[sizeof(curSubdir) - 1] = 0;
872 for (i = 0; i < ssi_file_num_lines; i++) {
873 const char *listed_file = ssi_file_lines[i];
874 /* compare without the leading '/' */
875 if (!strcmp(&curSubdir[1], listed_file)) {
876 ret = 1;
877 }
878 }
879 curSubdir[sublen] = 0;
880 return ret;
881 #if LWIP_HTTPD_SSI_BY_FILE_EXTENSION
882 } else {
883 /* check file extension */
884 size_t loop;
885 for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
886 if (strstr(filename, g_pcSSIExtensions[loop])) {
887 return 1;
888 }
889 }
890 #endif /* LWIP_HTTPD_SSI_BY_FILE_EXTENSION */
891 }
892 }
893 return 0;
894 }
895
ext_in_list(const char * filename,const char * ext_list)896 static int ext_in_list(const char* filename, const char *ext_list)
897 {
898 int found = 0;
899 const char *ext = ext_list;
900 if (ext_list == NULL) {
901 return 0;
902 }
903 while(*ext != '\0') {
904 const char *comma = strchr(ext, ',');
905 size_t ext_size;
906 size_t filename_size = strlen(filename);
907 if (comma == NULL) {
908 comma = strchr(ext, '\0');
909 }
910 ext_size = comma - ext;
911 if ((filename[filename_size - ext_size - 1] == '.') &&
912 !strncmp(&filename[filename_size - ext_size], ext, ext_size)) {
913 found = 1;
914 break;
915 }
916 ext = comma + 1;
917 }
918
919 return found;
920 }
921
file_to_exclude(const char * filename)922 static int file_to_exclude(const char *filename)
923 {
924 return (exclude_list != NULL) && ext_in_list(filename, exclude_list);
925 }
926
file_can_be_compressed(const char * filename)927 static int file_can_be_compressed(const char *filename)
928 {
929 return (ncompress_list == NULL) || !ext_in_list(filename, ncompress_list);
930 }
931
process_file(FILE * data_file,FILE * struct_file,const char * filename)932 int process_file(FILE *data_file, FILE *struct_file, const char *filename)
933 {
934 char varname[MAX_PATH_LEN];
935 int i = 0;
936 char qualifiedName[MAX_PATH_LEN];
937 int file_size;
938 u16_t http_hdr_chksum = 0;
939 u16_t http_hdr_len = 0;
940 int chksum_count = 0;
941 u8_t flags = 0;
942 u8_t has_content_len;
943 u8_t *file_data;
944 int is_ssi;
945 int can_be_compressed;
946 int is_compressed = 0;
947 int flags_printed;
948
949 /* create qualified name (@todo: prepend slash or not?) */
950 snprintf(qualifiedName, sizeof(qualifiedName), "%s/%s", curSubdir, filename);
951 /* create C variable name */
952 strncpy(varname, qualifiedName, sizeof(varname));
953 /* convert slashes & dots to underscores */
954 fix_filename_for_c(varname, MAX_PATH_LEN);
955 register_filename(varname);
956 #if ALIGN_PAYLOAD
957 /* to force even alignment of array, type 1 */
958 fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE);
959 fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++);
960 fprintf(data_file, "#endif" NEWLINE);
961 #endif /* ALIGN_PAYLOAD */
962 fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname);
963 /* encode source file name (used by file system, not returned to browser) */
964 fprintf(data_file, "/* %s (%"SZT_F" chars) */" NEWLINE, qualifiedName, strlen(qualifiedName) + 1);
965 file_put_ascii(data_file, qualifiedName, strlen(qualifiedName) + 1, &i);
966 #if ALIGN_PAYLOAD
967 /* pad to even number of bytes to assure payload is on aligned boundary */
968 while (i % PAYLOAD_ALIGNMENT != 0) {
969 fprintf(data_file, "0x%02x,", 0);
970 i++;
971 }
972 #endif /* ALIGN_PAYLOAD */
973 fprintf(data_file, NEWLINE);
974
975 is_ssi = is_ssi_file(filename);
976 if (is_ssi) {
977 flags |= FS_FILE_FLAGS_SSI;
978 }
979 has_content_len = !is_ssi;
980 can_be_compressed = includeHttpHeader && !is_ssi && file_can_be_compressed(filename);
981 file_data = get_file_data(filename, &file_size, can_be_compressed, &is_compressed);
982 if (includeHttpHeader) {
983 file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed);
984 flags |= FS_FILE_FLAGS_HEADER_INCLUDED;
985 if (has_content_len) {
986 flags |= FS_FILE_FLAGS_HEADER_PERSISTENT;
987 if (useHttp11) {
988 flags |= FS_FILE_FLAGS_HEADER_HTTPVER_1_1;
989 }
990 }
991 }
992 if (precalcChksum) {
993 chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size);
994 }
995
996 /* build declaration of struct fsdata_file in temp file */
997 fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname);
998 fprintf(struct_file, "file_%s," NEWLINE, lastFileVar);
999 fprintf(struct_file, "data_%s," NEWLINE, varname);
1000 fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i);
1001 fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i);
1002
1003 flags_printed = 0;
1004 if (flags & FS_FILE_FLAGS_HEADER_INCLUDED) {
1005 fputs("FS_FILE_FLAGS_HEADER_INCLUDED", struct_file);
1006 flags_printed = 1;
1007 }
1008 if (flags & FS_FILE_FLAGS_HEADER_PERSISTENT) {
1009 if (flags_printed) {
1010 fputs(" | ", struct_file);
1011 }
1012 fputs("FS_FILE_FLAGS_HEADER_PERSISTENT", struct_file);
1013 flags_printed = 1;
1014 }
1015 if (flags & FS_FILE_FLAGS_HEADER_HTTPVER_1_1) {
1016 if (flags_printed) {
1017 fputs(" | ", struct_file);
1018 }
1019 fputs("FS_FILE_FLAGS_HEADER_HTTPVER_1_1", struct_file);
1020 flags_printed = 1;
1021 }
1022 if (flags & FS_FILE_FLAGS_SSI) {
1023 if (flags_printed) {
1024 fputs(" | ", struct_file);
1025 }
1026 fputs("FS_FILE_FLAGS_SSI", struct_file);
1027 flags_printed = 1;
1028 }
1029 if (!flags_printed) {
1030 fputs("0", struct_file);
1031 }
1032 fputs("," NEWLINE, struct_file);
1033 if (precalcChksum) {
1034 fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
1035 fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname);
1036 fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
1037 }
1038 fprintf(struct_file, "}};" NEWLINE NEWLINE);
1039 strcpy(lastFileVar, varname);
1040
1041 /* write actual file contents */
1042 i = 0;
1043 fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size);
1044 process_file_data(data_file, file_data, file_size);
1045 fprintf(data_file, "};" NEWLINE NEWLINE);
1046 free(file_data);
1047 return 0;
1048 }
1049
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)1050 int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
1051 u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed)
1052 {
1053 int i = 0;
1054 int response_type = HTTP_HDR_OK;
1055 const char *file_type;
1056 const char *cur_string;
1057 size_t cur_len;
1058 int written = 0;
1059 size_t hdr_len = 0;
1060 u16_t acc;
1061 const char *file_ext;
1062 size_t j;
1063 u8_t provide_last_modified = includeLastModified;
1064
1065 memset(hdr_buf, 0, sizeof(hdr_buf));
1066
1067 if (useHttp11) {
1068 response_type = HTTP_HDR_OK_11;
1069 }
1070
1071 fprintf(data_file, NEWLINE "/* HTTP header */");
1072 if (strstr(filename, "404") == filename) {
1073 response_type = HTTP_HDR_NOT_FOUND;
1074 if (useHttp11) {
1075 response_type = HTTP_HDR_NOT_FOUND_11;
1076 }
1077 } else if (strstr(filename, "400") == filename) {
1078 response_type = HTTP_HDR_BAD_REQUEST;
1079 if (useHttp11) {
1080 response_type = HTTP_HDR_BAD_REQUEST_11;
1081 }
1082 } else if (strstr(filename, "501") == filename) {
1083 response_type = HTTP_HDR_NOT_IMPL;
1084 if (useHttp11) {
1085 response_type = HTTP_HDR_NOT_IMPL_11;
1086 }
1087 }
1088 cur_string = g_psHTTPHeaderStrings[response_type];
1089 cur_len = strlen(cur_string);
1090 fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
1091 written += file_put_ascii(data_file, cur_string, cur_len, &i);
1092 i = 0;
1093 if (precalcChksum) {
1094 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1095 hdr_len += cur_len;
1096 }
1097
1098 cur_string = serverID;
1099 cur_len = strlen(cur_string);
1100 fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
1101 written += file_put_ascii(data_file, cur_string, cur_len, &i);
1102 i = 0;
1103 if (precalcChksum) {
1104 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1105 hdr_len += cur_len;
1106 }
1107
1108 file_ext = filename;
1109 if (file_ext != NULL) {
1110 while (strstr(file_ext, ".") != NULL) {
1111 file_ext = strstr(file_ext, ".");
1112 file_ext++;
1113 }
1114 }
1115 if ((file_ext == NULL) || (*file_ext == 0)) {
1116 printf("failed to get extension for file \"%s\", using default.\n", filename);
1117 file_type = HTTP_HDR_DEFAULT_TYPE;
1118 } else {
1119 file_type = NULL;
1120 for (j = 0; j < NUM_HTTP_HEADERS; j++) {
1121 if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) {
1122 file_type = g_psHTTPHeaders[j].content_type;
1123 break;
1124 }
1125 }
1126 if (file_type == NULL) {
1127 printf("failed to get file type for extension \"%s\", using default.\n", file_ext);
1128 file_type = HTTP_HDR_DEFAULT_TYPE;
1129 }
1130 }
1131
1132 /* Content-Length is used for persistent connections in HTTP/1.1 but also for
1133 download progress in older versions
1134 @todo: just use a big-enough buffer and let the HTTPD send spaces? */
1135 if (provide_content_len) {
1136 char intbuf[MAX_PATH_LEN];
1137 int content_len = file_size;
1138 memset(intbuf, 0, sizeof(intbuf));
1139 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
1140 cur_len = strlen(cur_string);
1141 fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, content_len, cur_len + 2);
1142 written += file_put_ascii(data_file, cur_string, cur_len, &i);
1143 if (precalcChksum) {
1144 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1145 hdr_len += cur_len;
1146 }
1147
1148 lwip_itoa(intbuf, sizeof(intbuf), content_len);
1149 strcat(intbuf, "\r\n");
1150 cur_len = strlen(intbuf);
1151 written += file_put_ascii(data_file, intbuf, cur_len, &i);
1152 i = 0;
1153 if (precalcChksum) {
1154 memcpy(&hdr_buf[hdr_len], intbuf, cur_len);
1155 hdr_len += cur_len;
1156 }
1157 }
1158 if (provide_last_modified) {
1159 char modbuf[256];
1160 struct stat stat_data;
1161 struct tm *t;
1162 memset(modbuf, 0, sizeof(modbuf));
1163 memset(&stat_data, 0, sizeof(stat_data));
1164 cur_string = modbuf;
1165 strcpy(modbuf, "Last-Modified: ");
1166 if (stat(filename, &stat_data) != 0) {
1167 printf("stat(%s) failed with error %d\n", filename, errno);
1168 exit(-1);
1169 }
1170 t = gmtime(&stat_data.st_mtime);
1171 if (t == NULL) {
1172 printf("gmtime() failed with error %d\n", errno);
1173 exit(-1);
1174 }
1175 strftime(&modbuf[15], sizeof(modbuf) - 15, "%a, %d %b %Y %H:%M:%S GMT", t);
1176 cur_len = strlen(cur_string);
1177 fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%"SZT_F"+ bytes) */" NEWLINE, cur_string, cur_len + 2);
1178 written += file_put_ascii(data_file, cur_string, cur_len, &i);
1179 if (precalcChksum) {
1180 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1181 hdr_len += cur_len;
1182 }
1183
1184 modbuf[0] = 0;
1185 strcat(modbuf, "\r\n");
1186 cur_len = strlen(modbuf);
1187 written += file_put_ascii(data_file, modbuf, cur_len, &i);
1188 i = 0;
1189 if (precalcChksum) {
1190 memcpy(&hdr_buf[hdr_len], modbuf, cur_len);
1191 hdr_len += cur_len;
1192 }
1193 }
1194
1195 /* HTTP/1.1 implements persistent connections */
1196 if (useHttp11) {
1197 if (provide_content_len) {
1198 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE];
1199 } else {
1200 /* no Content-Length available, so a persistent connection is no possible
1201 because the client does not know the data length */
1202 cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
1203 }
1204 cur_len = strlen(cur_string);
1205 fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
1206 written += file_put_ascii(data_file, cur_string, cur_len, &i);
1207 i = 0;
1208 if (precalcChksum) {
1209 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1210 hdr_len += cur_len;
1211 }
1212 }
1213
1214 #if MAKEFS_SUPPORT_DEFLATE
1215 if (is_compressed) {
1216 /* tell the client about the deflate encoding */
1217 LWIP_ASSERT("error", deflateNonSsiFiles);
1218 cur_string = "Content-Encoding: deflate\r\n";
1219 cur_len = strlen(cur_string);
1220 fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
1221 written += file_put_ascii(data_file, cur_string, cur_len, &i);
1222 i = 0;
1223 }
1224 #else
1225 LWIP_UNUSED_ARG(is_compressed);
1226 #endif
1227
1228 /* write content-type, ATTENTION: this includes the double-CRLF! */
1229 cur_string = file_type;
1230 cur_len = strlen(cur_string);
1231 fprintf(data_file, NEWLINE "/* \"%s\" (%"SZT_F" bytes) */" NEWLINE, cur_string, cur_len);
1232 written += file_put_ascii(data_file, cur_string, cur_len, &i);
1233 i = 0;
1234
1235 /* ATTENTION: headers are done now (double-CRLF has been written!) */
1236
1237 if (precalcChksum) {
1238 LWIP_ASSERT("hdr_len + cur_len <= sizeof(hdr_buf)", hdr_len + cur_len <= sizeof(hdr_buf));
1239 memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
1240 hdr_len += cur_len;
1241
1242 LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
1243 acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
1244 *http_hdr_len = (u16_t)hdr_len;
1245 *http_hdr_chksum = acc;
1246 }
1247
1248 return written;
1249 }
1250
file_put_ascii(FILE * file,const char * ascii_string,int len,int * i)1251 int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i)
1252 {
1253 int x;
1254 for (x = 0; x < len; x++) {
1255 unsigned char cur = ascii_string[x];
1256 fprintf(file, "0x%02x,", cur);
1257 if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
1258 fprintf(file, NEWLINE);
1259 }
1260 }
1261 return len;
1262 }
1263
s_put_ascii(char * buf,const char * ascii_string,int len,int * i)1264 int s_put_ascii(char *buf, const char *ascii_string, int len, int *i)
1265 {
1266 int x;
1267 int idx = 0;
1268 for (x = 0; x < len; x++) {
1269 unsigned char cur = ascii_string[x];
1270 sprintf(&buf[idx], "0x%02x,", cur);
1271 idx += 5;
1272 if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
1273 sprintf(&buf[idx], NEWLINE);
1274 idx += NEWLINE_LEN;
1275 }
1276 }
1277 return len;
1278 }
1279