Static template generator for C
git clone git://soophie.de/ductape
1
CC=cc2
CFLAGS=-Wall -Wextra -Werror -pedantic3
LIBS=4
TARGET=ctl5
build: src/*.c6
$(CC) -o ctl $(CLFAGS) $(LIBS) $^6
build: src/main.c7
$(CC) -o $(TARGET) $(CFLAGS) $(LIBS) $^9
install: build9
cp ctl /usr/local/bin/10
cp $(TARGET) /usr/local/bin/11
mkdir -p /usr/local/include/so12
cp src/ctl.h /usr/local/include/so/12
rm ./ctl13
rm ./$(TARGET)15
clean:15
rm ./ctl16
rm ./$(TARGET)
1
# ctl1
# so/ctl3
Static template generator for C5
## Installation7
```8
sudo make install9
```11
## Usage13
```html8
% #include "src/ctl_macro.h" %14
% #include "macro.h" %10
% CTL_TEMPLDEF(11
example,12
char *title;16
% TEMPLDEF(17
sample,18
char *message;19
) %15
<!DOCTYPE html>16
<html lang="en">17
<head>18
<title>% STRING(ctx->title) %</title>19
</head>20
</html>22
% CTL_ENDTEMPL %23
```21
<h1>% STRING($(message)) %</h1>22
<ul>23
% for (size_t i = 0; i < 10; i++) { %24
<li>% NUMBER(i) %</li>25
% } %26
</ul>25
```bash26
ctl -h example.html > ctl_example.h27
ctl -o example.html > ctl_example.so28
% ENDTEMPL %29
```31
```c31
#include <so/ctl.h>32
#include "ctl/ctl_example.h"32
#define SO_STR_IMPL33
#include <so/str.h>34
#define CTL_DL_PATH($name) "./ctl_" #$name ".so"35
#define CTL_OUT (str_t *) ctl->out36
char *__out = NULL;38
void __ctl_write(const char *str) {39
str_append(&__out, (char *) str);37
#define CTL_WRITE($str) { \38
str_append(CTL_OUT, $str); \39
}42
ctl_t __ctl = {43
.write = __ctl_write,44
};41
#define $($var) ctx->$var43
#define STRING CTL_WRITE46
HTTP_GET("/example", example_page)47
example_t example = {48
.title = title,45
#define NUMBER($num) { str_appendf(CTL_OUT, "%%d", $num); }46
```48
```c49
#include <stdio.h>51
#define SO_CTL_IMPL52
#include <so/ctl.h>53
#define SO_STR_IMPL54
#include <so/str.h>56
#include "../sample.h"58
int main(void) {59
str_t out = STR_NULL;60
ctl_t *ctl = ctl_init(&out);61
sample_t data = {62
.message = "It works!",63
};50
return ctl_serve(&example, example_render, CTL_DL_PATH(example));64
sample_render(ctl, &data, "./sample.so");65
printf("%s", str_get(&out));66
str_free(&out);67
ctl_cleanup(ctl);68
return 0;69
}70
```54
```c55
#define STRING(x) ctl->write(x);72
```bash73
ctl -h sample.html > sample.h74
ctl -o sample.html > sample.so75
```
1
#ifndef SO_CTL_H2
#define SO_CTL_H1
#ifndef _SO_CTL_H2
#define _SO_CTL_H4
#include <stdio.h>5
#include <stdlib.h>6
typedef void (*ctl_write_fn_t)(const char *);7
typedef struct ctl_t ctl_t;8
typedef struct {9
ctl_write_fn_t write;10
} ctl_t;9
struct ctl_t {10
void *out;11
};13
typedef int (*ctl_render_fn_t)(ctl_t *, void *, const char *);
18
const char *dl_path;19
} ctl_templref_t;20
#define __CTL_TEMPLDEF_MACRO($name, $params) \21
#define __TEMPLDEF_MACRO($name, $params) \22
typedef struct { \23
$params \24
} $name##_t; \
43
return 0; \44
}45
#define __CTL_TEMPLDEF_HEADER($name, $params) \46
__CTL_TEMPLDEF_MACRO($name, $params) \46
#define __TEMPLDEF_HEADER($name, $params) \47
__TEMPLDEF_MACRO($name, $params) \48
\49
void ctl_##$name(ctl_t *ctl, $name##_t *ctx);50
#define CTL_TEMPLDEF($name, $params) \51
__CTL_TEMPLDEF_MACRO($name, $params) \51
#define TEMPLDEF($name, $params) \52
__TEMPLDEF_MACRO($name, $params) \53
\54
void ctl_##$name(ctl_t *ctl, $name##_t *ctx) {55
#define CTL_ENDTEMPL }56
#define ENDTEMPL }57
#define CTL_TEMPLBIND($name, $data, $dl_path) \58
#define TEMPLBIND($name, $data, $dl_path) \59
(ctl_templref_t) { \60
.fn = $name##_render, \61
.data = $data, \62
.dl_path = $dl_path, \63
},64
#define CTL_WRITE($str) ctl->write($str);65
#define INCLUDE($name) ctx->$name.fn(ctl, ctx->$name.data, ctx->$name.dl_path);67
ctl_t *ctl_init(void *out);68
void ctl_cleanup(ctl_t *self);70
#ifdef SO_CTL_IMPL72
ctl_t *ctl_init(void *out) {73
ctl_t *self = (ctl_t *) malloc(sizeof(ctl_t));74
*self = (ctl_t) {75
.out = out,76
};77
return self;78
}66
#define CTL_INCLUDE($name) ctx->$name.fn(ctl, ctx->$name.data, ctx->$name.dl_path);80
void ctl_cleanup(ctl_t *self) {81
if (!self) return;82
free(self);83
}68
#endif /* SO_CTL_H */85
#endif // SO_CTL_IMPL86
#endif // _SO_CTL_H
2
#include <stdlib.h>3
#include <getopt.h>5
#define LIB_STR_IMPL6
#include <libstr.h>5
#define SO_STR_IMPL6
#include <so/str.h>8
#define CTL_TMP_C_PATH ".ctl_tmp.c"9
#define CTL_TMP_SO_PATH ".ctl_tmp.so"
15
CTL_ERR_NO_WRITE,16
CTL_ERR_UNCLOSED_CODE,17
CTL_ERR_NO_COMPILE,18
} ctl_error_t;18
} ctl_err_t;20
typedef enum {21
CTL_MODE_HEADER,22
CTL_MODE_MODULE,23
} ctl_mode_t;25
void ctl_error_str(ctl_error_t err) {25
void ctl_error_str(ctl_err_t err) {26
switch (err) {27
case CTL_ERR_OK:28
break;
44
}45
}47
char *str_replace_all_len(char *buffer, char *str, char *replacement, int len) {48
char *ret = NULL;49
int str_len = strlen(str);50
for (int i = 0; i < len; i++) {51
char *ptr = &buffer[i];52
if (strncmp(ptr, str, str_len) == 0) {53
str_append(&ret, replacement);54
i += str_len - 1;55
}56
else {57
str_append_len(&ret, &buffer[i], 1);58
}59
}60
return ret;61
}63
char *str_hex_len(char *str, int len) {64
const int HEX = 4;65
char *ret = calloc(HEX * len + 1, sizeof(char));66
for (int i = 0; i < len; i++) {67
unsigned char hex = str[i] & 0xff;68
snprintf(ret + HEX * i, HEX * len, "\\x%02x", hex);69
}70
ret[HEX * len] = '\0';71
return ret;72
}74
ctl_error_t ctl_render(ctl_mode_t mode, char **template, char *content) {47
ctl_err_t ctl_render(ctl_mode_t mode, str_t *template, char *content) {48
int len = strlen(content);49
int set = 0;50
int is_code = 0;78
int templ = 0;51
int is_templ = 0;52
str_append(template, "#include <dlfcn.h>\n");53
str_append(template, "#include <so/ctl.h>\n");54
for (int i = 0; i < len; i++) {
61
if (is_code) {62
for (int j = 0; j < i - set; j++) {63
if (strncmp(content + set + j, "TEMPLDEF", 8) == 0) {91
templ = 1;64
is_templ = 1;65
}66
if (strncmp(content + set + j, "ENDTEMPL", 8) == 0) {94
templ = 0;67
is_templ = 0;68
}69
}70
if (mode == CTL_MODE_MODULE) {71
str_append_len(template, content + set, i - set);72
str_append(template, "\n");73
}101
if (mode == CTL_MODE_HEADER && templ == 1) {102
templ = 0;103
char *templdef = NULL;74
if (mode == CTL_MODE_HEADER && is_templ == 1) {75
is_templ = 0;76
str_t templdef = STR_NULL;77
str_append_len(&templdef, content + set, i - set);78
str_append(&templdef, "\n");106
templdef = str_replace_all(templdef, "CTL_TEMPLDEF", "__CTL_TEMPLDEF_HEADER");107
str_append(template, templdef);108
free(templdef);79
str_repall(&templdef, "TEMPLDEF", "__TEMPLDEF_HEADER");80
str_concat(template, templdef);81
str_free(&templdef);82
}83
}84
else {
95
hex_len = str_len - idx;96
}97
rem_len -= hex_len;125
char *str = str_replace_all_len(content + set + idx, "%%", "%", hex_len);126
char *hex = str_hex_len(str, strlen(str));127
if (mode == CTL_MODE_MODULE && templ == 1) {128
str_append(template, "ctl->write(\"");129
str_append(template, hex);130
str_append(template, "\");\n");98
str_t hex = STR_NULL;99
str_append_len(&hex, content + set + idx, hex_len);100
str_repall(&hex, "%%", "%");101
str_to_hex(&hex);102
if (mode == CTL_MODE_MODULE && is_templ == 1) {103
str_append(template, "CTL_WRITE(\"");104
str_concat(template, hex);105
str_append(template, "\")\n");106
}132
free(str);133
free(hex);107
str_free(&hex);108
idx += hex_len;109
}110
}
129
hex_len = str_len - idx;130
}131
rem_len -= hex_len;158
char *str = str_replace_all_len(content + set + idx, "%%", "%", hex_len);159
char *hex = str_hex_len(str, strlen(str));160
if (mode == CTL_MODE_MODULE && templ == 1) {161
str_append(template, "ctl->write(\"");162
str_append(template, hex);163
str_append(template, "\");\n");132
str_t hex = STR_NULL;133
str_append_len(&hex, content + set + idx, hex_len);134
str_repall(&hex, "%%", "%");135
str_to_hex(&hex);136
if (mode == CTL_MODE_MODULE && is_templ == 1) {137
str_append(template, "CTL_WRITE(\"");138
str_concat(template, hex);139
str_append(template, "\")\n");140
}165
free(str);166
free(hex);141
str_free(&hex);142
idx += hex_len;143
}144
}170
*template = str_replace_all(*template, "%%", "%");145
str_repall(template, "%%", "%");146
return CTL_ERR_OK;147
}174
ctl_error_t ctl_file_read(char **content, const char *filename) {149
ctl_err_t ctl_file_read(char **content, const char *filename) {150
FILE *fp = fopen(filename, "r");151
if (fp == NULL) {152
return CTL_ERR_NO_FILE;
161
return CTL_ERR_OK;162
}189
ctl_error_t ctl_file_read_binary(char **binary, size_t *len, const char *filename) {164
ctl_err_t ctl_file_read_binary(char **binary, size_t *len, const char *filename) {165
FILE *fp = fopen(filename, "rb");166
if (fp == NULL) {167
return CTL_ERR_NO_FILE;
175
return CTL_ERR_OK;176
}203
ctl_error_t ctl_file_write(const char *content, const char *filename) {178
ctl_err_t ctl_file_write(const char *content, const char *filename) {179
FILE *fp = fopen(filename, "w");180
if (fp == NULL) {181
return CTL_ERR_NO_FILE;182
}208
if (fwrite(content, sizeof(char), strlen(content), fp) < 0) {183
size_t len = strlen(content);184
if (fwrite(content, sizeof(char), len, fp) != len) {185
return CTL_ERR_NO_WRITE;186
}187
fclose(fp);
189
}191
int main(int argc, char **argv) {216
ctl_error_t err;192
ctl_err_t err;193
opterr = 0;194
ctl_mode_t mode;195
char *path = NULL;
221
ctl_error_str(err);222
return err;223
}248
char *template = NULL;224
str_t template = STR_NULL;225
err = ctl_render(mode, &template, content);226
free(content);227
if (err != CTL_ERR_OK) {252
if (template != NULL) {253
free(template);254
}228
str_free(&template);229
ctl_error_str(err);230
return err;231
}232
if (mode == CTL_MODE_HEADER) {259
fprintf(stdout, "%s", template);260
free(template);233
fprintf(stdout, "%s", str_get(&template));234
str_free(&template);235
return CTL_ERR_OK;236
}237
if (mode == CTL_MODE_MODULE) {264
err = ctl_file_write(template, CTL_TMP_C_PATH);265
free(template);238
err = ctl_file_write(str_get(&template), CTL_TMP_C_PATH);239
str_free(&template);240
char cmd[500];241
snprintf(cmd, sizeof(cmd), "cc -shared -fPIC -o %s %s", CTL_TMP_SO_PATH, CTL_TMP_C_PATH);242
int rc = system(cmd);