stacinhtml

Static C in HTML - simple static site generator
git clone git://git.kocotian.pl/stacinhtml.git
Log | Files | Refs | README | LICENSE

compile.c (9208B)


      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 		write(fd, "write(fd, \"", 11);
     84 		while (++i < input.len) {
     85 			if (*input.data == '\n')
     86 				dprintf(fd, "\"\n\"");
     87 			dprintf(fd, "\\x%02x", *(input.data++));
     88 		}
     89 		dprintf(fd, "\", %ld);", input.len);
     90 	}
     91 }
     92 
     93 static void
     94 generateC(int fd, String input)
     95 {
     96 	int c_mode;
     97 	size_t i, li;
     98 	String ibuf;
     99 	c_mode = 0;
    100 
    101 	ibuf.data = input.data;
    102 	ibuf.len = input.len;
    103 	for (i = li = 0; i < input.len; ++i) {
    104 		if (input.data[i] == '%') {
    105 			ibuf.len = ++i - li - 1;
    106 			generateCode(fd, ibuf, c_mode);
    107 			ibuf.data = input.data + i;
    108 			ibuf.len = input.len - i;
    109 			c_mode = !c_mode;
    110 			li = i;
    111 		}
    112 	}
    113 	generateCode(fd, ibuf, c_mode);
    114 }
    115 
    116 static Variable *
    117 getVariableByName(char *v)
    118 {
    119 	ssize_t n;
    120 	String s = { .data = v, .len = strlen(v) };
    121 	n = vss;
    122 	while (--n >= 0) {
    123 		if (!Strcmp(vs[n].name, s))
    124 			return vs + n;
    125 	}
    126 	return NULL;
    127 }
    128 
    129 static String
    130 getVariableValue(char *v)
    131 {
    132 	Variable *var = getVariableByName(v);
    133 	if (var == NULL) {
    134 		String s = {
    135 			.len = 0
    136 		};
    137 		return s;
    138 	}
    139 	return var->value;
    140 }
    141 
    142 static void
    143 preprocess(int outputfd, int inputfd, String *output)
    144 {
    145 	String parseinput, readinput;
    146 	char idata[BUFFER_SIZE];
    147 	ssize_t rb;
    148 
    149 	/* Reading data */
    150 	if ((rb = read(inputfd, idata, BUFFER_SIZE)) < 0)
    151 		die("read (input):");
    152 
    153 	/* Setting readinput variables */
    154 	readinput.data = idata;
    155 	readinput.len  = rb;
    156 
    157 	/* Zeroing first byte of an output data */
    158 	*(output->data) = '\0';
    159 
    160 	while (Strtok(readinput, &parseinput, '\n') > 0) {
    161 		if (*(parseinput.data) == '@' && *(parseinput.data + 1) != '@') {
    162 			++parseinput.data;
    163 			if (*(parseinput.data) == '#'); /* comment */
    164 			else if (*(parseinput.data) == '<') { /* do something special */
    165 				++parseinput.data;
    166 				if (!strncmp(parseinput.data, "###", 3)) {
    167 					String function;
    168 					strncat((*output).data, "%",
    169 							MIN(BUFFER_SIZE - ((*output).len)++, 1));
    170 					function = $(function);
    171 					strncat((*output).data, function.data,
    172 							MIN(BUFFER_SIZE - (*output).len, function.len));
    173 					(*output).len += function.len;
    174 					strncat((*output).data, "();%",
    175 							MIN(BUFFER_SIZE - ((*output).len)++, 4));
    176 					(*output).len += 3;
    177 				} else {
    178 					write(outputfd, "\n", 1);
    179 					write(outputfd, parseinput.data, parseinput.len - 2);
    180 				}
    181 				--parseinput.data;
    182 			} else { /* variable */
    183 				String tok;
    184 				if (Strtok(parseinput, &tok, '=') <= 0) {
    185 					/* TODO: return syntax error */;
    186 				}
    187 				strncpy(bufptr, (idata + (parseinput.data - idata)),
    188 						MIN(tok.len - 1, 8192 - (bufptr - buffer)));
    189 				vs[vss].name.data = bufptr;
    190 				vs[vss].name.len = tok.len - 1;
    191 				bufptr += tok.len - 1;
    192 				strncpy(bufptr, (idata + (parseinput.data - idata)) + (tok.len + 1),
    193 						MIN(parseinput.len - (tok.len + 1) - 1, 8192 - (bufptr - buffer)));
    194 				vs[vss].value.data = bufptr;
    195 				vs[vss].value.len = parseinput.len - (tok.len + 1) - 1;
    196 				bufptr += parseinput.len - (tok.len + 1) - 1;
    197 				vs[vss].name = Strtrim(vs[vss].name);
    198 				vs[vss].value = Strtrim(vs[vss].value);
    199 				++vss;
    200 			}
    201 			--parseinput.data;
    202 		} else {
    203 			strncat((*output).data, readinput.data,
    204 					MIN(BUFFER_SIZE - (*output).len, parseinput.len));
    205 			(*output).len += parseinput.len;
    206 		}
    207 		readinput.data += parseinput.len;
    208 		readinput.len -= parseinput.len;
    209 	}
    210 }
    211 
    212 static void
    213 usage(void)
    214 {
    215 	die("usage: %s [-v] [-o OUTPUT] INPUT [TEMPLATE]", argv0);
    216 }
    217 
    218 static void
    219 template(int outputfd, String templatename)
    220 {
    221 	size_t vsscopy, viter;
    222 	char tname[sizeof TEMPLATEDIR + 256];
    223 	char input_buf[BUFFER_SIZE];
    224 	int fd;
    225 	/* String function = $(function); -- unused, maybe will for later? */
    226 	String fun_iden = Striden(templatename);
    227 	String input = {
    228 		.data = input_buf,
    229 		.len = 0
    230 	};
    231 
    232 	/* Saving stack size */
    233 	vsscopy = vss;
    234 	/* Declaring function name as templatename */
    235 	/* DECLVAR_S(function, fun_iden); TODO: recursive templates handling */
    236 
    237 	strcpy(tname, TEMPLATEDIR);
    238 	strncpy(tname + (sizeof TEMPLATEDIR - 1), templatename.data, MIN(templatename.len + 1, 256));
    239 
    240 	/* Opening template */
    241 	if ((fd = open(tname, O_RDONLY)) < 0)
    242 		die("open (template '%s'):", tname);
    243 
    244 	/* Preprocessing */
    245 	preprocess(outputfd, fd, &input);
    246 
    247 	write(outputfd, "void ", 5);
    248 	write(outputfd, fun_iden.data, fun_iden.len);
    249 	write(outputfd, "(void) {\n", 9);
    250 
    251 	/* Declaring variables */
    252 	for (viter = vsscopy; viter < vss; ++viter)
    253 		dprintf(outputfd, "DECLVAR(%.*s, \"%.*s\"); ",
    254 				(int)vs[viter].name.len,  vs[viter].name.data,
    255 				(int)vs[viter].value.len, vs[viter].value.data);
    256 
    257 	/* Generating C code */
    258 	generateC(outputfd, input);
    259 	write(outputfd, "}\n", 2);
    260 
    261 	/* Restoring stack size */
    262 	vss = vsscopy;
    263 
    264 	/* Closing template */
    265 	close(fd);
    266 }
    267 
    268 /* Main */
    269 int
    270 main(int argc, char *argv[])
    271 {
    272 	/* Variables: */
    273 	/* File names and file descriptors */
    274 	char *inputfn = NULL, *outputfn = NULL;
    275 	int inputfd, outputfd;
    276 	size_t viter;
    277 
    278 	/* Input data */
    279 	char input_buf[BUFFER_SIZE];
    280 
    281 	String input = {
    282 		.data = input_buf,
    283 		.len = 0
    284 	}, caller;
    285 
    286 	/* Options parsing */
    287 	ARGBEGIN {
    288 	case 'o':
    289 		outputfn = ARGF(); break;
    290 	case 'v':
    291 		die("compile from stacinhtml-"VERSION); break;
    292 	default:
    293 		  usage(); break;
    294 	} ARGEND
    295 
    296 	/* Argument parsing */
    297 	if (argc == 1)
    298 		inputfn = argv[0];
    299 	else
    300 		usage();
    301 
    302 	if (outputfn == NULL) /* TODO */
    303 		die("output not specified (needed in current stage)");
    304 
    305 	/* Setting variable stack size to 0 */
    306 	vss = 0;
    307 	bufptr = buffer;
    308 
    309 	/* Declaring a few variables */
    310 	DECLVAR(title, outputfn);
    311 	DECLVAR(template, "basic.stac");
    312 
    313 	/* Opening an input */
    314 	if ((inputfd = open(inputfn, O_RDONLY)) < 0)
    315 		die("open (input):");
    316 
    317 	/* Opening an output */
    318 	if ((outputfd = open(outputfn, O_WRONLY | O_CREAT, 0644)) < 0)
    319 		die("open (output):");
    320 
    321 	/* On the beginning, we are going to include assemble.h */
    322 	write(outputfd, "#include <assemble.h>\n", 22);
    323 
    324 	/* Actual preprocessing */
    325 	preprocess(outputfd, inputfd, &input);
    326 
    327 	/* Closing an input */
    328 	close(inputfd);
    329 
    330 	/* Writing beginning of content() */
    331 	write(outputfd, "void content(void) {", 20);
    332 
    333 	/* And after that, generating C code to output */
    334 	generateC(outputfd, input);
    335 
    336 	/* After this, closing content() */
    337 	write(outputfd, "\n}", 2);
    338 
    339 	/* And finally adding templates */
    340 	DECLVAR(function, "content");
    341 	template(outputfd, getVariableByName("template")->value);
    342 	STACKPOP();
    343 
    344 	/* main() beginning */
    345 	write(outputfd, "\nint main(void) {\n\t", 19);
    346 
    347 	/* Declaring variables */
    348 	for (viter = 0; viter < vss; ++viter)
    349 		dprintf(outputfd, "DECLVAR(%.*s, \"%.*s\"); ",
    350 				(int)vs[viter].name.len,  vs[viter].name.data,
    351 				(int)vs[viter].value.len, vs[viter].value.data);
    352 
    353 	/* calling function */
    354 	caller = Striden($(template));
    355 	write(outputfd, caller.data, caller.len);
    356 
    357 	/* main() ending */
    358 	write(outputfd, "();\n}\n", 6);
    359 
    360 	/* Closing an output */
    361 	close(outputfd);
    362 
    363 	return 0;
    364 }