Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
63e006371This file is pinned to the selected release. Download the original to open it in your design tool.
/**
* 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;
}