comparison scid/theme.c @ 29:695637f1d8a7

scid: first index page in javascript
author David Demelier <markand@malikania.fr>
date Thu, 04 Aug 2022 14:13:58 +0200
parents 4c16bb25e4f1
children 43333d18e4b8
comparison
equal deleted inserted replaced
28:4c16bb25e4f1 29:695637f1d8a7
2 #include <errno.h> 2 #include <errno.h>
3 #include <limits.h> 3 #include <limits.h>
4 #include <string.h> 4 #include <string.h>
5 5
6 #include <duktape.h> 6 #include <duktape.h>
7 #include <jansson.h>
8 #include <mustache.h>
7 9
8 #include "log.h" 10 #include "log.h"
11 #include "scid.h"
9 #include "theme.h" 12 #include "theme.h"
10 #include "util.h" 13 #include "util.h"
14
15 #define SIGNATURE DUK_HIDDEN_SYMBOL("File")
11 16
12 struct theme { 17 struct theme {
13 char base[PATH_MAX]; 18 char base[PATH_MAX];
14 duk_context *ctx; 19 duk_context *ctx;
15 }; 20 };
16 21
22 /* {{{ mustache support */
23
24 static void
25 mch_parse_error(int code, const char *msg, unsigned int line, unsigned int col, void *data)
26 {
27 (void)code;
28
29 const char *path = data;
30
31 log_warn("theme: %s:%u:%u:%s", path, line, col, msg);
32 }
33
34 static int
35 mch_out_verbatim(const char *output, size_t size, void *data)
36 {
37 FILE *fp = data;
38
39 if (fwrite(output, 1, size, fp) != size)
40 return -1;
41
42 return 0;
43 }
44
45 static int
46 mch_out_escaped(const char *output, size_t size, void *data)
47 {
48 FILE *fp = data;
49
50 for (size_t i = 0; i < size; ++i) {
51 switch (output[i]) {
52 case '"':
53 if (mch_out_verbatim("&quot;", 6, fp) < 0)
54 return -1;
55 break;
56 case '&':
57 if (mch_out_verbatim("&amp;", 5, fp) < 0)
58 return -1;
59 break;
60 case '<':
61 if (mch_out_verbatim("&lt;", 4, fp) < 0)
62 return -1;
63 break;
64 case '>':
65 if (mch_out_verbatim("&gt;", 4, fp) < 0)
66 return -1;
67 break;
68 default:
69 if (mch_out_verbatim(&output[i], 1, fp) <0)
70 return -1;
71 break;
72 }
73 }
74
75 return 0;
76 }
77
78 static inline int
79 mch_out_int(FILE *fp, intmax_t val)
80 {
81 char buf[64] = {0};
82
83 snprintf(buf, sizeof (buf), "%jd", val);
84
85 return mch_out_verbatim(buf, strlen(buf), fp);
86 }
87
88 static inline int
89 mch_out_double(FILE *fp, double val)
90 {
91 char buf[64] = {0};
92
93 snprintf(buf, sizeof (buf), "%f", val);
94
95 return mch_out_verbatim(buf, strlen(buf), fp);
96 }
97
98 static int
99 mch_dump(void *node, int (*outputter)(const char *, size_t, void *), void *rdata, void *pdata)
100 {
101 (void)pdata;
102
103 FILE *fp = rdata;
104 const json_t *value = node;
105
106 switch (json_typeof(value)) {
107 case JSON_OBJECT:
108 case JSON_ARRAY:
109 /* This indicates a document construction error. */
110 // TODO: change to error log.
111 abort();
112 break;
113 case JSON_STRING:
114 return outputter(json_string_value(value), json_string_length(value), fp);
115 case JSON_INTEGER:
116 return mch_out_int(fp, json_integer_value(value));
117 case JSON_REAL:
118 return mch_out_double(fp, json_real_value(value));
119 case JSON_TRUE:
120 return mch_out_verbatim("true", 4, fp);
121 case JSON_FALSE:
122 return mch_out_verbatim("false", 5, fp);
123 default:
124 break;
125 }
126
127 return 0;
128 }
129
130 static void *
131 mch_get_root(void *pdata)
132 {
133 return pdata;
134 }
135
136 static void *
137 mch_get_child_by_name(void *node, const char *name, size_t size, void *pdata)
138 {
139 (void)pdata;
140
141 json_t *value = node;
142
143 #if 0
144 printf("-> Seeking name '%.*s'\n", (int)size, name);
145 printf("-> Type of node: %d (%p)\n", json_typeof(value), value);
146 #endif
147
148 if (json_is_object(value))
149 return json_object_getn(node, name, size);
150
151 return NULL;
152 }
153
154 static void *
155 mch_get_child_by_index(void *node, unsigned int index, void *pdata)
156 {
157 (void)pdata;
158
159 json_t *value = node;
160
161 #if 0
162 printf("-> Seeking index '%u'\n", index);
163 printf("-> Type of node: %d (%p)\n", json_typeof(value), value);
164 #endif
165
166 if (json_is_array(value))
167 return json_array_get(node, index);
168
169 return NULL;
170 }
171
172 static MUSTACHE_TEMPLATE *
173 mch_get_partial(const char *name, size_t size, void *pdata)
174 {
175 (void)name;
176 (void)size;
177 (void)pdata;
178
179 return NULL;
180 }
181
182 static int
183 mch_process(const char *path, const char *input, FILE *fp, json_t *doc)
184 {
185 MUSTACHE_PARSER parser = {
186 .parse_error = mch_parse_error
187 };
188 MUSTACHE_RENDERER rdr = {
189 .out_verbatim = mch_out_verbatim,
190 .out_escaped = mch_out_escaped
191 };
192 MUSTACHE_DATAPROVIDER pv = {
193 .dump = mch_dump,
194 .get_root = mch_get_root,
195 .get_child_by_name = mch_get_child_by_name,
196 .get_child_by_index = mch_get_child_by_index,
197 .get_partial = mch_get_partial
198 };
199 MUSTACHE_TEMPLATE *tmpl;
200 int status;
201
202 if (!(tmpl = mustache_compile(input, strlen(input), &parser, (void *)path, 0)))
203 return -1;
204
205 status = mustache_process(tmpl, &rdr, fp, &pv, doc);
206 mustache_release(tmpl);
207
208 return status ? -1 : 0;
209 }
210
211 void
212 mch_templatize(FILE *fp, const char *path, json_t *doc)
213 {
214 assert(path);
215
216 char *in = NULL;
217
218 if ((in = util_read(path))) {
219 /* Process template but don't return a partially written file. */
220 if (mch_process(path, in, fp, doc) < 0)
221 log_warn("theme: mustache error %s", path);
222
223 free(in);
224 } else
225 log_warn("theme: %s: %s", path, strerror(errno));
226 }
227
228 /* }}} */
229
230 /* {{{ Scid module */
231
232 static duk_ret_t
233 Scid_render(duk_context *ctx)
234 {
235 FILE *fp = duk_require_pointer(ctx, 0);
236 const char *path = duk_require_string(ctx, 1);
237 const char *json = duk_json_encode(ctx, 2);
238 json_t *doc = NULL;
239 json_error_t err;
240
241 if (json && !(doc = json_loads(json, 0, &err)))
242 return duk_error(ctx, DUK_ERR_ERROR, "%d:%d:%s", err.line, err.column, err.text);
243
244 mch_templatize(fp, theme_path(scid.theme, path), doc);
245
246 if (doc)
247 json_decref(doc);
248
249 return 0;
250 }
251
252 static duk_ret_t
253 Scid_print(duk_context *ctx)
254 {
255 puts(duk_require_string(ctx, 0));
256
257 return 0;
258 }
259
260 static const duk_function_list_entry functions[] = {
261 { "render", Scid_render, 3 },
262 { "print", Scid_print, 1 },
263 { NULL, NULL, 0 }
264 };
265
266 /* }}} */
267
17 static char * 268 static char *
18 render(struct theme *t, const char *json, const char *function) 269 call(struct theme *t, json_t *json, const char *function)
19 { 270 {
20 char *ret = NULL; 271 char *out = NULL, *dump = NULL;
272 size_t outsz = 0;
273 FILE *fp = NULL;
274 int nargs = 1;
21 275
22 duk_get_global_string(t->ctx, function); 276 duk_get_global_string(t->ctx, function);
23 277
24 if (!duk_is_callable(t->ctx, -1)) { 278 if (!duk_is_callable(t->ctx, -1))
25 duk_pop(t->ctx); 279 goto over;
26 return NULL; 280 if (!(fp = open_memstream(&out, &outsz)))
281 goto over;
282
283 duk_push_pointer(t->ctx, fp);
284
285 if (json && (dump = json_dumps(json, JSON_COMPACT))) {
286 duk_push_string(t->ctx, dump);
287 duk_json_decode(t->ctx, -1);
288 nargs++;
27 } 289 }
28 290
29 duk_push_string(t->ctx, json); 291 if (duk_pcall(t->ctx, nargs) != 0)
30 duk_json_decode(t->ctx, -1);
31
32 if (duk_pcall(t->ctx, 1) != 0) {
33 log_warn("theme: %s", duk_safe_to_string(t->ctx, -1)); 292 log_warn("theme: %s", duk_safe_to_string(t->ctx, -1));
34 } else if (duk_is_string(t->ctx, -1)) 293
35 ret = util_strdup(duk_get_string(t->ctx, -1)); 294 over:
36 295 duk_pop(t->ctx);
37 duk_pop_n(t->ctx, 1);
38 296
39 /* 297 /*
40 * For convenience, otherwise all callers have to check for non-NULL 298 * For convenience, otherwise all callers have to check for non-NULL
41 * after calling the function. 299 * after calling the function.
42 */ 300 */
43 if (!ret) 301 free(dump);
44 ret = util_strdup(""); 302
45 303 if (fp)
46 return ret; 304 fclose(fp);
305 if (!out)
306 out = util_strdup("");
307
308 return out;
47 } 309 }
48 310
49 const char * 311 const char *
50 theme_path(struct theme *t, const char *filename) 312 theme_path(struct theme *t, const char *filename)
51 { 313 {
79 else { 341 else {
80 if (duk_peval_string(t->ctx, data) != 0) 342 if (duk_peval_string(t->ctx, data) != 0)
81 log_warn("theme: %s", duk_safe_to_string(t->ctx, -1)); 343 log_warn("theme: %s", duk_safe_to_string(t->ctx, -1));
82 344
83 duk_pop(t->ctx); 345 duk_pop(t->ctx);
346 duk_push_object(t->ctx);
347 duk_put_function_list(t->ctx, -1, functions);
348 duk_put_global_string(t->ctx, "Scid");
349 free(data);
84 } 350 }
85 351
86 return t; 352 return t;
87 } 353 }
88 354
89 char * 355 char *
90 theme_render_index(struct theme *t, const char *json) 356 theme_page_index(struct theme *t, json_t *json)
91 { 357 {
92 assert(t); 358 assert(t);
93 assert(json); 359
94 360 return call(t, json, "onPageIndex");
95 return render(t, json, "index"); 361 }
362
363 char *
364 theme_page_status(struct theme *t, enum khttp status)
365 {
366 assert(t);
367
368 (void)t;
369 (void)status;
370
371 #if 0
372 return call(t, json, "onPageStatus");
373 #endif
374 return "ERROR";
96 } 375 }
97 376
98 void 377 void
99 theme_free(struct theme *t) 378 theme_free(struct theme *t)
100 { 379 {