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("&", fp); 36 break; 37 38 case '<': 39 fputs("<", fp); 40 break; 41 42 case '>': 43 fputs(">", fp); 44 break; 45 46 case '"': 47 fputs(""", fp); 48 break; 49 50 case '\'': 51 fputs("'", 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 }