558 lines
16 KiB
C
558 lines
16 KiB
C
/*
|
|
* OSCam WebIf pages generator
|
|
* Copyright (C) 2013 Unix Solutions Ltd.
|
|
*
|
|
* Authors: Georgi Chorbadzhiyski (gf@unixsol.org)
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <libgen.h>
|
|
|
|
#include "../config.h"
|
|
#ifdef WITH_COMPRESS_WEBIF
|
|
#include "../minilzo/minilzo.h"
|
|
#define USE_COMPRESSION 1
|
|
#endif
|
|
|
|
#define MAX_TEMPLATES 512
|
|
static char *index_filename = "pages_index.txt";
|
|
static char *defined_file = "is_defined.txt";
|
|
static char *output_pages_c = "pages.c";
|
|
static char *output_pages_h = "pages.h";
|
|
|
|
struct template
|
|
{
|
|
char ident[64];
|
|
char file[128];
|
|
char deps[1024];
|
|
uint32_t data_len;
|
|
enum { TXT, BIN } type;
|
|
uint8_t mime_type;
|
|
#ifdef USE_COMPRESSION
|
|
uint8_t *buf;
|
|
size_t buf_len;
|
|
uint32_t ident_ofs;
|
|
uint32_t data_ofs;
|
|
uint32_t deps_ofs;
|
|
#endif
|
|
};
|
|
|
|
struct templates
|
|
{
|
|
unsigned int num;
|
|
struct template data[MAX_TEMPLATES];
|
|
};
|
|
|
|
static struct templates templates;
|
|
static FILE *output_file;
|
|
|
|
__attribute__ ((noreturn)) static void die(const char *s, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, s);
|
|
fprintf(stderr, "ERROR: ");
|
|
vfprintf(stderr, s, args);
|
|
if(s[strlen(s) - 1] != '\n')
|
|
{ fprintf(stderr, "\n"); }
|
|
va_end(args);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static FILE *xfopen(char *filename, char *mode)
|
|
{
|
|
FILE *fh = fopen(filename, mode);
|
|
if(!fh)
|
|
{ die("fopen(%s, %s): %s\n", filename, mode, strerror(errno)); }
|
|
return fh;
|
|
}
|
|
|
|
static void readfile(const char *filename, uint8_t **data, size_t *data_len)
|
|
{
|
|
struct stat sb;
|
|
if(stat(filename, &sb) != 0)
|
|
{ die("stat(%s): %s\n", filename, strerror(errno)); }
|
|
int fd = open(filename, O_RDONLY);
|
|
if(fd < 0)
|
|
{ die("open(%s): %s\n", filename, strerror(errno)); }
|
|
*data_len = sb.st_size;
|
|
*data = malloc(*data_len);
|
|
if(!*data)
|
|
{ die("%s(%s): can't alloc %zd bytes\n", __func__, filename, *data_len); }
|
|
if(read(fd, *data, *data_len) < 0)
|
|
{ die("read(%d, %zd): %s\n", fd, *data_len, strerror(errno)); }
|
|
close(fd);
|
|
}
|
|
|
|
static bool is_text(char *filename)
|
|
{
|
|
char *ext = strchr(basename(filename), '.');
|
|
if(ext)
|
|
{
|
|
ext++;
|
|
if(strcmp(ext, "html") == 0) { return true; }
|
|
else if(strcmp(ext, "json") == 0) { return true; }
|
|
else if(strcmp(ext, "xml") == 0) { return true; }
|
|
else if(strcmp(ext, "css") == 0) { return true; }
|
|
else if(strcmp(ext, "svg") == 0) { return true; }
|
|
else if(strcmp(ext, "js") == 0) { return true; }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static uint8_t mime_type_from_filename(char *filename)
|
|
{
|
|
char *ext = strchr(basename(filename), '.');
|
|
if(ext)
|
|
{
|
|
ext++;
|
|
// See "enum template_types" bellow
|
|
if(strcmp(ext, "png") == 0) { return 1; }
|
|
else if(strcmp(ext, "gif") == 0) { return 2; }
|
|
else if(strcmp(ext, "ico") == 0) { return 3; }
|
|
else if(strcmp(ext, "jpg") == 0) { return 4; }
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void parse_index_file(char *filename)
|
|
{
|
|
uint8_t *is_defined = NULL;
|
|
size_t is_defined_len = 0;
|
|
if(access(defined_file, R_OK) != -1)
|
|
readfile(defined_file, &is_defined, &is_defined_len);
|
|
FILE *f = xfopen(filename, "r");
|
|
int max_fields = 3;
|
|
char line[1024];
|
|
while(fgets(line, sizeof(line) - 1, f))
|
|
{
|
|
int field = 0, pos = 0;
|
|
char *ident = "", *file = "", *deps = "";
|
|
int len = strlen(line);
|
|
if(!len || !isalnum((unsigned char)line[0])) // Skip comments and junk
|
|
{ continue; }
|
|
// Parse text[ ]text[ ]text
|
|
do
|
|
{
|
|
while(line[pos] == ' ' || line[pos] == '\t') // Skip white space
|
|
{ pos++; }
|
|
if(line[pos] == '\n')
|
|
{ break; }
|
|
int start = pos;
|
|
while(line[pos] != ' ' && line[pos] != '\t' && line[pos] != '\n') // Data
|
|
{ pos++; }
|
|
switch(++field)
|
|
{
|
|
case 1:
|
|
ident = line + start;
|
|
line[pos] = '\0';
|
|
break;
|
|
case 2:
|
|
file = line + start;
|
|
line[pos] = '\0';
|
|
break;
|
|
case 3:
|
|
deps = line + start;
|
|
line[pos] = '\0';
|
|
break;
|
|
}
|
|
if(field >= max_fields)
|
|
{ break; }
|
|
pos++;
|
|
}
|
|
while(pos < len);
|
|
|
|
if(deps && strlen(deps) && is_defined){
|
|
if(strstr(deps, ",")){
|
|
int i,def_found=0;
|
|
char *ptr, *saveptr1 = NULL;
|
|
char *deps_sep = strdup(deps);
|
|
for(i = 0, ptr = strtok_r(deps_sep, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1), i++)
|
|
{
|
|
if(strstr((char *)is_defined, ptr))
|
|
{ def_found = 1; }
|
|
}
|
|
free(deps_sep);
|
|
if(!def_found)
|
|
{ continue; }
|
|
}
|
|
else if( !strstr((char *)is_defined, deps))
|
|
{ continue; }
|
|
}
|
|
if(!strlen(ident) || !strlen(file))
|
|
{ continue; }
|
|
|
|
#define template_set(var) \
|
|
do { \
|
|
len = strlen(var); \
|
|
pos = sizeof(templates.data[0].var); \
|
|
if (len > pos - 1) \
|
|
die("%s=%s length exceeds maxlen (%d > %d)\n", #var, var, len, pos - 1); \
|
|
snprintf(templates.data[templates.num].var, pos, "%s", var); \
|
|
} while (0)
|
|
template_set(ident);
|
|
template_set(file);
|
|
template_set(deps);
|
|
|
|
templates.data[templates.num].type = is_text(file) ? TXT : BIN;
|
|
templates.data[templates.num].mime_type = mime_type_from_filename(file);
|
|
templates.num++;
|
|
if(templates.num == MAX_TEMPLATES - 1)
|
|
{
|
|
die("Too many templates in %s. Maximum is %d. Increase MAX_TEMPLATES!\n",
|
|
filename, MAX_TEMPLATES);
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
static void print_template(int tpl_idx)
|
|
{
|
|
static bool ifdef_open = 0;
|
|
char *prev_deps = "";
|
|
char *next_deps = "";
|
|
char *ident = templates.data[tpl_idx].ident;
|
|
char *deps = templates.data[tpl_idx].deps;
|
|
if(tpl_idx > 0)
|
|
{ prev_deps = templates.data[tpl_idx - 1].deps; }
|
|
if(tpl_idx + 1 < templates.num)
|
|
{ next_deps = templates.data[tpl_idx + 1].deps; }
|
|
int deps_len = strlen(deps);
|
|
|
|
// Put guards
|
|
if(deps_len && strcmp(deps, prev_deps) != 0)
|
|
{
|
|
int i, commas = 0;
|
|
for(i = 0; i < deps_len; i++)
|
|
{
|
|
if(deps[i] == ',')
|
|
{ commas++; }
|
|
}
|
|
if(commas == 0)
|
|
{
|
|
fprintf(output_file, "#ifdef %s\n", deps);
|
|
}
|
|
else
|
|
{
|
|
char *ptr, *saveptr1 = NULL;
|
|
char *split_deps = strdup(deps);
|
|
for(i = 0, ptr = strtok_r(split_deps, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1), i++)
|
|
{
|
|
if(i == 0)
|
|
{ fprintf(output_file, "#if defined(%s)", ptr); }
|
|
else
|
|
{ fprintf(output_file, " || defined(%s)", ptr); }
|
|
}
|
|
fprintf(output_file, "\n");
|
|
free(split_deps);
|
|
}
|
|
ifdef_open = 1;
|
|
}
|
|
|
|
#ifdef USE_COMPRESSION
|
|
fprintf(output_file, "\t{ .tpl_name_ofs=%5u, .tpl_data_ofs=%5u, .tpl_deps_ofs=%5u, .tpl_data_len=%5u, .tpl_type=%u }, /* %s %s %s */\n",
|
|
templates.data[tpl_idx].ident_ofs,
|
|
templates.data[tpl_idx].data_ofs,
|
|
templates.data[tpl_idx].deps_ofs,
|
|
templates.data[tpl_idx].data_len,
|
|
templates.data[tpl_idx].mime_type,
|
|
ident,
|
|
templates.data[tpl_idx].file,
|
|
deps
|
|
);
|
|
#else
|
|
fprintf(output_file, "\t{ .tpl_name=\"%s\", .tpl_data=TPL%s, .tpl_deps=\"%s\", .tpl_data_len=%u, .tpl_type=%u },\n",
|
|
ident, ident, deps, templates.data[tpl_idx].data_len, templates.data[tpl_idx].mime_type
|
|
);
|
|
#endif
|
|
|
|
if(ifdef_open && strcmp(deps, next_deps) != 0)
|
|
{
|
|
fprintf(output_file, "#endif\n");
|
|
ifdef_open = 0;
|
|
}
|
|
}
|
|
|
|
#ifdef USE_COMPRESSION
|
|
static void dump_cbinary(char *var_name, uint8_t *buf, size_t buf_len, size_t obuf_len)
|
|
{
|
|
fprintf(output_file, "static const char *%s = \"", var_name);
|
|
int i;
|
|
for(i = 0; i < buf_len; i++)
|
|
{
|
|
fprintf(output_file, "\\x%02x", buf[i]);
|
|
}
|
|
fprintf(output_file, "\";\n");
|
|
fprintf(output_file, "static const size_t %s_len = %zu;\n" , var_name, buf_len);
|
|
fprintf(output_file, "static const size_t %s_olen = %zu;\n\n", var_name, obuf_len);
|
|
}
|
|
|
|
#define HEAP_ALLOC(var, size) \
|
|
lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
|
|
|
|
static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);
|
|
#else
|
|
|
|
static void dump_text(char *ident, uint8_t *buf, size_t buf_len)
|
|
{
|
|
int i;
|
|
fprintf(output_file, "#define TPL%s \\\n\"", ident);
|
|
for(i = 0; i < buf_len; i++)
|
|
{
|
|
switch(buf[i])
|
|
{
|
|
case '\n':
|
|
if(i < buf_len - 1)
|
|
{ fprintf(output_file, "\\n\\\n"); }
|
|
else
|
|
{ fprintf(output_file, "\\n"); }
|
|
break;
|
|
case '\\':
|
|
fprintf(output_file, "\\\\");
|
|
break;
|
|
case '"' :
|
|
fprintf(output_file, "\\\"");
|
|
break;
|
|
default :
|
|
fprintf(output_file, "%c", buf[i]);
|
|
break;
|
|
}
|
|
}
|
|
fprintf(output_file, "\"\n\n");
|
|
}
|
|
|
|
static void dump_binary(char *ident, uint8_t *buf, size_t buf_len)
|
|
{
|
|
fprintf(output_file, "#define TPL%s \\\n\"", ident);
|
|
int i;
|
|
for(i = 0; i < buf_len; i++)
|
|
{
|
|
fprintf(output_file, "\\x%02x", buf[i]);
|
|
}
|
|
fprintf(output_file, "\"\n\n");
|
|
}
|
|
#endif
|
|
|
|
int main(void)
|
|
{
|
|
int i;
|
|
|
|
parse_index_file(index_filename);
|
|
|
|
output_file = xfopen(output_pages_h, "w");
|
|
fprintf(output_file, "#ifndef WEBIF_PAGES_H_\n");
|
|
fprintf(output_file, "#define WEBIF_PAGES_H_\n");
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "enum template_types {\n");
|
|
fprintf(output_file, " TEMPLATE_TYPE_TEXT = 0,\n");
|
|
fprintf(output_file, " TEMPLATE_TYPE_PNG = 1,\n");
|
|
fprintf(output_file, " TEMPLATE_TYPE_GIF = 2,\n");
|
|
fprintf(output_file, " TEMPLATE_TYPE_ICO = 3,\n");
|
|
fprintf(output_file, " TEMPLATE_TYPE_JPG = 4,\n");
|
|
fprintf(output_file, "};\n");
|
|
fprintf(output_file, "\n");
|
|
#ifdef USE_COMPRESSION
|
|
fprintf(output_file, "#define COMPRESSED_TEMPLATES 1\n\n");
|
|
fprintf(output_file, "struct template {\n");
|
|
fprintf(output_file, " uint32_t tpl_name_ofs;\n");
|
|
fprintf(output_file, " uint32_t tpl_data_ofs;\n");
|
|
fprintf(output_file, " uint32_t tpl_deps_ofs;\n");
|
|
fprintf(output_file, " uint32_t tpl_data_len;\n");
|
|
fprintf(output_file, " uint8_t tpl_type;\n");
|
|
fprintf(output_file, "};\n");
|
|
#else
|
|
fprintf(output_file, "struct template {\n");
|
|
fprintf(output_file, " char *tpl_name;\n");
|
|
fprintf(output_file, " char *tpl_data;\n");
|
|
fprintf(output_file, " char *tpl_deps;\n");
|
|
fprintf(output_file, " uint32_t tpl_data_len;\n");
|
|
fprintf(output_file, " uint8_t tpl_type;\n");
|
|
fprintf(output_file, "};\n");
|
|
#endif
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "int32_t templates_count(void);\n");
|
|
fprintf(output_file, "bool template_is_image(enum template_types tpl_type);\n");
|
|
fprintf(output_file, "const char *template_get_mimetype(enum template_types tpl_type);\n");
|
|
fprintf(output_file, "const struct template *templates_get(void);\n");
|
|
#ifdef USE_COMPRESSION
|
|
fprintf(output_file, "void templates_get_data(const char **data, size_t *data_len, size_t *odata_len);\n");
|
|
#endif
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "#endif\n");
|
|
fclose(output_file);
|
|
|
|
output_file = xfopen(output_pages_c, "w");
|
|
fprintf(output_file, "#include \"../globals.h\"\n");
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "#ifdef WEBIF\n");
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "#include \"pages.h\"\n");
|
|
fprintf(output_file, "\n");
|
|
|
|
#ifdef USE_COMPRESSION
|
|
// Calculate positions at which the values would be storred
|
|
uint32_t cur_pos = 0;
|
|
#define align_up(val, align) (val += (align - val % align))
|
|
for(i = 0; i < templates.num; i++)
|
|
{
|
|
struct template *t = &templates.data[i];
|
|
readfile(t->file, &t->buf, &t->buf_len);
|
|
t->data_len = t->buf_len;
|
|
// +1 to leave space for \0
|
|
t->ident_ofs = cur_pos;
|
|
cur_pos += strlen(t->ident) + 1;
|
|
align_up(cur_pos, sizeof(void *));
|
|
t->data_ofs = cur_pos;
|
|
cur_pos += t->data_len + 1;
|
|
align_up(cur_pos, sizeof(void *));
|
|
t->deps_ofs = cur_pos;
|
|
cur_pos += strlen(t->deps) + 1;
|
|
align_up(cur_pos, sizeof(void *));
|
|
}
|
|
|
|
// Allocate template data and populate it
|
|
#define data_len cur_pos
|
|
if(!data_len)
|
|
die("No defined templates");
|
|
uint8_t *data = calloc(1, data_len);
|
|
if(!data)
|
|
{ die("Can't alloc %u bytes", data_len); }
|
|
for(i = 0; i < templates.num; i++)
|
|
{
|
|
struct template *t = &templates.data[i];
|
|
memcpy(data + t->ident_ofs, t->ident, strlen(t->ident));
|
|
memcpy(data + t->data_ofs , t->buf , t->buf_len);
|
|
free(t->buf);
|
|
if(!t->deps[0]) // No need to copy empty deps
|
|
{ continue; }
|
|
memcpy(data + t->deps_ofs, t->deps, strlen(t->deps));
|
|
}
|
|
FILE *bin = xfopen("pages.bin", "w");
|
|
fwrite(data, data_len, 1, bin);
|
|
fclose(bin);
|
|
|
|
// Compress template data
|
|
lzo_uint in_len = data_len;
|
|
lzo_uint out_len = data_len + data_len / 16 + 64 + 3; // Leave enough space in the output
|
|
uint8_t *out = malloc(out_len);
|
|
if(!out)
|
|
{ die("Can't alloc %zu bytes", out_len); }
|
|
|
|
if(lzo_init() != LZO_E_OK)
|
|
{
|
|
fprintf(stderr, "internal error - lzo_init() failed !!!\n");
|
|
fprintf(stderr, "(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable '-DLZO_DEBUG' for diagnostics)\n");
|
|
free(out);
|
|
free(data);
|
|
return 3;
|
|
}
|
|
|
|
int r = lzo1x_1_compress(data, in_len, out, &out_len, wrkmem);
|
|
if(r == LZO_E_OK)
|
|
{
|
|
int64_t saved_bytes = (int64_t)in_len - (int64_t)out_len;
|
|
printf("GEN\tCompressed %lu template bytes into %lu bytes. %" PRId64 " saved bytes (%.2f%%).\n",
|
|
(unsigned long)in_len, (unsigned long)out_len,
|
|
saved_bytes, 100 - ((float)out_len / in_len) * 100);
|
|
}
|
|
else
|
|
{
|
|
/* this should NEVER happen */
|
|
printf("internal error - compression failed: %d\n", r);
|
|
free(out);
|
|
free(data);
|
|
return 2;
|
|
}
|
|
|
|
bin = xfopen("pages.bin.compressed", "w");
|
|
fwrite(out, out_len, 1, bin);
|
|
fclose(bin);
|
|
|
|
dump_cbinary("templates_data", out, out_len, data_len);
|
|
free(out);
|
|
free(data);
|
|
#else
|
|
for(i = 0; i < templates.num; i++)
|
|
{
|
|
uint8_t *buf;
|
|
size_t buf_len;
|
|
readfile(templates.data[i].file, &buf, &buf_len);
|
|
templates.data[i].data_len = buf_len;
|
|
switch(templates.data[i].type)
|
|
{
|
|
case TXT:
|
|
dump_text(templates.data[i].ident, buf, buf_len);
|
|
break;
|
|
case BIN:
|
|
dump_binary(templates.data[i].ident, buf, buf_len);
|
|
break;
|
|
}
|
|
free(buf);
|
|
}
|
|
#endif
|
|
|
|
fprintf(output_file, "static const struct template templates[] = {\n");
|
|
for(i = 0; i < templates.num; i++)
|
|
{
|
|
print_template(i);
|
|
}
|
|
fprintf(output_file, "};\n");
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "int32_t templates_count(void) { return sizeof(templates) / sizeof(struct template); }\n");
|
|
fprintf(output_file, "const struct template *templates_get(void) { return templates; }\n");
|
|
#ifdef USE_COMPRESSION
|
|
fprintf(output_file, "void templates_get_data(const char **data, size_t *data_len, size_t *data_olen) { *data = templates_data; *data_len = templates_data_len; *data_olen = templates_data_olen; }\n");
|
|
#endif
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "bool template_is_image(enum template_types tpl_type) {\n");
|
|
fprintf(output_file, " switch (tpl_type) {\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_PNG:\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_GIF:\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_ICO:\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_JPG:\n");
|
|
fprintf(output_file, " return true;\n");
|
|
fprintf(output_file, " default:\n");
|
|
fprintf(output_file, " return false;\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " return false;\n");
|
|
fprintf(output_file, "}\n");
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "const char *template_get_mimetype(enum template_types tpl_type) {\n");
|
|
fprintf(output_file, " switch (tpl_type) {\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_TEXT: return \"text/plain\";\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_PNG : return \"image/png\";\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_GIF : return \"image/gif\";\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_ICO : return \"image/x-icon\";\n");
|
|
fprintf(output_file, " case TEMPLATE_TYPE_JPG : return \"image/jpg\";\n");
|
|
fprintf(output_file, " }\n");
|
|
fprintf(output_file, " return \"\";\n");
|
|
fprintf(output_file, "}\n");
|
|
fprintf(output_file, "\n");
|
|
fprintf(output_file, "#endif\n");
|
|
fclose(output_file);
|
|
|
|
return 0;
|
|
}
|