tntpedia

tntpedia stac build
git clone git://git.kocotian.pl/tntpedia.git
Log | Files | Refs | README | LICENSE

compile.c (9265B)


      1 /*
      2    stacinhtml - STAtic C IN HTML - simple static site generator
      3    Copyright (C) 2021  Kacper Kocot <kocotian@kocotian.pl>
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 3 of the License, or
      8    (at your option) any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
     18 
     19 */
     20 
     21 #define _XOPEN_SOURCE 700
     22 
     23 #include <fcntl.h>
     24 #include <stdio.h>
     25 #include <string.h>
     26 #include <sys/stat.h>
     27 #include <sys/types.h>
     28 #include <unistd.h>
     29 
     30 #include "arg.h"
     31 #include "str.h"
     32 #include "util.h"
     33 
     34 /* Macros */
     35 #define $(VARNAME) getVariableValue(#VARNAME)
     36 #define $$(VARNAME) write(fd, $(VARNAME).data, $(VARNAME).len);
     37 #define DECLVAR(VARNAME, VALUE) \
     38 	(vs[vss].name.len = strlen(vs[vss].name.data = #VARNAME), \
     39 	 vs[vss].value.len = strlen(vs[vss].value.data = VALUE == NULL ? "<null>" : VALUE), \
     40 	 ++vss)
     41 #define DECLVAR_S(VARNAME, VALUE) \
     42 	(vs[vss].name.len = strlen(vs[vss].name.data = #VARNAME), \
     43 	 vs[vss].value = VALUE, \
     44 	 ++vss)
     45 #define DECLVAR_N(VARNAME, VALUE, N) \
     46 	(vs[vss].name.len = strlen(vs[vss].name.data = #VARNAME), \
     47 	 vs[vss].value = VALUE, vs[vss].len = N, \
     48 	 ++vss)
     49 #define STACKPOP() --vss
     50 #define BUFFER_SIZE 64 * 1024
     51 #define VS_MAX 256
     52 
     53 /* Types */
     54 typedef struct {
     55 	String name;
     56 	String value;
     57 } Variable;
     58 
     59 /* Prototypes */
     60 static void generateCode(int fd, String input, int c_mode);
     61 static void generateC(int fd, String input);
     62 static Variable *getVariableByName(char *v);
     63 static String getVariableValue(char *varname);
     64 static void preprocess(int outputfd, int inputfd, String *output);
     65 static void usage(void);
     66 
     67 /* Globals */
     68 char *argv0;
     69 
     70 static Variable vs[VS_MAX]; /* Variable stack */
     71 static size_t vss; /* Variable stack size */
     72 static char buffer[8192]; /* Buffer for persistent things like variables */
     73 static char *bufptr; /* Pointer to usable space in buffer */
     74 
     75 /* Functions */
     76 static void
     77 generateCode(int fd, String input, int c_mode)
     78 {
     79 	if (c_mode) {
     80 		write(fd, input.data, input.len);
     81 	} else {
     82 		size_t i = -1;
     83 		unsigned char c = 0;
     84 		write(fd, "write(fd, \"", 11);
     85 		while (++i < input.len) {
     86 			c = (unsigned)(*input.data);
     87 			if (*input.data == '\n')
     88 				dprintf(fd, "\"\n\"");
     89 			dprintf(fd, "\\%03o", c);
     90 			++input.data;
     91 		}
     92 		dprintf(fd, "\", %ld);", input.len);
     93 	}
     94 }
     95 
     96 static void
     97 generateC(int fd, String input)
     98 {
     99 	int c_mode;
    100 	size_t i, li;
    101 	String ibuf;
    102 	c_mode = 0;
    103 
    104 	ibuf.data = input.data;
    105 	ibuf.len = input.len;
    106 	for (i = li = 0; i < input.len; ++i) {
    107 		if (input.data[i] == '%') {
    108 			ibuf.len = ++i - li - 1;
    109 			generateCode(fd, ibuf, c_mode);
    110 			ibuf.data = input.data + i;
    111 			ibuf.len = input.len - i;
    112 			c_mode = !c_mode;
    113 			li = i;
    114 		}
    115 	}
    116 	generateCode(fd, ibuf, c_mode);
    117 }
    118 
    119 static Variable *
    120 getVariableByName(char *v)
    121 {
    122 	ssize_t n;
    123 	String s = { .data = v, .len = strlen(v) };
    124 	n = vss;
    125 	while (--n >= 0) {
    126 		if (!Strcmp(vs[n].name, s))
    127 			return vs + n;
    128 	}
    129 	return NULL;
    130 }
    131 
    132 static String
    133 getVariableValue(char *v)
    134 {
    135 	Variable *var = getVariableByName(v);
    136 	if (var == NULL) {
    137 		String s = {
    138 			.len = 0
    139 		};
    140 		return s;
    141 	}
    142 	return var->value;
    143 }
    144 
    145 static void
    146 preprocess(int outputfd, int inputfd, String *output)
    147 {
    148 	String parseinput, readinput;
    149 	char idata[BUFFER_SIZE];
    150 	ssize_t rb;
    151 
    152 	/* Reading data */
    153 	if ((rb = read(inputfd, idata, BUFFER_SIZE)) < 0)
    154 		die("read (input):");
    155 
    156 	/* Setting readinput variables */
    157 	readinput.data = idata;
    158 	readinput.len  = rb;
    159 
    160 	/* Zeroing first byte of an output data */
    161 	*(output->data) = '\0';
    162 
    163 	while (Strtok(readinput, &parseinput, '\n') > 0) {
    164 		if (*(parseinput.data) == '@' && *(parseinput.data + 1) != '@') {
    165 			++parseinput.data;
    166 			if (*(parseinput.data) == '#'); /* comment */
    167 			else if (*(parseinput.data) == '<') { /* do something special */
    168 				++parseinput.data;
    169 				if (!strncmp(parseinput.data, "###", 3)) {
    170 					String function;
    171 					strncat((*output).data, "%",
    172 							MIN(BUFFER_SIZE - ((*output).len)++, 1));
    173 					function = $(function);
    174 					strncat((*output).data, function.data,
    175 							MIN(BUFFER_SIZE - (*output).len, function.len));
    176 					(*output).len += function.len;
    177 					strncat((*output).data, "();%",
    178 							MIN(BUFFER_SIZE - ((*output).len)++, 4));
    179 					(*output).len += 3;
    180 				} else {
    181 					write(outputfd, "\n", 1);
    182 					write(outputfd, parseinput.data, parseinput.len - 2);
    183 				}
    184 				--parseinput.data;
    185 			} else { /* variable */
    186 				String tok;
    187 				if (Strtok(parseinput, &tok, '=') <= 0) {
    188 					/* TODO: return syntax error */;
    189 				}
    190 				strncpy(bufptr, (idata + (parseinput.data - idata)),
    191 						MIN(tok.len - 1, 8192 - (bufptr - buffer)));
    192 				vs[vss].name.data = bufptr;
    193 				vs[vss].name.len = tok.len - 1;
    194 				bufptr += tok.len - 1;
    195 				strncpy(bufptr, (idata + (parseinput.data - idata)) + (tok.len + 1),
    196 						MIN(parseinput.len - (tok.len + 1) - 1, 8192 - (bufptr - buffer)));
    197 				vs[vss].value.data = bufptr;
    198 				vs[vss].value.len = parseinput.len - (tok.len + 1) - 1;
    199 				bufptr += parseinput.len - (tok.len + 1) - 1;
    200 				vs[vss].name = Strtrim(vs[vss].name);
    201 				vs[vss].value = Strtrim(vs[vss].value);
    202 				++vss;
    203 			}
    204 			--parseinput.data;
    205 		} else {
    206 			strncat((*output).data, readinput.data,
    207 					MIN(BUFFER_SIZE - (*output).len, parseinput.len));
    208 			(*output).len += parseinput.len;
    209 		}
    210 		readinput.data += parseinput.len;
    211 		readinput.len -= parseinput.len;
    212 	}
    213 }
    214 
    215 static void
    216 usage(void)
    217 {
    218 	die("usage: %s [-v] [-o OUTPUT] INPUT [TEMPLATE]", argv0);
    219 }
    220 
    221 static void
    222 template(int outputfd, String templatename)
    223 {
    224 	size_t vsscopy, viter;
    225 	char tname[sizeof TEMPLATEDIR + 256];
    226 	char input_buf[BUFFER_SIZE];
    227 	int fd;
    228 	/* String function = $(function); -- unused, maybe will for later? */
    229 	String fun_iden = Striden(templatename);
    230 	String input = {
    231 		.data = input_buf,
    232 		.len = 0
    233 	};
    234 
    235 	/* Saving stack size */
    236 	vsscopy = vss;
    237 	/* Declaring function name as templatename */
    238 	/* DECLVAR_S(function, fun_iden); TODO: recursive templates handling */
    239 
    240 	strcpy(tname, TEMPLATEDIR);
    241 	strncpy(tname + (sizeof TEMPLATEDIR - 1), templatename.data, MIN(templatename.len + 1, 256));
    242 
    243 	/* Opening template */
    244 	if ((fd = open(tname, O_RDONLY)) < 0)
    245 		die("open (template '%s'):", tname);
    246 
    247 	/* Preprocessing */
    248 	preprocess(outputfd, fd, &input);
    249 
    250 	write(outputfd, "void ", 5);
    251 	write(outputfd, fun_iden.data, fun_iden.len);
    252 	write(outputfd, "(void) {\n", 9);
    253 
    254 	/* Declaring variables */
    255 	for (viter = vsscopy; viter < vss; ++viter)
    256 		dprintf(outputfd, "DECLVAR(%.*s, \"%.*s\"); ",
    257 				(int)vs[viter].name.len,  vs[viter].name.data,
    258 				(int)vs[viter].value.len, vs[viter].value.data);
    259 
    260 	/* Generating C code */
    261 	generateC(outputfd, input);
    262 	write(outputfd, "}\n", 2);
    263 
    264 	/* Restoring stack size */
    265 	vss = vsscopy;
    266 
    267 	/* Closing template */
    268 	close(fd);
    269 }
    270 
    271 /* Main */
    272 int
    273 main(int argc, char *argv[])
    274 {
    275 	/* Variables: */
    276 	/* File names and file descriptors */
    277 	char *inputfn = NULL, *outputfn = NULL;
    278 	int inputfd, outputfd;
    279 	size_t viter;
    280 
    281 	/* Input data */
    282 	char input_buf[BUFFER_SIZE];
    283 
    284 	String input = {
    285 		.data = input_buf,
    286 		.len = 0
    287 	}, caller;
    288 
    289 	/* Options parsing */
    290 	ARGBEGIN {
    291 	case 'o':
    292 		outputfn = ARGF(); break;
    293 	case 'v':
    294 		die("compile from stacinhtml-"VERSION); break;
    295 	default:
    296 		  usage(); break;
    297 	} ARGEND
    298 
    299 	/* Argument parsing */
    300 	if (argc == 1)
    301 		inputfn = argv[0];
    302 	else
    303 		usage();
    304 
    305 	if (outputfn == NULL) /* TODO */
    306 		die("output not specified (needed in current stage)");
    307 
    308 	/* Setting variable stack size to 0 */
    309 	vss = 0;
    310 	bufptr = buffer;
    311 
    312 	/* Declaring a few variables */
    313 	DECLVAR(title, outputfn);
    314 	DECLVAR(template, "basic.stac");
    315 
    316 	/* Opening an input */
    317 	if ((inputfd = open(inputfn, O_RDONLY)) < 0)
    318 		die("open (input):");
    319 
    320 	/* Opening an output */
    321 	if ((outputfd = open(outputfn, O_WRONLY | O_CREAT, 0644)) < 0)
    322 		die("open (output):");
    323 
    324 	/* On the beginning, we are going to include assemble.h */
    325 	write(outputfd, "#include <assemble.h>\n", 22);
    326 
    327 	/* Actual preprocessing */
    328 	preprocess(outputfd, inputfd, &input);
    329 
    330 	/* Closing an input */
    331 	close(inputfd);
    332 
    333 	/* Writing beginning of content() */
    334 	write(outputfd, "void content(void) {", 20);
    335 
    336 	/* And after that, generating C code to output */
    337 	generateC(outputfd, input);
    338 
    339 	/* After this, closing content() */
    340 	write(outputfd, "\n}", 2);
    341 
    342 	/* And finally adding templates */
    343 	DECLVAR(function, "content");
    344 	template(outputfd, getVariableByName("template")->value);
    345 	STACKPOP();
    346 
    347 	/* main() beginning */
    348 	write(outputfd, "\nint main(void) {\n\t", 19);
    349 
    350 	/* Declaring variables */
    351 	for (viter = 0; viter < vss; ++viter)
    352 		dprintf(outputfd, "DECLVAR(%.*s, \"%.*s\"); ",
    353 				(int)vs[viter].name.len,  vs[viter].name.data,
    354 				(int)vs[viter].value.len, vs[viter].value.data);
    355 
    356 	/* calling function */
    357 	caller = Striden($(template));
    358 	write(outputfd, caller.data, caller.len);
    359 
    360 	/* main() ending */
    361 	write(outputfd, "();\n}\n", 6);
    362 
    363 	/* Closing an output */
    364 	close(outputfd);
    365 
    366 	return 0;
    367 }