xref: /btstack/3rd-party/lwip/core/src/apps/http/makefsdata/makefsdata.c (revision 97dc5e692c7d94a280158af58036a0efee5b0e56)
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