source: trunk/webif/pages_gen.c@ 8434

Last change on this file since 8434 was 8434, checked in by gf, 9 years ago

webif: Create and use private templates array.

This allows storing of additional information along with templates
data (which is const).

For now we store name hash but in the future the data that comes from
struct templates (in webif/pages.c) may need to be preprocessed
(uncompressed for example) and the abstraction in this commit makes
it easier to do such changes.

File size: 10.4 KB
Line 
1/*
2 * OSCam WebIf pages generator
3 * Copyright (C) 2013 Unix Solutions Ltd.
4 *
5 * Authors: Georgi Chorbadzhiyski (gf@unixsol.org)
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20#include <ctype.h>
21#include <stdio.h>
22#include <stdarg.h>
23#include <stdlib.h>
24#include <stdbool.h>
25#include <string.h>
26#include <errno.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <inttypes.h>
32#include <libgen.h>
33
34#define MAX_TEMPLATES 256
35static char *index_filename = "pages_index.txt";
36static char *output_pages_c = "pages.c";
37static char *output_pages_h = "pages.h";
38
39struct templates {
40 unsigned int num;
41 struct {
42 char ident[64];
43 char file[128];
44 char deps[256];
45 uint32_t data_len;
46 enum { TXT, BIN } type;
47 } data[MAX_TEMPLATES];
48};
49
50static struct templates templates;
51static FILE *output_file;
52
53static void die(const char * s, ...) {
54 va_list args;
55 va_start(args, s);
56 fprintf(stderr, "ERROR: ");
57 vfprintf(stderr, s, args);
58 if (s[strlen(s) - 1] != '\n')
59 fprintf(stderr, "\n");
60 va_end(args);
61 exit(EXIT_FAILURE);
62}
63
64static FILE *xfopen(char *filename, char *mode) {
65 FILE *fh = fopen(filename, mode);
66 if (!fh)
67 die("fopen(%s, %s): %s\n", filename, mode, strerror(errno));
68 return fh;
69}
70
71static void readfile(const char *filename, uint8_t **data, size_t *data_len) {
72 struct stat sb;
73 if (stat(filename, &sb) != 0)
74 die("stat(%s): %s\n", filename, strerror(errno));
75 int fd = open(filename, O_RDONLY);
76 if (fd < 0)
77 die("open(%s): %s\n", filename, strerror(errno));
78 *data_len = sb.st_size;
79 *data = malloc(*data_len);
80 if (!*data)
81 die("%s(%s): can't alloc %zd bytes\n", __func__, filename, *data_len);
82 if (read(fd, *data, *data_len) < 0)
83 die("read(%d, %zd): %s\n", fd, *data_len, strerror(errno));
84}
85
86static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
87static const char Pad64 = '=';
88
89int b64_encode(const uint8_t *src, size_t srclength, uint8_t *target, size_t targsize)
90{
91 size_t datalength = 0;
92 uint8_t input[3] = {0,0,0};
93 uint8_t output[4] = {0,0,0,0};
94 unsigned int i;
95
96 while (2 < srclength) {
97 input[0] = *src++;
98 input[1] = *src++;
99 input[2] = *src++;
100 srclength -= 3;
101 output[0] = input[0] >> 2;
102 output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
103 output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
104 output[3] = input[2] & 0x3f;
105 if (datalength + 4 > targsize)
106 return -1;
107 target[datalength++] = Base64[output[0]];
108 target[datalength++] = Base64[output[1]];
109 target[datalength++] = Base64[output[2]];
110 target[datalength++] = Base64[output[3]];
111 }
112
113 /* Now we worry about padding. */
114 if (0 != srclength) {
115 /* Get what's left. */
116 input[0] = input[1] = input[2] = '\0';
117 for (i = 0; i < srclength; i++)
118 input[i] = *src++;
119
120 output[0] = input[0] >> 2;
121 output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
122 output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
123
124 if (datalength + 4 > targsize)
125 return (-1);
126 target[datalength++] = Base64[output[0]];
127 target[datalength++] = Base64[output[1]];
128 if (srclength == 1)
129 target[datalength++] = Pad64;
130 else
131 target[datalength++] = Base64[output[2]];
132 target[datalength++] = Pad64;
133 }
134 if (datalength >= targsize)
135 return -1;
136 target[datalength] = '\0'; /* Returned value doesn't count \0. */
137 return datalength;
138}
139
140static bool is_text(char *filename) {
141 char *ext = strchr(basename(filename), '.');
142 if (ext) {
143 ext++;
144 if (strcmp(ext, "html") == 0) return true;
145 else if (strcmp(ext, "json") == 0) return true;
146 else if (strcmp(ext, "xml") == 0) return true;
147 else if (strcmp(ext, "css") == 0) return true;
148 else if (strcmp(ext, "svg") == 0) return true;
149 else if (strcmp(ext, "js") == 0) return true;
150 }
151 return false;
152}
153
154static void parse_index_file(char *filename) {
155 FILE *f = xfopen(filename, "r");
156 int max_fields = 3;
157 char line[1024];
158 while (fgets(line, sizeof(line) - 1, f)) {
159 int field = 0, pos = 0;
160 char *ident = "", *file = "", *deps = "";
161 int len = strlen(line);
162 if (!len || !isalnum(line[0])) // Skip comments and junk
163 continue;
164 // Parse text[ ]text[ ]text
165 do {
166 while (line[pos] == ' ' || line[pos] == '\t') // Skip white space
167 pos++;
168 if (line[pos] == '\n')
169 break;
170 int start = pos;
171 while (line[pos] != ' ' && line[pos] != '\t' && line[pos] != '\n') // Data
172 pos++;
173 switch (++field) {
174 case 1: ident = line + start; line[pos] = '\0'; break;
175 case 2: file = line + start; line[pos] = '\0'; break;
176 case 3: deps = line + start; line[pos] = '\0'; break;
177 }
178 if (field >= max_fields)
179 break;
180 pos++;
181 } while (pos < len);
182 if (!strlen(ident) || !strlen(file))
183 continue;
184
185#define template_set(var) \
186 do { \
187 len = strlen(var); \
188 pos = sizeof(templates.data[0].var); \
189 if (len > pos - 1) \
190 die("%s=%s length exceeds maxlen (%d > %d)\n", #var, var, len, pos - 1); \
191 snprintf(templates.data[templates.num].var, pos, "%s", var); \
192 } while (0)
193 template_set(ident);
194 template_set(file);
195 template_set(deps);
196
197 templates.data[templates.num].type = is_text(file) ? TXT : BIN;
198 templates.num++;
199 if (templates.num == MAX_TEMPLATES - 1) {
200 die("Too many templates in %s. Maximum is %d. Increase MAX_TEMPLATES!\n",
201 filename, MAX_TEMPLATES);
202 }
203 }
204 fclose(f);
205}
206
207static void print_template(int tpl_idx) {
208 static bool ifdef_open = 0;
209 char *prev_deps = "";
210 char *next_deps = "";
211 char *ident = templates.data[tpl_idx].ident;
212 char *deps = templates.data[tpl_idx].deps;
213 if (tpl_idx > 0)
214 prev_deps = templates.data[tpl_idx - 1].deps;
215 if (tpl_idx + 1 < templates.num)
216 next_deps = templates.data[tpl_idx + 1].deps;
217 int deps_len = strlen(deps);
218
219 // Put guards
220 if (deps_len && strcmp(deps, prev_deps) != 0) {
221 int i, commas = 0;
222 for (i = 0; i < deps_len; i++) {
223 if (deps[i] == ',')
224 commas++;
225 }
226 if (commas == 0) {
227 fprintf(output_file, "#ifdef %s\n", deps);
228 } else {
229 char *ptr, *saveptr1 = NULL;
230 char *split_deps = strdup(deps);
231 for (i = 0, ptr = strtok_r(split_deps, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1), i++) {
232 if (i == 0)
233 fprintf(output_file, "#if defined(%s)", ptr);
234 else
235 fprintf(output_file, " || defined(%s)", ptr);
236 }
237 fprintf(output_file, "\n");
238 free(split_deps);
239 }
240 ifdef_open = 1;
241 }
242
243 fprintf(output_file, "\t{ .tpl_name=\"%s\", .tpl_data=%s%s, .tpl_deps=\"%s\", .tpl_data_len=%u },\n",
244 ident,
245 templates.data[tpl_idx].type == TXT ? "TPL" : "", ident,
246 deps,
247 templates.data[tpl_idx].data_len
248 );
249
250 if (ifdef_open && strcmp(deps, next_deps) != 0) {
251 fprintf(output_file, "#endif\n");
252 ifdef_open = 0;
253 }
254}
255
256static uint32_t dump_text(char *ident, uint8_t *buf, size_t buf_len) {
257 int i;
258 fprintf(output_file, "#define TPL%s \\\n\"", ident);
259 for (i = 0; i < buf_len; i++) {
260 switch (buf[i]) {
261 case '\n':
262 if (i < buf_len - 1)
263 fprintf(output_file, "\\n\\\n");
264 else
265 fprintf(output_file, "\\n");
266 break;
267 case '\\': fprintf(output_file, "\\\\"); break;
268 case '"' : fprintf(output_file, "\\\""); break;
269 default : fprintf(output_file, "%c", buf[i]); break;
270 }
271 }
272 fprintf(output_file, "\"\n\n");
273 return buf_len;
274}
275
276static char *get_mime(char *filename) {
277 char *ext = strchr(filename, '.');
278 if (ext) {
279 ext++;
280 if (strcmp(ext, "png") == 0) return "image/png";
281 else if (strcmp(ext, "gif") == 0) return "image/gif";
282 else if (strcmp(ext, "jpg") == 0) return "image/jpg";
283 else if (strcmp(ext, "ico") == 0) return "image/x-icon";
284 }
285 return "unknown";
286}
287
288static uint32_t dump_base64(char *ident, char *mime, uint8_t *buf, size_t buf_len) {
289 char tpl_type[32];
290 size_t b64_buf_len = buf_len * 4 + 16;
291 uint8_t *b64_buf = malloc(b64_buf_len);
292 if (!b64_buf)
293 die("%s: can't alloc %zd bytes\n", __func__, b64_buf_len);
294 int i, b64_len = b64_encode(buf, buf_len, b64_buf, b64_buf_len);
295 snprintf(tpl_type, sizeof(tpl_type), "data:%s;base64,", mime);
296 fprintf(output_file, "#define %s \"%s\\\n", ident, tpl_type);
297 for (i = 0; i < b64_len; i++) {
298 if (i && i % 76 == 0)
299 fprintf(output_file, "\\\n");
300 fprintf(output_file, "%c", b64_buf[i]);
301 }
302 fprintf(output_file, "\"\n\n");
303 free(b64_buf);
304 return strlen(tpl_type) + b64_len;
305}
306
307int main(void) {
308 int i;
309
310 parse_index_file(index_filename);
311
312 output_file = xfopen(output_pages_h, "w");
313 fprintf(output_file, "#ifndef WEBIF_PAGES_H_\n");
314 fprintf(output_file, "#define WEBIF_PAGES_H_\n");
315 fprintf(output_file, "\n");
316 fprintf(output_file, "struct template {\n");
317 fprintf(output_file, " char *tpl_name;\n");
318 fprintf(output_file, " char *tpl_data;\n");
319 fprintf(output_file, " char *tpl_deps;\n");
320 fprintf(output_file, " uint32_t tpl_data_len;\n");
321 fprintf(output_file, "};\n");
322 fprintf(output_file, "\n");
323 fprintf(output_file, "int32_t templates_count(void);\n");
324 fprintf(output_file, "\n");
325 fprintf(output_file, "#endif\n");
326 fclose(output_file);
327
328 output_file = xfopen(output_pages_c, "w");
329 fprintf(output_file, "#include \"../globals.h\"\n");
330 fprintf(output_file, "\n");
331 fprintf(output_file, "#ifdef WEBIF\n");
332 fprintf(output_file, "\n");
333 fprintf(output_file, "#include \"pages.h\"\n");
334 fprintf(output_file, "\n");
335
336 for (i = 0; i < templates.num; i++) {
337 uint8_t *buf;
338 size_t buf_len, data_len = 0;
339 readfile(templates.data[i].file, &buf, &buf_len);
340 switch (templates.data[i].type) {
341 case TXT: data_len = dump_text(templates.data[i].ident, buf, buf_len); break;
342 case BIN: data_len = dump_base64(templates.data[i].ident, get_mime(templates.data[i].file), buf, buf_len); break;
343 }
344 free(buf);
345 templates.data[i].data_len = data_len;
346 }
347
348 fprintf(output_file, "const struct template templates[] = {\n");
349 for (i = 0; i < templates.num; i++) {
350 print_template(i);
351 }
352 fprintf(output_file, "};\n");
353 fprintf(output_file, "\n");
354 fprintf(output_file, "int32_t templates_count(void) { return sizeof(templates) / sizeof(struct template); }\n");
355 fprintf(output_file, "\n");
356 fprintf(output_file, "#endif\n");
357 fclose(output_file);
358
359 return 0;
360}
Note: See TracBrowser for help on using the repository browser.