Static template generator for C
git clone git://soophie.de/ductape
blob: 5530c41c8eeba41d535cfa1e3f2fce5758da619b (7337 bytes)[doc][blame][raw]
1
#include <stdio.h>2
#include <stdlib.h>3
#include <getopt.h>5
#define LIB_STR_IMPL6
#include <libstr.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_error_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) {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
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) {75
int len = strlen(content);76
int set = 0;77
int is_code = 0;78
int templ = 0;79
str_append(template, "#include <dlfcn.h>\n");80
str_append(template, "#include <so/ctl.h>\n");81
for (int i = 0; i < len; i++) {82
if (content[i] == '%') {83
// ignore escaped %84
if (i + 1 < len && content[i + 1] == '%') {85
i += 1;86
continue;87
}88
if (is_code) {89
for (int j = 0; j < i - set; j++) {90
if (strncmp(content + set + j, "TEMPLDEF", 8) == 0) {91
templ = 1;92
}93
if (strncmp(content + set + j, "ENDTEMPL", 8) == 0) {94
templ = 0;95
}96
}97
if (mode == CTL_MODE_MODULE) {98
str_append_len(template, content + set, i - set);99
str_append(template, "\n");100
}101
if (mode == CTL_MODE_HEADER && templ == 1) {102
templ = 0;103
char *templdef = NULL;104
str_append_len(&templdef, content + set, i - set);105
str_append(&templdef, "\n");106
templdef = str_replace_all(templdef, "CTL_TEMPLDEF", "__CTL_TEMPLDEF_HEADER");107
str_append(template, templdef);108
free(templdef);109
}110
}111
else {112
const int STRING_LIMIT = 4095;113
int str_len = i - set;114
int rem_len = str_len;115
int hex_len = 0;116
int idx = 0;117
while (rem_len > 0) {118
if (rem_len > STRING_LIMIT) {119
hex_len = STRING_LIMIT;120
}121
else {122
hex_len = str_len - idx;123
}124
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");131
}132
free(str);133
free(hex);134
idx += hex_len;135
}136
}137
is_code = !is_code;138
set = i + 1;139
}140
}141
if (is_code) {142
return CTL_ERR_UNCLOSED_CODE;143
}144
else {145
const int STRING_LIMIT = 4095;146
int str_len = len - set;147
int rem_len = str_len;148
int hex_len = 0;149
int idx = 0;150
while (rem_len > 0) {151
if (rem_len > STRING_LIMIT) {152
hex_len = STRING_LIMIT;153
}154
else {155
hex_len = str_len - idx;156
}157
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");164
}165
free(str);166
free(hex);167
idx += hex_len;168
}169
}170
*template = str_replace_all(*template, "%%", "%");171
return CTL_ERR_OK;172
}174
ctl_error_t ctl_file_read(char **content, const char *filename) {175
FILE *fp = fopen(filename, "r");176
if (fp == NULL) {177
return CTL_ERR_NO_FILE;178
}179
fseek(fp, 0, SEEK_END);180
int len = ftell(fp);181
fseek(fp, 0, SEEK_SET);182
*content = calloc(len + 1, sizeof(char));183
fread(*content, sizeof(char), len, fp);184
(*content)[len] = '\0';185
fclose(fp);186
return CTL_ERR_OK;187
}189
ctl_error_t ctl_file_read_binary(char **binary, size_t *len, const char *filename) {190
FILE *fp = fopen(filename, "rb");191
if (fp == NULL) {192
return CTL_ERR_NO_FILE;193
}194
fseek(fp, 0, SEEK_END);195
*len = ftell(fp);196
fseek(fp, 0, SEEK_SET);197
*binary = calloc(*len, sizeof(char));198
fread(*binary, sizeof(char), *len, fp);199
fclose(fp);200
return CTL_ERR_OK;201
}203
ctl_error_t ctl_file_write(const char *content, const char *filename) {204
FILE *fp = fopen(filename, "w");205
if (fp == NULL) {206
return CTL_ERR_NO_FILE;207
}208
if (fwrite(content, sizeof(char), strlen(content), fp) < 0) {209
return CTL_ERR_NO_WRITE;210
}211
fclose(fp);212
return CTL_ERR_OK;213
}215
int main(int argc, char **argv) {216
ctl_error_t err;217
opterr = 0;218
ctl_mode_t mode;219
char *path = NULL;220
int opt;221
while ((opt = getopt(argc, argv, "h:o:")) != -1) {222
switch (opt) {223
case 'h':224
mode = CTL_MODE_HEADER;225
path = optarg;226
break;227
case 'o':228
mode = CTL_MODE_MODULE;229
path = optarg;230
break;231
default:232
err = CTL_ERR_NO_ARGS;233
ctl_error_str(err);234
return err;235
}236
}237
if (path == NULL) {238
err = CTL_ERR_NO_ARGS;239
ctl_error_str(err);240
return err;241
}242
char *content = NULL;243
err = ctl_file_read(&content, path);244
if (err != CTL_ERR_OK) {245
ctl_error_str(err);246
return err;247
}248
char *template = NULL;249
err = ctl_render(mode, &template, content);250
free(content);251
if (err != CTL_ERR_OK) {252
if (template != NULL) {253
free(template);254
}255
ctl_error_str(err);256
return err;257
}258
if (mode == CTL_MODE_HEADER) {259
fprintf(stdout, "%s", template);260
free(template);261
return CTL_ERR_OK;262
}263
if (mode == CTL_MODE_MODULE) {264
err = ctl_file_write(template, CTL_TMP_C_PATH);265
free(template);266
char cmd[500];267
snprintf(cmd, sizeof(cmd), "cc -shared -fPIC -o %s %s", CTL_TMP_SO_PATH, CTL_TMP_C_PATH);268
int rc = system(cmd);269
remove(CTL_TMP_C_PATH);270
if (rc != 0) {271
err = CTL_ERR_NO_COMPILE;272
ctl_error_str(err);273
return err;274
}275
char *binary = NULL;276
size_t len = 0;277
err = ctl_file_read_binary(&binary, &len, CTL_TMP_SO_PATH);278
remove(CTL_TMP_SO_PATH);279
if (err != CTL_ERR_OK) {280
ctl_error_str(err);281
return err;282
}283
fwrite(binary, sizeof(char), len, stdout);284
free(binary);285
return CTL_ERR_OK;286
}287
}