Static template generator for C
git clone git://soophie.de/ductape
blob: 4e6d5386cabde0c6398df6013f0dad96999c4d11 (6675 bytes)[doc][blame][raw]
1
#include <stdio.h>2
#include <stdlib.h>3
#include <getopt.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"11
typedef enum {12
CTL_ERR_OK,13
CTL_ERR_NO_ARGS,14
CTL_ERR_NO_FILE,15
CTL_ERR_NO_WRITE,16
CTL_ERR_UNCLOSED_CODE,17
CTL_ERR_NO_COMPILE,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_err_t err) {26
switch (err) {27
case CTL_ERR_OK:28
break;29
case CTL_ERR_NO_ARGS:30
fprintf(stderr, "Usage: ctl -h <templ_path> (HEADER), ctl -o <templ_path> (MODULE)\n");31
break;32
case CTL_ERR_NO_FILE:33
fprintf(stderr, "Error: No file found!\n");34
break;35
case CTL_ERR_NO_WRITE:36
fprintf(stderr, "Error: Can't write to file!\n");37
break;38
case CTL_ERR_UNCLOSED_CODE:39
fprintf(stderr, "Error: Unclosed code block!\n");40
break;41
case CTL_ERR_NO_COMPILE:42
fprintf(stderr, "Error: Failed to compile!\n");43
break;44
}45
}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;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++) {55
if (content[i] == '%') {56
// ignore escaped %57
if (i + 1 < len && content[i + 1] == '%') {58
i += 1;59
continue;60
}61
if (is_code) {62
for (int j = 0; j < i - set; j++) {63
if (strncmp(content + set + j, "TEMPLDEF", 8) == 0) {64
is_templ = 1;65
}66
if (strncmp(content + set + j, "ENDTEMPL", 8) == 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
}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");79
str_repall(&templdef, "TEMPLDEF", "__TEMPLDEF_HEADER");80
str_concat(template, templdef);81
str_free(&templdef);82
}83
}84
else {85
const int STRING_LIMIT = 4095;86
int str_len = i - set;87
int rem_len = str_len;88
int hex_len = 0;89
int idx = 0;90
while (rem_len > 0) {91
if (rem_len > STRING_LIMIT) {92
hex_len = STRING_LIMIT;93
}94
else {95
hex_len = str_len - idx;96
}97
rem_len -= hex_len;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
}107
str_free(&hex);108
idx += hex_len;109
}110
}111
is_code = !is_code;112
set = i + 1;113
}114
}115
if (is_code) {116
return CTL_ERR_UNCLOSED_CODE;117
}118
else {119
const int STRING_LIMIT = 4095;120
int str_len = len - set;121
int rem_len = str_len;122
int hex_len = 0;123
int idx = 0;124
while (rem_len > 0) {125
if (rem_len > STRING_LIMIT) {126
hex_len = STRING_LIMIT;127
}128
else {129
hex_len = str_len - idx;130
}131
rem_len -= hex_len;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
}141
str_free(&hex);142
idx += hex_len;143
}144
}145
str_repall(template, "%%", "%");146
return CTL_ERR_OK;147
}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;153
}154
fseek(fp, 0, SEEK_END);155
int len = ftell(fp);156
fseek(fp, 0, SEEK_SET);157
*content = calloc(len + 1, sizeof(char));158
fread(*content, sizeof(char), len, fp);159
(*content)[len] = '\0';160
fclose(fp);161
return CTL_ERR_OK;162
}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;168
}169
fseek(fp, 0, SEEK_END);170
*len = ftell(fp);171
fseek(fp, 0, SEEK_SET);172
*binary = calloc(*len, sizeof(char));173
fread(*binary, sizeof(char), *len, fp);174
fclose(fp);175
return CTL_ERR_OK;176
}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
}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);188
return CTL_ERR_OK;189
}191
int main(int argc, char **argv) {192
ctl_err_t err;193
opterr = 0;194
ctl_mode_t mode;195
char *path = NULL;196
int opt;197
while ((opt = getopt(argc, argv, "h:o:")) != -1) {198
switch (opt) {199
case 'h':200
mode = CTL_MODE_HEADER;201
path = optarg;202
break;203
case 'o':204
mode = CTL_MODE_MODULE;205
path = optarg;206
break;207
default:208
err = CTL_ERR_NO_ARGS;209
ctl_error_str(err);210
return err;211
}212
}213
if (path == NULL) {214
err = CTL_ERR_NO_ARGS;215
ctl_error_str(err);216
return err;217
}218
char *content = NULL;219
err = ctl_file_read(&content, path);220
if (err != CTL_ERR_OK) {221
ctl_error_str(err);222
return err;223
}224
str_t template = STR_NULL;225
err = ctl_render(mode, &template, content);226
free(content);227
if (err != CTL_ERR_OK) {228
str_free(&template);229
ctl_error_str(err);230
return err;231
}232
if (mode == CTL_MODE_HEADER) {233
fprintf(stdout, "%s", str_get(&template));234
str_free(&template);235
return CTL_ERR_OK;236
}237
if (mode == CTL_MODE_MODULE) {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);243
remove(CTL_TMP_C_PATH);244
if (rc != 0) {245
err = CTL_ERR_NO_COMPILE;246
ctl_error_str(err);247
return err;248
}249
char *binary = NULL;250
size_t len = 0;251
err = ctl_file_read_binary(&binary, &len, CTL_TMP_SO_PATH);252
remove(CTL_TMP_SO_PATH);253
if (err != CTL_ERR_OK) {254
ctl_error_str(err);255
return err;256
}257
fwrite(binary, sizeof(char), len, stdout);258
free(binary);259
return CTL_ERR_OK;260
}261
}