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