simplefiles

A lightweight C CGI web application for managing files.
git clone https://scm.kuandu.systems/git-raw/simplefiles.git
Log | Files | Refs | README | LICENSE

html.c (5924B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <unistd.h>
      5 
      6 #include "html.h"
      7 
      8 static const char *
      9 find_variable(const char *,
     10     const struct html_var *, size_t,
     11     const struct html_var *, size_t);
     12 
     13 static int
     14 render_text(FILE *,
     15     const char *,
     16     size_t,
     17     const struct html_var *,
     18     size_t,
     19     const struct html_var *,
     20     size_t);
     21 
     22 static const struct html_block *
     23 find_block(const char *, const struct html_block *, size_t);
     24 static const char *
     25 find_marker(const char *, size_t, const char[2]);
     26 
     27 void
     28 html_escape(FILE * fp, const char *s)
     29 {
     30 	unsigned char c;
     31 
     32 	while ((c = (unsigned char) *s++) != '\0') {
     33 		switch (c) {
     34 		case '&':
     35 			fputs("&amp;", fp);
     36 			break;
     37 
     38 		case '<':
     39 			fputs("&lt;", fp);
     40 			break;
     41 
     42 		case '>':
     43 			fputs("&gt;", fp);
     44 			break;
     45 
     46 		case '"':
     47 			fputs("&quot;", fp);
     48 			break;
     49 
     50 		case '\'':
     51 			fputs("&#39;", fp);
     52 			break;
     53 
     54 		default:
     55 			fputc(c, fp);
     56 			break;
     57 		}
     58 	}
     59 }
     60 
     61 /*
     62  * NOTE: Templates may contain at most one block.
     63  */
     64 int
     65 render_template(FILE *fp, const char *filename,
     66     const struct html_var *vars, size_t nvars,
     67     const struct html_block *blocks, size_t nblocks)
     68 {
     69 	FILE *template;
     70 	char *text;
     71 	long size;
     72 	size_t len;
     73 	const char *block_start;
     74 	const char *block_end;
     75 	const char *p;
     76 	char block_name[128];
     77 	size_t block_name_len;
     78 	const struct html_block *block;
     79 	size_t i;
     80 
     81 	template = fopen(filename, "r");
     82 	if (template == NULL)
     83 		return -1;
     84 
     85 	if (fseek(template, 0, SEEK_END) == -1) {
     86 		fclose(template);
     87 		return -1;
     88 	}
     89 
     90 	size = ftell(template);
     91 	if (size < 0) {
     92 		fclose(template);
     93 		return -1;
     94 	}
     95 
     96 	if (fseek(template, 0, SEEK_SET) == -1) {
     97 		fclose(template);
     98 		return -1;
     99 	}
    100 
    101 	len = (size_t)size;
    102 
    103 	text = malloc(len + 1);
    104 	if (text == NULL) {
    105 		fclose(template);
    106 		return -1;
    107 	}
    108 
    109 	if (fread(text, 1, len, template) != len) {
    110 		free(text);
    111 		fclose(template);
    112 		return -1;
    113 	}
    114 
    115 	text[len] = '\0';
    116 
    117 	fclose(template);
    118 
    119 	/*
    120 	 * Find the first block.
    121 	 */
    122 	block_start = strstr(text, "{{#");
    123 
    124 	if (block_start == NULL) {
    125 		/*
    126 		 * No blocks: render the whole template normally.
    127 		 */
    128 		if (render_text(fp, text, len,
    129 		    vars, nvars,
    130 		    NULL, 0) == -1) {
    131 			free(text);
    132 			return -1;
    133 		}
    134 
    135 		free(text);
    136 		return 0;
    137 	}
    138 
    139 	/*
    140 	 * Render text before the block.
    141 	 */
    142 	if (render_text(fp,
    143 	    text,
    144 	    (size_t)(block_start - text),
    145 	    vars, nvars,
    146 	    NULL, 0) == -1) {
    147 		free(text);
    148 		return -1;
    149 	}
    150 
    151 	/*
    152 	 * Find the end of the block name.
    153 	 */
    154 	block_end = strstr(block_start + 3, "}}");
    155 	if (block_end == NULL) {
    156 		free(text);
    157 		return -1;
    158 	}
    159 
    160 	block_name_len =
    161 	    (size_t)(block_end - (block_start + 3));
    162 
    163 	if (block_name_len == 0 ||
    164 	    block_name_len >= sizeof(block_name)) {
    165 		free(text);
    166 		return -1;
    167 	}
    168 
    169 	memcpy(block_name,
    170 	    block_start + 3,
    171 	    block_name_len);
    172 	block_name[block_name_len] = '\0';
    173 
    174 	block = find_block(block_name, blocks, nblocks);
    175 	if (block == NULL) {
    176 		free(text);
    177 		return -1;
    178 	}
    179 
    180 	/*
    181 	 * Find the closing block marker.
    182 	 */
    183 	p = block_end + 2;
    184 
    185 	{
    186 		char closing[128];
    187 
    188 		if (snprintf(closing, sizeof(closing),
    189 		    "{{/%s}}", block_name) >=
    190 		    (int)sizeof(closing)) {
    191 			free(text);
    192 			return -1;
    193 		}
    194 
    195 		block_start = strstr(p, closing);
    196 		if (block_start == NULL) {
    197 			free(text);
    198 			return -1;
    199 		}
    200 
    201 		block_end = block_start;
    202 	}
    203 
    204 	/*
    205 	 * Render the block once for every row.
    206 	 */
    207 	for (i = 0; i < block->nrows; i++) {
    208 		const char *row_start = p;
    209 		size_t row_len =
    210 		    (size_t)(block_end - row_start);
    211 
    212 		if (render_text(fp,
    213 		    row_start,
    214 		    row_len,
    215 		    vars, nvars,
    216 		    block->rows[i].vars,
    217 		    block->rows[i].nvars) == -1) {
    218 			free(text);
    219 			return -1;
    220 		}
    221 	}
    222 
    223 	/*
    224 	 * Render anything after the block.
    225 	 */
    226 	p = block_end + strlen(
    227 	    "{{/");
    228 
    229 	p = strstr(p, "}}");
    230 	if (p == NULL) {
    231 		free(text);
    232 		return -1;
    233 	}
    234 
    235 	p += 2;
    236 
    237 	if (render_text(fp,
    238 	    p,
    239 	    strlen(p),
    240 	    vars, nvars,
    241 	    NULL, 0) == -1) {
    242 		free(text);
    243 		return -1;
    244 	}
    245 
    246 	free(text);
    247 	return 0;
    248 }
    249 
    250 static const char *
    251 find_variable(const char *name,
    252     const struct html_var * vars, size_t nvars,
    253     const struct html_var * row_vars, size_t nrowvars)
    254 {
    255 	size_t i;
    256 
    257 	for (i = 0; i < nrowvars; i++) {
    258 		if (strcmp(name, row_vars[i].name) == 0)
    259 			return row_vars[i].value;
    260 	}
    261 
    262 	for (i = 0; i < nvars; i++) {
    263 		if (strcmp(name, vars[i].name) == 0)
    264 			return vars[i].value;
    265 	}
    266 
    267 	return NULL;
    268 }
    269 
    270 static int
    271 render_text(FILE * fp,
    272     const char *text,
    273     size_t len,
    274     const struct html_var * vars,
    275     size_t nvars,
    276     const struct html_var * row_vars,
    277     size_t nrowvars)
    278 {
    279 	const char *p;
    280 	const char *end;
    281 
    282 	p = text;
    283 	end = text + len;
    284 
    285 	while (p < end) {
    286 		const char *start;
    287 		const char *close;
    288 		char name[128];
    289 		size_t namelen;
    290 		const char *value;
    291 
    292 		start = find_marker(p, (size_t)(end - p), "{{");
    293 
    294 		if (start == NULL) {
    295 			if (fwrite(p, 1, (size_t) (end - p), fp) !=
    296 			    (size_t) (end - p))
    297 				return -1;
    298 			break;
    299 		}
    300 		if (fwrite(p, 1, (size_t) (start - p), fp) !=
    301 		    (size_t) (start - p))
    302 			return -1;
    303 
    304 		close = find_marker(start + 2, (size_t)(end - (start + 2)), "}}");
    305 
    306 		if (close == NULL)
    307 			return -1;
    308 
    309 		namelen = (size_t) (close - (start + 2));
    310 
    311 		if (namelen == 0 || namelen >= sizeof(name))
    312 			return -1;
    313 
    314 		memcpy(name, start + 2, namelen);
    315 		name[namelen] = '\0';
    316 
    317 		value = find_variable(name,
    318 		    vars, nvars,
    319 		    row_vars, nrowvars);
    320 
    321 		if (value == NULL)
    322 			return -1;
    323 
    324 		html_escape(fp, value);
    325 
    326 		p = close + 2;
    327 	}
    328 
    329 	return 0;
    330 }
    331 
    332 static const struct html_block *
    333 find_block(const char *name,
    334     const struct html_block *blocks, size_t nblocks)
    335 {
    336 	size_t i;
    337 
    338 	for (i = 0; i < nblocks; i++) {
    339 		if (strcmp(name, blocks[i].name) == 0)
    340 			return &blocks[i];
    341 	}
    342 
    343 	return NULL;
    344 }
    345 
    346 static const char *
    347 find_marker(const char *text, size_t len, const char marker[2])
    348 {
    349 	size_t i;
    350 
    351 	if (len < 2)
    352 		return NULL;
    353 
    354 	for (i = 0; i + 1 < len; i++) {
    355 		if (text[i] == marker[0] &&
    356 		    text[i + 1] == marker[1])
    357 			return text + i;
    358 	}
    359 
    360 	return NULL;
    361 }