/* * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }