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
35typedef unsigned char uint8;
36typedef unsigned short uint16;
37typedef 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
49static uint8 s_outbuf[OUT_BUF_SIZE];
50static 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. */
54tdefl_compressor g_deflator;
55tinfl_decompressor g_inflator;
56
57int 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) */
95const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
96char 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"
104static int payload_alingment_dummy_counter = 0;
105
106#define HEX_BYTES_PER_LINE 16
107
108#define MAX_PATH_LEN 256
109
110struct file_entry {
111  struct file_entry *next;
112  const char *filename_c;
113};
114
115int process_sub(FILE *data_file, FILE *struct_file);
116int process_file(FILE *data_file, FILE *struct_file, const char *filename);
117int 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);
119int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
120int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
121void concat_files(const char *file1, const char *file2, const char *targetfile);
122int check_path(char *path, size_t size);
123static int checkSsiByFilelist(const char* filename_listfile);
124static int ext_in_list(const char* filename, const char *ext_list);
125static int file_to_exclude(const char* filename);
126static int file_can_be_compressed(const char* filename);
127
128/* 5 bytes per char + 3 bytes per line */
129static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
130
131char curSubdir[MAX_PATH_LEN];
132char lastFileVar[MAX_PATH_LEN];
133char hdr_buf[4096];
134
135unsigned char processSubs = 1;
136unsigned char includeHttpHeader = 1;
137unsigned char useHttp11 = 0;
138unsigned char supportSsi = 1;
139unsigned char precalcChksum = 0;
140unsigned char includeLastModified = 0;
141#if MAKEFS_SUPPORT_DEFLATE
142unsigned char deflateNonSsiFiles = 0;
143size_t deflatedBytesReduced = 0;
144size_t overallDataBytes = 0;
145#endif
146const char *exclude_list = NULL;
147const char *ncompress_list = NULL;
148
149struct file_entry *first_file = NULL;
150struct file_entry *last_file = NULL;
151
152static char *ssi_file_buffer;
153static char **ssi_file_lines;
154static size_t ssi_file_num_lines;
155
156static 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
179int 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
381int 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
404static 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
422void 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
435int 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
532static 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
630static 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
654static 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
692static 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
703static 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
741static 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
754static 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
834static 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
869static 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
895static int file_to_exclude(const char *filename)
896{
897    return (exclude_list != NULL) && ext_in_list(filename, exclude_list);
898}
899
900static int file_can_be_compressed(const char *filename)
901{
902    return (ncompress_list == NULL) || !ext_in_list(filename, ncompress_list);
903}
904
905int 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
1023int 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
1224int 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
1237int 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