1 /*
2  Author: José Bollo <jobol@nonadev.net>
3  Author: José Bollo <jose.bollo@iot.bzh>
4 
5  https://gitlab.com/jobol/mustach
6 
7  Licensed under the Apache License, Version 2.0 (the "License");
8  you may not use this file except in compliance with the License.
9  You may obtain a copy of the License at
10 
11      http://www.apache.org/licenses/LICENSE-2.0
12 
13  Unless required by applicable law or agreed to in writing, software
14  distributed under the License is distributed on an "AS IS" BASIS,
15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  See the License for the specific language governing permissions and
17  limitations under the License.
18 */
19 
20 #define _GNU_SOURCE
21 
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <ctype.h>
27 
28 #include "mustach.h"
29 
30 #define NAME_LENGTH_MAX   1024
31 #define DEPTH_MAX         256
32 
33 #if !defined(NO_OPEN_MEMSTREAM)
memfile_open(char ** buffer,size_t * size)34 static FILE *memfile_open(char **buffer, size_t *size)
35 {
36 	return open_memstream(buffer, size);
37 }
memfile_abort(FILE * file,char ** buffer,size_t * size)38 static void memfile_abort(FILE *file, char **buffer, size_t *size)
39 {
40 	fclose(file);
41 	free(*buffer);
42 	*buffer = NULL;
43 	*size = 0;
44 }
memfile_close(FILE * file,char ** buffer,size_t * size)45 static int memfile_close(FILE *file, char **buffer, size_t *size)
46 {
47 	int rc;
48 
49 	/* adds terminating null */
50 	rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
51 	fclose(file);
52 	if (rc == 0)
53 		/* removes terminating null of the length */
54 		(*size)--;
55 	else {
56 		free(*buffer);
57 		*buffer = NULL;
58 		*size = 0;
59 	}
60 	return rc;
61 }
62 #else
memfile_open(char ** buffer,size_t * size)63 static FILE *memfile_open(char **buffer, size_t *size)
64 {
65 	return tmpfile();
66 }
memfile_abort(FILE * file,char ** buffer,size_t * size)67 static void memfile_abort(FILE *file, char **buffer, size_t *size)
68 {
69 	fclose(file);
70 	*buffer = NULL;
71 	*size = 0;
72 }
memfile_close(FILE * file,char ** buffer,size_t * size)73 static int memfile_close(FILE *file, char **buffer, size_t *size)
74 {
75 	int rc;
76 	size_t s;
77 	char *b;
78 
79 	s = (size_t)ftell(file);
80 	b = malloc(s + 1);
81 	if (b == NULL) {
82 		rc = MUSTACH_ERROR_SYSTEM;
83 		errno = ENOMEM;
84 		s = 0;
85 	} else {
86 		rewind(file);
87 		if (1 == fread(b, s, 1, file)) {
88 			rc = 0;
89 			b[s] = 0;
90 		} else {
91 			rc = MUSTACH_ERROR_SYSTEM;
92 			free(b);
93 			b = NULL;
94 			s = 0;
95 		}
96 	}
97 	*buffer = b;
98 	*size = s;
99 	return rc;
100 }
101 #endif
102 
getpartial(struct mustach_itf * itf,void * closure,const char * name,char ** result)103 static int getpartial(struct mustach_itf *itf, void *closure, const char *name, char **result)
104 {
105 	int rc;
106 	FILE *file;
107 	size_t size;
108 
109 	*result = NULL;
110 	file = memfile_open(result, &size);
111 	if (file == NULL)
112 		rc = MUSTACH_ERROR_SYSTEM;
113 	else {
114 		rc = itf->put(closure, name, 0, file);
115 		if (rc < 0)
116 			memfile_abort(file, result, &size);
117 		else
118 			rc = memfile_close(file, result, &size);
119 	}
120 	return rc;
121 }
122 
process(const char * template,struct mustach_itf * itf,void * closure,FILE * file,const char * opstr,const char * clstr)123 static int process(const char *template, struct mustach_itf *itf, void *closure, FILE *file, const char *opstr, const char *clstr)
124 {
125 	char name[NAME_LENGTH_MAX + 1], *partial, c, *tmp;
126 	const char *beg, *term;
127 	struct { const char *name, *again; size_t length; int emit, entered; } stack[DEPTH_MAX];
128 	size_t oplen, cllen, len, l;
129 	int depth, rc, emit;
130 
131 	emit = 1;
132 	oplen = strlen(opstr);
133 	cllen = strlen(clstr);
134 	depth = 0;
135 	for(;;) {
136 		beg = strstr(template, opstr);
137 		if (beg == NULL) {
138 			/* no more mustach */
139 			if (emit)
140 				fwrite(template, strlen(template), 1, file);
141 			return depth ? MUSTACH_ERROR_UNEXPECTED_END : 0;
142 		}
143 		if (emit)
144 			fwrite(template, (size_t)(beg - template), 1, file);
145 		beg += oplen;
146 		term = strstr(beg, clstr);
147 		if (term == NULL)
148 			return MUSTACH_ERROR_UNEXPECTED_END;
149 		template = term + cllen;
150 		len = (size_t)(term - beg);
151 		c = *beg;
152 		switch(c) {
153 		case '!':
154 		case '=':
155 			break;
156 		case '{':
157 			for (l = 0 ; clstr[l] == '}' ; l++);
158 			if (clstr[l]) {
159 				if (!len || beg[len-1] != '}')
160 					return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
161 				len--;
162 			} else {
163 				if (term[l] != '}')
164 					return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
165 				template++;
166 			}
167 			c = '&';
168 			/*@fallthrough@*/
169 		case '^':
170 		case '#':
171 		case '/':
172 		case '&':
173 		case '>':
174 #if !defined(NO_EXTENSION_FOR_MUSTACH) && !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
175 		case ':':
176 #endif
177 			beg++; len--;
178 		default:
179 			while (len && isspace(beg[0])) { beg++; len--; }
180 			while (len && isspace(beg[len-1])) len--;
181 #if defined(NO_EXTENSION_FOR_MUSTACH) || defined(NO_ALLOW_EMPTY_TAG)
182 			if (len == 0)
183 				return MUSTACH_ERROR_EMPTY_TAG;
184 #endif
185 			if (len > NAME_LENGTH_MAX)
186 				return MUSTACH_ERROR_TAG_TOO_LONG;
187 			memcpy(name, beg, len);
188 			name[len] = 0;
189 			break;
190 		}
191 		switch(c) {
192 		case '!':
193 			/* comment */
194 			/* nothing to do */
195 			break;
196 		case '=':
197 			/* defines separators */
198 			if (len < 5 || beg[len - 1] != '=')
199 				return MUSTACH_ERROR_BAD_SEPARATORS;
200 			beg++;
201 			len -= 2;
202 			for (l = 0; l < len && !isspace(beg[l]) ; l++);
203 			if (l == len)
204 				return MUSTACH_ERROR_BAD_SEPARATORS;
205 			oplen = l;
206 			tmp = alloca(oplen + 1);
207 			memcpy(tmp, beg, oplen);
208 			tmp[oplen] = 0;
209 			opstr = tmp;
210 			while (l < len && isspace(beg[l])) l++;
211 			if (l == len)
212 				return MUSTACH_ERROR_BAD_SEPARATORS;
213 			cllen = len - l;
214 			tmp = alloca(cllen + 1);
215 			memcpy(tmp, beg + l, cllen);
216 			tmp[cllen] = 0;
217 			clstr = tmp;
218 			break;
219 		case '^':
220 		case '#':
221 			/* begin section */
222 			if (depth == DEPTH_MAX)
223 				return MUSTACH_ERROR_TOO_DEEP;
224 			rc = emit;
225 			if (rc) {
226 				rc = itf->enter(closure, name);
227 				if (rc < 0)
228 					return rc;
229 			}
230 			stack[depth].name = beg;
231 			stack[depth].again = template;
232 			stack[depth].length = len;
233 			stack[depth].emit = emit;
234 			stack[depth].entered = rc;
235 			if ((c == '#') == (rc == 0))
236 				emit = 0;
237 			depth++;
238 			break;
239 		case '/':
240 			/* end section */
241 			if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len))
242 				return MUSTACH_ERROR_CLOSING;
243 			rc = emit && stack[depth].entered ? itf->next(closure) : 0;
244 			if (rc < 0)
245 				return rc;
246 			if (rc) {
247 				template = stack[depth++].again;
248 			} else {
249 				emit = stack[depth].emit;
250 				if (emit && stack[depth].entered)
251 					itf->leave(closure);
252 			}
253 			break;
254 		case '>':
255 			/* partials */
256 			if (emit) {
257 				rc = getpartial(itf, closure, name, &partial);
258 				if (rc == 0) {
259 					rc = process(partial, itf, closure, file, opstr, clstr);
260 					free(partial);
261 				}
262 				if (rc < 0)
263 					return rc;
264 			}
265 			break;
266 		default:
267 			/* replacement */
268 			if (emit) {
269 				rc = itf->put(closure, name, c != '&', file);
270 				if (rc < 0)
271 					return rc;
272 			}
273 			break;
274 		}
275 	}
276 }
277 
fmustach(const char * template,struct mustach_itf * itf,void * closure,FILE * file)278 int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file)
279 {
280 	int rc = itf->start ? itf->start(closure) : 0;
281 	if (rc == 0)
282 		rc = process(template, itf, closure, file, "{{", "}}");
283 	return rc;
284 }
285 
fdmustach(const char * template,struct mustach_itf * itf,void * closure,int fd)286 int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd)
287 {
288 	int rc;
289 	FILE *file;
290 
291 	file = fdopen(fd, "w");
292 	if (file == NULL) {
293 		rc = MUSTACH_ERROR_SYSTEM;
294 		errno = ENOMEM;
295 	} else {
296 		rc = fmustach(template, itf, closure, file);
297 		fclose(file);
298 	}
299 	return rc;
300 }
301 
mustach(const char * template,struct mustach_itf * itf,void * closure,char ** result,size_t * size)302 int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size)
303 {
304 	int rc;
305 	FILE *file;
306 	size_t s;
307 
308 	*result = NULL;
309 	if (size == NULL)
310 		size = &s;
311 	file = memfile_open(result, size);
312 	if (file == NULL)
313 		rc = MUSTACH_ERROR_SYSTEM;
314 	else {
315 		rc = fmustach(template, itf, closure, file);
316 		if (rc < 0)
317 			memfile_abort(file, result, size);
318 		else
319 			rc = memfile_close(file, result, size);
320 	}
321 	return rc;
322 }
323 
324