| /** |
| * makefsdata: Converts a directory structure for use with the lwIP httpd. |
| * |
| * This file is part of the lwIP TCP/IP stack. |
| * |
| * Author: Jim Pettinato |
| * Simon Goldschmidt |
| * |
| * @todo: |
| * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and |
| * PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #ifdef WIN32 |
| #define WIN32_LEAN_AND_MEAN |
| #include "windows.h" |
| #else |
| #include <dir.h> |
| #endif |
| #include <dos.h> |
| #include <string.h> |
| #include <time.h> |
| #include <sys/stat.h> |
| |
| /** Makefsdata can generate *all* files deflate-compressed (where file size shrinks). |
| * Since nearly all browsers support this, this is a good way to reduce ROM size. |
| * To compress the files, "miniz.c" must be downloaded seperately. |
| */ |
| #ifndef MAKEFS_SUPPORT_DEFLATE |
| #define MAKEFS_SUPPORT_DEFLATE 0 |
| #endif |
| |
| #define COPY_BUFSIZE (1024*1024) /* 1 MByte */ |
| |
| #if MAKEFS_SUPPORT_DEFLATE |
| #include "../miniz.c" |
| |
| typedef unsigned char uint8; |
| typedef unsigned short uint16; |
| typedef unsigned int uint; |
| |
| #define my_max(a,b) (((a) > (b)) ? (a) : (b)) |
| #define my_min(a,b) (((a) < (b)) ? (a) : (b)) |
| |
| /* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression. |
| COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */ |
| #define COMP_OUT_BUF_SIZE COPY_BUFSIZE |
| |
| /* OUT_BUF_SIZE is the size of the output buffer used during decompression. |
| 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) */ |
| #define OUT_BUF_SIZE COPY_BUFSIZE |
| static uint8 s_outbuf[OUT_BUF_SIZE]; |
| static uint8 s_checkbuf[OUT_BUF_SIZE]; |
| |
| /* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). |
| 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. */ |
| tdefl_compressor g_deflator; |
| tinfl_decompressor g_inflator; |
| |
| int deflate_level = 10; /* default compression level, can be changed via command line */ |
| #define USAGE_ARG_DEFLATE " [-defl<:compr_level>]" |
| #else /* MAKEFS_SUPPORT_DEFLATE */ |
| #define USAGE_ARG_DEFLATE "" |
| #endif /* MAKEFS_SUPPORT_DEFLATE */ |
| |
| /* Compatibility defines Win32 vs. DOS */ |
| #ifdef WIN32 |
| |
| #define FIND_T WIN32_FIND_DATAA |
| #define FIND_T_FILENAME(fInfo) (fInfo.cFileName) |
| #define FIND_T_IS_DIR(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) |
| #define FIND_T_IS_FILE(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) |
| #define FIND_RET_T HANDLE |
| #define FINDFIRST_FILE(path, result) FindFirstFileA(path, result) |
| #define FINDFIRST_DIR(path, result) FindFirstFileA(path, result) |
| #define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result) |
| #define FINDFIRST_SUCCEEDED(ret) (ret != INVALID_HANDLE_VALUE) |
| #define FINDNEXT_SUCCEEDED(ret) (ret == TRUE) |
| |
| #define GETCWD(path, len) GetCurrentDirectoryA(len, path) |
| #define CHDIR(path) SetCurrentDirectoryA(path) |
| #define CHDIR_SUCCEEDED(ret) (ret == TRUE) |
| |
| #else |
| |
| #define FIND_T struct ffblk |
| #define FIND_T_FILENAME(fInfo) (fInfo.ff_name) |
| #define FIND_T_IS_DIR(fInfo) ((fInfo.ff_attrib & FA_DIREC) == FA_DIREC) |
| #define FIND_T_IS_FILE(fInfo) (1) |
| #define FIND_RET_T int |
| #define FINDFIRST_FILE(path, result) findfirst(path, result, FA_ARCH) |
| #define FINDFIRST_DIR(path, result) findfirst(path, result, FA_DIREC) |
| #define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result) |
| #define FINDFIRST_SUCCEEDED(ret) (ret == 0) |
| #define FINDNEXT_SUCCEEDED(ret) (ret == 0) |
| |
| #define GETCWD(path, len) getcwd(path, len) |
| #define CHDIR(path) chdir(path) |
| #define CHDIR_SUCCEEDED(ret) (ret == 0) |
| |
| #endif |
| |
| #define NEWLINE "\r\n" |
| #define NEWLINE_LEN 2 |
| |
| /* define this to get the header variables we use to build HTTP headers */ |
| #define LWIP_HTTPD_DYNAMIC_HEADERS 1 |
| #define LWIP_HTTPD_SSI 1 |
| #include "lwip/init.h" |
| #include "../httpd_structs.h" |
| #include "lwip/apps/fs.h" |
| |
| #include "../core/inet_chksum.c" |
| #include "../core/def.c" |
| |
| /** (Your server name here) */ |
| const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n"; |
| char serverIDBuffer[1024]; |
| |
| /* change this to suit your MEM_ALIGNMENT */ |
| #define PAYLOAD_ALIGNMENT 4 |
| /* set this to 0 to prevent aligning payload */ |
| #define ALIGN_PAYLOAD 1 |
| /* define this to a type that has the required alignment */ |
| #define PAYLOAD_ALIGN_TYPE "unsigned int" |
| static int payload_alingment_dummy_counter = 0; |
| |
| #define HEX_BYTES_PER_LINE 16 |
| |
| #define MAX_PATH_LEN 256 |
| |
| struct file_entry |
| { |
| struct file_entry* next; |
| const char* filename_c; |
| }; |
| |
| int process_sub(FILE *data_file, FILE *struct_file); |
| int process_file(FILE *data_file, FILE *struct_file, const char *filename); |
| int 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); |
| int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i); |
| int s_put_ascii(char *buf, const char *ascii_string, int len, int *i); |
| void concat_files(const char *file1, const char *file2, const char *targetfile); |
| int check_path(char* path, size_t size); |
| |
| /* 5 bytes per char + 3 bytes per line */ |
| static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)]; |
| |
| char curSubdir[MAX_PATH_LEN]; |
| char lastFileVar[MAX_PATH_LEN]; |
| char hdr_buf[4096]; |
| |
| unsigned char processSubs = 1; |
| unsigned char includeHttpHeader = 1; |
| unsigned char useHttp11 = 0; |
| unsigned char supportSsi = 1; |
| unsigned char precalcChksum = 0; |
| unsigned char includeLastModified = 0; |
| #if MAKEFS_SUPPORT_DEFLATE |
| unsigned char deflateNonSsiFiles = 0; |
| size_t deflatedBytesReduced = 0; |
| size_t overallDataBytes = 0; |
| #endif |
| |
| struct file_entry* first_file = NULL; |
| struct file_entry* last_file = NULL; |
| |
| static void print_usage(void) |
| { |
| printf(" Usage: htmlgen [targetdir] [-s] [-e] [-i] [-11] [-nossi] [-c] [-f:<filename>] [-m] [-svr:<name>]" USAGE_ARG_DEFLATE NEWLINE NEWLINE); |
| printf(" targetdir: relative or absolute path to files to convert" NEWLINE); |
| printf(" switch -s: toggle processing of subdirectories (default is on)" NEWLINE); |
| printf(" switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE); |
| printf(" switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE); |
| printf(" switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE); |
| printf(" switch -c: precalculate checksums for all pages (default is off)" NEWLINE); |
| printf(" switch -f: target filename (default is \"fsdata.c\")" NEWLINE); |
| printf(" switch -m: include \"Last-Modified\" header based on file time" NEWLINE); |
| printf(" switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE); |
| #if MAKEFS_SUPPORT_DEFLATE |
| printf(" switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE); |
| printf(" ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE); |
| #endif |
| printf(" if targetdir not specified, htmlgen will attempt to" NEWLINE); |
| printf(" process files in subdirectory 'fs'" NEWLINE); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| char path[MAX_PATH_LEN]; |
| char appPath[MAX_PATH_LEN]; |
| FILE *data_file; |
| FILE *struct_file; |
| int filesProcessed; |
| int i; |
| char targetfile[MAX_PATH_LEN]; |
| strcpy(targetfile, "fsdata.c"); |
| |
| memset(path, 0, sizeof(path)); |
| memset(appPath, 0, sizeof(appPath)); |
| |
| printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE); |
| printf(" by Jim Pettinato - circa 2003 " NEWLINE); |
| printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE); |
| |
| strcpy(path, "fs"); |
| for (i = 1; i < argc; i++) { |
| if (argv[i] == NULL) { |
| continue; |
| } |
| if (argv[i][0] == '-') { |
| if (strstr(argv[i], "-svr:") == argv[i]) { |
| snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]); |
| serverID = serverIDBuffer; |
| printf("Using Server-ID: \"%s\"\n", serverID); |
| } else if (strstr(argv[i], "-s") == argv[i]) { |
| processSubs = 0; |
| } else if (strstr(argv[i], "-e") == argv[i]) { |
| includeHttpHeader = 0; |
| } else if (strstr(argv[i], "-11") == argv[i]) { |
| useHttp11 = 1; |
| } else if (strstr(argv[i], "-nossi") == argv[i]) { |
| supportSsi = 0; |
| } else if (strstr(argv[i], "-c") == argv[i]) { |
| precalcChksum = 1; |
| } else if (strstr(argv[i], "-f:") == argv[i]) { |
| strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1); |
| targetfile[sizeof(targetfile) - 1] = 0; |
| printf("Writing to file \"%s\"\n", targetfile); |
| } else if (strstr(argv[i], "-m") == argv[i]) { |
| includeLastModified = 1; |
| } else if (strstr(argv[i], "-defl") == argv[i]) { |
| #if MAKEFS_SUPPORT_DEFLATE |
| char* colon = strstr(argv[i], ":"); |
| if (colon) { |
| if (colon[1] != 0) { |
| int defl_level = atoi(&colon[1]); |
| if ((defl_level >= 0) && (defl_level <= 10)) { |
| deflate_level = defl_level; |
| } else { |
| printf("ERROR: deflate level must be [0..10]" NEWLINE); |
| exit(0); |
| } |
| } |
| } |
| deflateNonSsiFiles = 1; |
| printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level); |
| #else |
| printf("WARNING: Deflate support is disabled\n"); |
| #endif |
| } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) { |
| print_usage(); |
| exit(0); |
| } |
| } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) { |
| print_usage(); |
| exit(0); |
| } else { |
| strncpy(path, argv[i], sizeof(path)-1); |
| path[sizeof(path)-1] = 0; |
| } |
| } |
| |
| if (!check_path(path, sizeof(path))) { |
| printf("Invalid path: \"%s\"." NEWLINE, path); |
| exit(-1); |
| } |
| |
| GETCWD(appPath, MAX_PATH_LEN); |
| /* if command line param or subdir named 'fs' not found spout usage verbiage */ |
| if (!CHDIR_SUCCEEDED(CHDIR(path))) { |
| /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */ |
| printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path); |
| print_usage(); |
| exit(-1); |
| } |
| CHDIR(appPath); |
| |
| printf("HTTP %sheader will %s statically included." NEWLINE, |
| (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""), |
| (includeHttpHeader ? "be" : "not be")); |
| |
| sprintf(curSubdir, ""); /* start off in web page's root directory - relative paths */ |
| printf(" Processing all files in directory %s", path); |
| if (processSubs) { |
| printf(" and subdirectories..." NEWLINE NEWLINE); |
| } else { |
| printf("..." NEWLINE NEWLINE); |
| } |
| |
| data_file = fopen("fsdata.tmp", "wb"); |
| if (data_file == NULL) { |
| printf("Failed to create file \"fsdata.tmp\"\n"); |
| exit(-1); |
| } |
| struct_file = fopen("fshdr.tmp", "wb"); |
| if (struct_file == NULL) { |
| printf("Failed to create file \"fshdr.tmp\"\n"); |
| fclose(data_file); |
| exit(-1); |
| } |
| |
| CHDIR(path); |
| |
| fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE); |
| fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE); |
| fprintf(data_file, "#include \"fsdata.h\"" NEWLINE NEWLINE NEWLINE); |
| |
| fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE); |
| /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */ |
| fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE); |
| /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */ |
| fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE); |
| |
| /* define alignment defines */ |
| #if ALIGN_PAYLOAD |
| 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); |
| #endif |
| fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE" NEWLINE "#define FSDATA_ALIGN_PRE" NEWLINE "#endif" NEWLINE); |
| fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE); |
| #if ALIGN_PAYLOAD |
| fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE); |
| #endif |
| |
| sprintf(lastFileVar, "NULL"); |
| |
| filesProcessed = process_sub(data_file, struct_file); |
| |
| /* data_file now contains all of the raw data.. now append linked list of |
| * file header structs to allow embedded app to search for a file name */ |
| fprintf(data_file, NEWLINE NEWLINE); |
| fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar); |
| fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed); |
| |
| fclose(data_file); |
| fclose(struct_file); |
| |
| CHDIR(appPath); |
| /* append struct_file to data_file */ |
| printf(NEWLINE "Creating target file..." NEWLINE NEWLINE); |
| concat_files("fsdata.tmp", "fshdr.tmp", targetfile); |
| |
| /* if succeeded, delete the temporary files */ |
| if (remove("fsdata.tmp") != 0) { |
| printf("Warning: failed to delete fsdata.tmp\n"); |
| } |
| if (remove("fshdr.tmp") != 0) { |
| printf("Warning: failed to delete fshdr.tmp\n"); |
| } |
| |
| printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed); |
| #if MAKEFS_SUPPORT_DEFLATE |
| if (deflateNonSsiFiles) { |
| printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE, |
| (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced*100.0)/overallDataBytes)); |
| } |
| #endif |
| printf(NEWLINE); |
| |
| while (first_file != NULL) { |
| struct file_entry* fe = first_file; |
| first_file = fe->next; |
| free(fe); |
| } |
| |
| return 0; |
| } |
| |
| int check_path(char* path, size_t size) |
| { |
| size_t slen; |
| if (path[0] == 0) { |
| /* empty */ |
| return 0; |
| } |
| slen = strlen(path); |
| if (slen >= size) { |
| /* not NULL-terminated */ |
| return 0; |
| } |
| while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) { |
| /* path should not end with trailing backslash */ |
| path[slen] = 0; |
| slen--; |
| } |
| if (slen == 0) { |
| return 0; |
| } |
| return 1; |
| } |
| |
| static void copy_file(const char *filename_in, FILE *fout) |
| { |
| FILE *fin; |
| size_t len; |
| void* buf; |
| fin = fopen(filename_in, "rb"); |
| if (fin == NULL) { |
| printf("Failed to open file \"%s\"\n", filename_in); |
| exit(-1); |
| } |
| buf = malloc(COPY_BUFSIZE); |
| while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) { |
| fwrite(buf, 1, len, fout); |
| } |
| free(buf); |
| fclose(fin); |
| } |
| |
| void concat_files(const char *file1, const char *file2, const char *targetfile) |
| { |
| FILE *fout; |
| fout = fopen(targetfile, "wb"); |
| if (fout == NULL) { |
| printf("Failed to open file \"%s\"\n", targetfile); |
| exit(-1); |
| } |
| copy_file(file1, fout); |
| copy_file(file2, fout); |
| fclose(fout); |
| } |
| |
| int process_sub(FILE *data_file, FILE *struct_file) |
| { |
| FIND_T fInfo; |
| FIND_RET_T fret; |
| int filesProcessed = 0; |
| |
| if (processSubs) { |
| /* process subs recursively */ |
| size_t sublen = strlen(curSubdir); |
| size_t freelen = sizeof(curSubdir) - sublen - 1; |
| LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir)); |
| fret = FINDFIRST_DIR("*", &fInfo); |
| if (FINDFIRST_SUCCEEDED(fret)) { |
| do { |
| const char *curName = FIND_T_FILENAME(fInfo); |
| if ((curName[0] == '.') || (strcmp(curName, "CVS") == 0)) { |
| continue; |
| } |
| if (!FIND_T_IS_DIR(fInfo)) { |
| continue; |
| } |
| if (freelen > 0) { |
| CHDIR(curName); |
| strncat(curSubdir, "/", freelen); |
| strncat(curSubdir, curName, freelen - 1); |
| curSubdir[sizeof(curSubdir) - 1] = 0; |
| printf("processing subdirectory %s/..." NEWLINE, curSubdir); |
| filesProcessed += process_sub(data_file, struct_file); |
| CHDIR(".."); |
| curSubdir[sublen] = 0; |
| } else { |
| printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, curName); |
| } |
| } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo))); |
| } |
| } |
| |
| fret = FINDFIRST_FILE("*.*", &fInfo); |
| if (FINDFIRST_SUCCEEDED(fret)) { |
| /* at least one file in directory */ |
| do { |
| if (FIND_T_IS_FILE(fInfo)) { |
| const char *curName = FIND_T_FILENAME(fInfo); |
| printf("processing %s/%s..." NEWLINE, curSubdir, curName); |
| if (process_file(data_file, struct_file, curName) < 0) { |
| printf(NEWLINE "Error... aborting" NEWLINE); |
| return -1; |
| } |
| filesProcessed++; |
| } |
| } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo))); |
| } |
| return filesProcessed; |
| } |
| |
| u8_t* get_file_data(const char* filename, int* file_size, int can_be_compressed, int* is_compressed) |
| { |
| FILE *inFile; |
| size_t fsize = 0; |
| u8_t* buf; |
| size_t r; |
| int rs; |
| inFile = fopen(filename, "rb"); |
| if (inFile == NULL) { |
| printf("Failed to open file \"%s\"\n", filename); |
| exit(-1); |
| } |
| fseek(inFile, 0, SEEK_END); |
| rs = ftell(inFile); |
| if (rs < 0) { |
| printf("ftell failed with %d\n", errno); |
| exit(-1); |
| } |
| fsize = (size_t)rs; |
| fseek(inFile, 0, SEEK_SET); |
| buf = (u8_t*)malloc(fsize); |
| LWIP_ASSERT("buf != NULL", buf != NULL); |
| r = fread(buf, 1, fsize, inFile); |
| *file_size = fsize; |
| *is_compressed = 0; |
| #if MAKEFS_SUPPORT_DEFLATE |
| overallDataBytes += fsize; |
| if (deflateNonSsiFiles) { |
| if (can_be_compressed) { |
| if (fsize < OUT_BUF_SIZE) { |
| u8_t* ret_buf; |
| tdefl_status status; |
| size_t in_bytes = fsize; |
| size_t out_bytes = OUT_BUF_SIZE; |
| const void *next_in = buf; |
| void *next_out = s_outbuf; |
| /* 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). */ |
| mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); |
| if (!deflate_level) { |
| comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; |
| } |
| status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); |
| if (status != TDEFL_STATUS_OKAY) { |
| printf("tdefl_init() failed!\n"); |
| exit(-1); |
| } |
| memset(s_outbuf, 0, sizeof(s_outbuf)); |
| status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH); |
| if (status != TDEFL_STATUS_DONE) { |
| printf("deflate failed: %d\n", status); |
| exit(-1); |
| } |
| LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE); |
| if (out_bytes < fsize) { |
| ret_buf = (u8_t*)malloc(out_bytes); |
| LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL); |
| memcpy(ret_buf, s_outbuf, out_bytes); |
| { |
| /* sanity-check compression be inflating and comparing to the original */ |
| tinfl_status dec_status; |
| tinfl_decompressor inflator; |
| size_t dec_in_bytes = out_bytes; |
| size_t dec_out_bytes = OUT_BUF_SIZE; |
| next_out = s_checkbuf; |
| |
| tinfl_init(&inflator); |
| memset(s_checkbuf, 0, sizeof(s_checkbuf)); |
| dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0); |
| LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE); |
| LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes); |
| LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize)); |
| } |
| /* free original buffer, use compressed data + size */ |
| free(buf); |
| buf = ret_buf; |
| *file_size = out_bytes; |
| printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes*100.0)/fsize)); |
| deflatedBytesReduced += (size_t)(fsize - out_bytes); |
| *is_compressed = 1; |
| } else { |
| printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize)); |
| } |
| } else { |
| printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE); |
| } |
| } else { |
| printf(" - SSI file, cannot be compressed" NEWLINE); |
| } |
| } |
| #else |
| LWIP_UNUSED_ARG(can_be_compressed); |
| #endif |
| fclose(inFile); |
| return buf; |
| } |
| |
| void process_file_data(FILE* data_file, u8_t* file_data, size_t file_size) |
| { |
| size_t written, i, src_off=0; |
| |
| size_t off = 0; |
| for (i = 0; i < file_size; i++) { |
| LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5); |
| sprintf(&file_buffer_c[off], "0x%02.2x,", file_data[i]); |
| off += 5; |
| if ((++src_off % HEX_BYTES_PER_LINE) == 0) { |
| LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN); |
| memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN); |
| off += NEWLINE_LEN; |
| } |
| if (off + 20 >= sizeof(file_buffer_c)) { |
| written = fwrite(file_buffer_c, 1, off, data_file); |
| LWIP_ASSERT("written == off", written == off); |
| off = 0; |
| } |
| } |
| written = fwrite(file_buffer_c, 1, off, data_file); |
| LWIP_ASSERT("written == off", written == off); |
| } |
| |
| int 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) |
| { |
| int chunk_size = TCP_MSS; |
| int offset, src_offset; |
| size_t len; |
| int i = 0; |
| #if LWIP_TCP_TIMESTAMPS |
| /* when timestamps are used, usable space is 12 bytes less per segment */ |
| chunk_size -= 12; |
| #endif |
| |
| fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE); |
| fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname); |
| |
| if (hdr_len > 0) { |
| /* add checksum for HTTP header */ |
| fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len); |
| i++; |
| } |
| src_offset = 0; |
| for (offset = hdr_len; ; offset += len) { |
| unsigned short chksum; |
| void* data = (void*)&file_data[src_offset]; |
| len = LWIP_MIN(chunk_size, (int)file_size - src_offset); |
| if (len == 0) { |
| break; |
| } |
| chksum = ~inet_chksum(data, (u16_t)len); |
| /* add checksum for data */ |
| fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, offset, chksum, len); |
| i++; |
| } |
| fprintf(struct_file, "};" NEWLINE); |
| fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE); |
| return i; |
| } |
| |
| static int is_valid_char_for_c_var(char x) |
| { |
| if (((x >= 'A') && (x <= 'Z')) || |
| ((x >= 'a') && (x <= 'z')) || |
| ((x >= '0') && (x <= '9')) || |
| (x == '_')) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| static void fix_filename_for_c(char* qualifiedName, size_t max_len) |
| { |
| struct file_entry* f; |
| size_t len = strlen(qualifiedName); |
| char *new_name = (char*)malloc(len + 2); |
| int filename_ok; |
| int cnt = 0; |
| size_t i; |
| if (len + 3 == max_len) { |
| printf("File name too long: \"%s\"\n", qualifiedName); |
| exit(-1); |
| } |
| strcpy(new_name, qualifiedName); |
| for (i = 0; i < len; i++) { |
| if (!is_valid_char_for_c_var(new_name[i])) { |
| new_name[i] = '_'; |
| } |
| } |
| do { |
| filename_ok = 1; |
| for (f = first_file; f != NULL; f = f->next) { |
| if (!strcmp(f->filename_c, new_name)) { |
| filename_ok = 0; |
| cnt++; |
| /* try next unique file name */ |
| sprintf(&new_name[len], "%d", cnt); |
| break; |
| } |
| } |
| } while (!filename_ok && (cnt < 999)); |
| if (!filename_ok) { |
| printf("Failed to get unique file name: \"%s\"\n", qualifiedName); |
| exit(-1); |
| } |
| strcpy(qualifiedName, new_name); |
| free(new_name); |
| } |
| |
| static void register_filename(const char* qualifiedName) |
| { |
| struct file_entry* fe = (struct file_entry*)malloc(sizeof(struct file_entry)); |
| fe->filename_c = strdup(qualifiedName); |
| fe->next = NULL; |
| if (first_file == NULL) { |
| first_file = last_file = fe; |
| } else { |
| last_file->next = fe; |
| last_file = fe; |
| } |
| } |
| |
| int is_ssi_file(const char* filename) |
| { |
| size_t loop; |
| for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { |
| if (strstr(filename, g_pcSSIExtensions[loop])) { |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| int process_file(FILE *data_file, FILE *struct_file, const char *filename) |
| { |
| char varname[MAX_PATH_LEN]; |
| int i = 0; |
| char qualifiedName[MAX_PATH_LEN]; |
| int file_size; |
| u16_t http_hdr_chksum = 0; |
| u16_t http_hdr_len = 0; |
| int chksum_count = 0; |
| u8_t flags = 0; |
| const char* flags_str; |
| u8_t has_content_len; |
| u8_t* file_data; |
| int is_compressed = 0; |
| |
| /* create qualified name (@todo: prepend slash or not?) */ |
| sprintf(qualifiedName,"%s/%s", curSubdir, filename); |
| /* create C variable name */ |
| strcpy(varname, qualifiedName); |
| /* convert slashes & dots to underscores */ |
| fix_filename_for_c(varname, MAX_PATH_LEN); |
| register_filename(varname); |
| #if ALIGN_PAYLOAD |
| /* to force even alignment of array, type 1 */ |
| fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE); |
| fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++); |
| fprintf(data_file, "#endif" NEWLINE); |
| #endif /* ALIGN_PAYLOAD */ |
| fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname); |
| /* encode source file name (used by file system, not returned to browser) */ |
| fprintf(data_file, "/* %s (%d chars) */" NEWLINE, qualifiedName, strlen(qualifiedName)+1); |
| file_put_ascii(data_file, qualifiedName, strlen(qualifiedName)+1, &i); |
| #if ALIGN_PAYLOAD |
| /* pad to even number of bytes to assure payload is on aligned boundary */ |
| while(i % PAYLOAD_ALIGNMENT != 0) { |
| fprintf(data_file, "0x%02.2x,", 0); |
| i++; |
| } |
| #endif /* ALIGN_PAYLOAD */ |
| fprintf(data_file, NEWLINE); |
| |
| has_content_len = !is_ssi_file(filename); |
| file_data = get_file_data(filename, &file_size, includeHttpHeader && has_content_len, &is_compressed); |
| if (includeHttpHeader) { |
| file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed); |
| flags = FS_FILE_FLAGS_HEADER_INCLUDED; |
| if (has_content_len) { |
| flags |= FS_FILE_FLAGS_HEADER_PERSISTENT; |
| } |
| } |
| if (precalcChksum) { |
| chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size); |
| } |
| |
| /* build declaration of struct fsdata_file in temp file */ |
| fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname); |
| fprintf(struct_file, "file_%s," NEWLINE, lastFileVar); |
| fprintf(struct_file, "data_%s," NEWLINE, varname); |
| fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i); |
| fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i); |
| switch(flags) |
| { |
| case(FS_FILE_FLAGS_HEADER_INCLUDED): |
| flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED"; |
| break; |
| case(FS_FILE_FLAGS_HEADER_PERSISTENT): |
| flags_str = "FS_FILE_FLAGS_HEADER_PERSISTENT"; |
| break; |
| case(FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT): |
| flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT"; |
| break; |
| default: |
| flags_str = "0"; |
| break; |
| } |
| fprintf(struct_file, "%s," NEWLINE, flags_str); |
| if (precalcChksum) { |
| fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE); |
| fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname); |
| fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE); |
| } |
| fprintf(struct_file, "}};" NEWLINE NEWLINE); |
| strcpy(lastFileVar, varname); |
| |
| /* write actual file contents */ |
| i = 0; |
| fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size); |
| process_file_data(data_file, file_data, file_size); |
| fprintf(data_file, "};" NEWLINE NEWLINE); |
| free(file_data); |
| return 0; |
| } |
| |
| int 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) |
| { |
| int i = 0; |
| int response_type = HTTP_HDR_OK; |
| const char* file_type; |
| const char *cur_string; |
| size_t cur_len; |
| int written = 0; |
| size_t hdr_len = 0; |
| u16_t acc; |
| const char *file_ext; |
| int j; |
| u8_t provide_last_modified = includeLastModified; |
| |
| memset(hdr_buf, 0, sizeof(hdr_buf)); |
| |
| if (useHttp11) { |
| response_type = HTTP_HDR_OK_11; |
| } |
| |
| fprintf(data_file, NEWLINE "/* HTTP header */"); |
| if (strstr(filename, "404") == filename) { |
| response_type = HTTP_HDR_NOT_FOUND; |
| if (useHttp11) { |
| response_type = HTTP_HDR_NOT_FOUND_11; |
| } |
| } else if (strstr(filename, "400") == filename) { |
| response_type = HTTP_HDR_BAD_REQUEST; |
| if (useHttp11) { |
| response_type = HTTP_HDR_BAD_REQUEST_11; |
| } |
| } else if (strstr(filename, "501") == filename) { |
| response_type = HTTP_HDR_NOT_IMPL; |
| if (useHttp11) { |
| response_type = HTTP_HDR_NOT_IMPL_11; |
| } |
| } |
| cur_string = g_psHTTPHeaderStrings[response_type]; |
| cur_len = strlen(cur_string); |
| fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); |
| written += file_put_ascii(data_file, cur_string, cur_len, &i); |
| i = 0; |
| if (precalcChksum) { |
| memcpy(&hdr_buf[hdr_len], cur_string, cur_len); |
| hdr_len += cur_len; |
| } |
| |
| cur_string = serverID; |
| cur_len = strlen(cur_string); |
| fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); |
| written += file_put_ascii(data_file, cur_string, cur_len, &i); |
| i = 0; |
| if (precalcChksum) { |
| memcpy(&hdr_buf[hdr_len], cur_string, cur_len); |
| hdr_len += cur_len; |
| } |
| |
| file_ext = filename; |
| if (file_ext != NULL) { |
| while(strstr(file_ext, ".") != NULL) { |
| file_ext = strstr(file_ext, "."); |
| file_ext++; |
| } |
| } |
| if ((file_ext == NULL) || (*file_ext == 0)) { |
| printf("failed to get extension for file \"%s\", using default.\n", filename); |
| file_type = HTTP_HDR_DEFAULT_TYPE; |
| } else { |
| file_type = NULL; |
| for (j = 0; j < NUM_HTTP_HEADERS; j++) { |
| if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) { |
| file_type = g_psHTTPHeaders[j].content_type; |
| break; |
| } |
| } |
| if (file_type == NULL) { |
| printf("failed to get file type for extension \"%s\", using default.\n", file_ext); |
| file_type = HTTP_HDR_DEFAULT_TYPE; |
| } |
| } |
| |
| /* Content-Length is used for persistent connections in HTTP/1.1 but also for |
| download progress in older versions |
| @todo: just use a big-enough buffer and let the HTTPD send spaces? */ |
| if (provide_content_len) { |
| char intbuf[MAX_PATH_LEN]; |
| int content_len = file_size; |
| memset(intbuf, 0, sizeof(intbuf)); |
| cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH]; |
| cur_len = strlen(cur_string); |
| fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%d+ bytes) */" NEWLINE, cur_string, content_len, cur_len+2); |
| written += file_put_ascii(data_file, cur_string, cur_len, &i); |
| if (precalcChksum) { |
| memcpy(&hdr_buf[hdr_len], cur_string, cur_len); |
| hdr_len += cur_len; |
| } |
| |
| _itoa(content_len, intbuf, 10); |
| strcat(intbuf, "\r\n"); |
| cur_len = strlen(intbuf); |
| written += file_put_ascii(data_file, intbuf, cur_len, &i); |
| i = 0; |
| if (precalcChksum) { |
| memcpy(&hdr_buf[hdr_len], intbuf, cur_len); |
| hdr_len += cur_len; |
| } |
| } |
| if (provide_last_modified) { |
| char modbuf[256]; |
| struct stat stat_data; |
| struct tm* t; |
| memset(modbuf, 0, sizeof(modbuf)); |
| memset(&stat_data, 0, sizeof(stat_data)); |
| cur_string = modbuf; |
| strcpy(modbuf, "Last-Modified: "); |
| if (stat(filename, &stat_data) != 0) { |
| printf("stat(%s) failed with error %d\n", filename, errno); |
| exit(-1); |
| } |
| t = gmtime(&stat_data.st_mtime); |
| if (t == NULL) { |
| printf("gmtime() failed with error %d\n", errno); |
| exit(-1); |
| } |
| strftime(&modbuf[15], sizeof(modbuf)-15, "%a, %d %b %Y %H:%M:%S GMT", t); |
| cur_len = strlen(cur_string); |
| fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%d+ bytes) */" NEWLINE, cur_string, cur_len+2); |
| written += file_put_ascii(data_file, cur_string, cur_len, &i); |
| if (precalcChksum) { |
| memcpy(&hdr_buf[hdr_len], cur_string, cur_len); |
| hdr_len += cur_len; |
| } |
| |
| modbuf[0] = 0; |
| strcat(modbuf, "\r\n"); |
| cur_len = strlen(modbuf); |
| written += file_put_ascii(data_file, modbuf, cur_len, &i); |
| i = 0; |
| if (precalcChksum) { |
| memcpy(&hdr_buf[hdr_len], modbuf, cur_len); |
| hdr_len += cur_len; |
| } |
| } |
| |
| /* HTTP/1.1 implements persistent connections */ |
| if (useHttp11) { |
| if (provide_content_len) { |
| cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE]; |
| } else { |
| /* no Content-Length available, so a persistent connection is no possible |
| because the client does not know the data length */ |
| cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE]; |
| } |
| cur_len = strlen(cur_string); |
| fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); |
| written += file_put_ascii(data_file, cur_string, cur_len, &i); |
| i = 0; |
| if (precalcChksum) { |
| memcpy(&hdr_buf[hdr_len], cur_string, cur_len); |
| hdr_len += cur_len; |
| } |
| } |
| |
| #if MAKEFS_SUPPORT_DEFLATE |
| if (is_compressed) { |
| /* tell the client about the deflate encoding */ |
| LWIP_ASSERT("error", deflateNonSsiFiles); |
| cur_string = "Content-Encoding: deflate\r\n"; |
| cur_len = strlen(cur_string); |
| fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); |
| written += file_put_ascii(data_file, cur_string, cur_len, &i); |
| i = 0; |
| } |
| #else |
| LWIP_UNUSED_ARG(is_compressed); |
| #endif |
| |
| /* write content-type, ATTENTION: this includes the double-CRLF! */ |
| cur_string = file_type; |
| cur_len = strlen(cur_string); |
| fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len); |
| written += file_put_ascii(data_file, cur_string, cur_len, &i); |
| i = 0; |
| |
| /* ATTENTION: headers are done now (double-CRLF has been written!) */ |
| |
| if (precalcChksum) { |
| memcpy(&hdr_buf[hdr_len], cur_string, cur_len); |
| hdr_len += cur_len; |
| |
| LWIP_ASSERT("hdr_len <= 0xffff", hdr_len <= 0xffff); |
| LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len); |
| acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len); |
| *http_hdr_len = (u16_t)hdr_len; |
| *http_hdr_chksum = acc; |
| } |
| |
| return written; |
| } |
| |
| int file_put_ascii(FILE *file, const char* ascii_string, int len, int *i) |
| { |
| int x; |
| for (x = 0; x < len; x++) { |
| unsigned char cur = ascii_string[x]; |
| fprintf(file, "0x%02.2x,", cur); |
| if ((++(*i) % HEX_BYTES_PER_LINE) == 0) { |
| fprintf(file, NEWLINE); |
| } |
| } |
| return len; |
| } |
| |
| int s_put_ascii(char *buf, const char *ascii_string, int len, int *i) |
| { |
| int x; |
| int idx = 0; |
| for (x = 0; x < len; x++) { |
| unsigned char cur = ascii_string[x]; |
| sprintf(&buf[idx], "0x%02.2x,", cur); |
| idx += 5; |
| if ((++(*i) % HEX_BYTES_PER_LINE) == 0) { |
| sprintf(&buf[idx], NEWLINE); |
| idx += NEWLINE_LEN; |
| } |
| } |
| return len; |
| } |