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