source: trunk/webif/pages_gen.c@ 8435

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

webif: Remove the need to save config info in struct templates.

Configuration data for templates was introduced in commit r7221
as a way to fix ticket #2665.

Basically we kept the configuration deps in the template and when we
loaded the template from file we checked if the config items are enabled.
If they weren't we ignorred the template like it didn't exist.

But now there is a better way. If config item was disabled when the
binary was built then the template does not exist in struct templates
at all.

So we can just check if the requested template exists in struct templates,
and if it doesn't exists we just don't try to load it from file, so the
problem in ticket #2665 is still solved but we remove lots of code (one
less place place that knows about configuration items, yay!) and lower
the binary size by ~1.4k.

File size: 10.3 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_data_len=%u },\n",
244 ident,
245 templates.data[tpl_idx].type == TXT ? "TPL" : "", ident,
246 templates.data[tpl_idx].data_len
247 );
248
249 if (ifdef_open && strcmp(deps, next_deps) != 0) {
250 fprintf(output_file, "#endif\n");
251 ifdef_open = 0;
252 }
253}
254
255static uint32_t dump_text(char *ident, uint8_t *buf, size_t buf_len) {
256 int i;
257 fprintf(output_file, "#define TPL%s \\\n\"", ident);
258 for (i = 0; i < buf_len; i++) {
259 switch (buf[i]) {
260 case '\n':
261 if (i < buf_len - 1)
262 fprintf(output_file, "\\n\\\n");
263 else
264 fprintf(output_file, "\\n");
265 break;
266 case '\\': fprintf(output_file, "\\\\"); break;
267 case '"' : fprintf(output_file, "\\\""); break;
268 default : fprintf(output_file, "%c", buf[i]); break;
269 }
270 }
271 fprintf(output_file, "\"\n\n");
272 return buf_len;
273}
274
275static char *get_mime(char *filename) {
276 char *ext = strchr(filename, '.');
277 if (ext) {
278 ext++;
279 if (strcmp(ext, "png") == 0) return "image/png";
280 else if (strcmp(ext, "gif") == 0) return "image/gif";
281 else if (strcmp(ext, "jpg") == 0) return "image/jpg";
282 else if (strcmp(ext, "ico") == 0) return "image/x-icon";
283 }
284 return "unknown";
285}
286
287static uint32_t dump_base64(char *ident, char *mime, uint8_t *buf, size_t buf_len) {
288 char tpl_type[32];
289 size_t b64_buf_len = buf_len * 4 + 16;
290 uint8_t *b64_buf = malloc(b64_buf_len);
291 if (!b64_buf)
292 die("%s: can't alloc %zd bytes\n", __func__, b64_buf_len);
293 int i, b64_len = b64_encode(buf, buf_len, b64_buf, b64_buf_len);
294 snprintf(tpl_type, sizeof(tpl_type), "data:%s;base64,", mime);
295 fprintf(output_file, "#define %s \"%s\\\n", ident, tpl_type);
296 for (i = 0; i < b64_len; i++) {
297 if (i && i % 76 == 0)
298 fprintf(output_file, "\\\n");
299 fprintf(output_file, "%c", b64_buf[i]);
300 }
301 fprintf(output_file, "\"\n\n");
302 free(b64_buf);
303 return strlen(tpl_type) + b64_len;
304}
305
306int main(void) {
307 int i;
308
309 parse_index_file(index_filename);
310
311 output_file = xfopen(output_pages_h, "w");
312 fprintf(output_file, "#ifndef WEBIF_PAGES_H_\n");
313 fprintf(output_file, "#define WEBIF_PAGES_H_\n");
314 fprintf(output_file, "\n");
315 fprintf(output_file, "struct template {\n");
316 fprintf(output_file, " char *tpl_name;\n");
317 fprintf(output_file, " char *tpl_data;\n");
318 fprintf(output_file, " uint32_t tpl_data_len;\n");
319 fprintf(output_file, "};\n");
320 fprintf(output_file, "\n");
321 fprintf(output_file, "int32_t templates_count(void);\n");
322 fprintf(output_file, "\n");
323 fprintf(output_file, "#endif\n");
324 fclose(output_file);
325
326 output_file = xfopen(output_pages_c, "w");
327 fprintf(output_file, "#include \"../globals.h\"\n");
328 fprintf(output_file, "\n");
329 fprintf(output_file, "#ifdef WEBIF\n");
330 fprintf(output_file, "\n");
331 fprintf(output_file, "#include \"pages.h\"\n");
332 fprintf(output_file, "\n");
333
334 for (i = 0; i < templates.num; i++) {
335 uint8_t *buf;
336 size_t buf_len, data_len = 0;
337 readfile(templates.data[i].file, &buf, &buf_len);
338 switch (templates.data[i].type) {
339 case TXT: data_len = dump_text(templates.data[i].ident, buf, buf_len); break;
340 case BIN: data_len = dump_base64(templates.data[i].ident, get_mime(templates.data[i].file), buf, buf_len); break;
341 }
342 free(buf);
343 templates.data[i].data_len = data_len;
344 }
345
346 fprintf(output_file, "const struct template templates[] = {\n");
347 for (i = 0; i < templates.num; i++) {
348 print_template(i);
349 }
350 fprintf(output_file, "};\n");
351 fprintf(output_file, "\n");
352 fprintf(output_file, "int32_t templates_count(void) { return sizeof(templates) / sizeof(struct template); }\n");
353 fprintf(output_file, "\n");
354 fprintf(output_file, "#endif\n");
355 fclose(output_file);
356
357 return 0;
358}
Note: See TracBrowser for help on using the repository browser.