Static template generator for C
git clone git://soophie.de/ductape
blob: 4f3eb426cb558b6a17a20bf5ba10bc8af2410721 (10146 bytes)[doc][blame][raw]
1
// @file main.c3
#include <stdio.h>4
#include <stdlib.h>5
#include <ctype.h>7
#define SO_STR_IMPL8
// @include <so/str.h>9
#include <so/str.h>11
#define DUC_BUILDPATH "DUC_BUILDPATH"12
#define DUC_HEADERPATH "DUC_HEADERPATH"14
#define DUC_SYM_SIG_HASH "__DUC_SIG_HASH_"15
#define DUC_SYM_UID "__DUC_UID_"16
#define DUC_SYM_SPLIT "%"17
#define DUC_SYM_SPLIT_ESCAPE DUC_SYM_SPLIT DUC_SYM_SPLIT18
#define DUC_SYM_WRITE "DUC_WRITE"19
#define DUC_SYM_TEMPLDEF "TEMPLDEF"20
#define DUC_SYM_ENDTEMPL "ENDTEMPL"21
#define DUC_SYM_TEMPLDEF_HEADER "__TEMPLDEF_HEADER"23
#define DUC_TMP_C_PATH ".duc_tmp.c"24
#define DUC_TMP_SO_PATH ".duc_tmp.so"26
#define DUC_CMD_COMPILE "cc -shared -fPIC -o %s %s"28
// @enum duc_err_t29
typedef enum {30
DUC_ERR_OK,31
DUC_ERR_NO_ARGS,32
DUC_ERR_NO_FILE,33
DUC_ERR_NO_WRITE,34
DUC_ERR_UNCLOSED_CODE,35
DUC_ERR_NO_COMPILE,36
__DUC_ERR__COUNT,37
} duc_err_t;39
// @enum duc_mode_t40
typedef enum {41
DUC_MODE_HEADER,42
DUC_MODE_MODULE,43
__DUC_MODE__COUNT,44
} duc_mode_t;46
// @func duc_error_str47
void duc_error_str(duc_err_t err) {48
switch (err) {49
case DUC_ERR_OK:50
break;51
case DUC_ERR_NO_ARGS:52
fprintf(stderr, "Usage: ductape <files...>\n");53
break;54
case DUC_ERR_NO_FILE:55
fprintf(stderr, "Error: No file found!\n");56
break;57
case DUC_ERR_NO_WRITE:58
fprintf(stderr, "Error: Can't write to file!\n");59
break;60
case DUC_ERR_UNCLOSED_CODE:61
fprintf(stderr, "Error: Unclosed code block!\n");62
break;63
case DUC_ERR_NO_COMPILE:64
fprintf(stderr, "Error: Failed to compile!\n");65
break;66
case __DUC_ERR__COUNT:67
break;68
}69
}71
// @func duc_sig_hash72
size_t duc_sig_hash(char *str, size_t len) {73
size_t hash = 5381;74
size_t i = 0;75
while (i < len) {76
unsigned char c = (unsigned) str[i];77
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {78
i++;79
continue;80
}81
hash = ((hash << 5) + hash) + c;82
i++;83
}84
return hash;85
}87
// @func duc_render88
duc_err_t duc_render(duc_mode_t mode, str_t *out, char *content, char **uid) {89
size_t len = strlen(content);90
size_t set = 0;91
size_t is_code = 0;92
size_t is_templ = 0;93
size_t hash = 0;94
for (size_t i = 0; i < len; i++) {95
if (content[i] == *DUC_SYM_SPLIT) {96
// ignore escaped DUC_SYM_SPLIT97
if (i + 1 < len && content[i + 1] == *DUC_SYM_SPLIT) {98
i += 1;99
continue;100
}101
if (is_code) {102
for (size_t j = 0; j < i - set; j++) {103
if (strncmp(content + set + j, DUC_SYM_TEMPLDEF, strlen(DUC_SYM_TEMPLDEF)) == 0) {104
is_templ = 1;105
hash = duc_sig_hash(content + set, i - set);106
bool is_arg = false;107
bool is_uid = false;108
size_t uid_set = 0;109
for (size_t k = j; k < i - set; k++) {110
char c = content[set + k];111
if (c == '(') {112
is_arg = true;113
}114
if (is_arg && isalpha(c)) {115
is_uid = true;116
is_arg = false;117
uid_set = k;118
}119
if (is_uid && (!isalpha(c) && !isdigit(c) && c != '_')) {120
is_uid = false;121
*uid = strndup(content + set + uid_set, k - uid_set);122
break;123
}124
}125
}126
if (strncmp(content + set + j, DUC_SYM_ENDTEMPL, strlen(DUC_SYM_ENDTEMPL)) == 0) {127
is_templ = 0;128
}129
}130
if (mode == DUC_MODE_MODULE) {131
str_append_len(out, content + set, i - set);132
str_append(out, "\n");133
}134
if (mode == DUC_MODE_HEADER && is_templ == 1) {135
is_templ = 0;136
str_t templdef = STR_NULL;137
str_append_len(&templdef, content + set, i - set);138
str_append(&templdef, "\n");139
str_repall(&templdef, DUC_SYM_TEMPLDEF, DUC_SYM_TEMPLDEF_HEADER);140
str_concat(out, templdef);141
str_free(&templdef);142
}143
}144
else {145
const size_t STRING_LIMIT = 4095;146
size_t str_len = i - set;147
size_t rem_len = str_len;148
size_t hex_len = 0;149
size_t 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
str_t hex = STR_NULL;159
str_append_len(&hex, content + set + idx, hex_len);160
str_repall(&hex, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);161
str_to_hex(&hex);162
if (mode == DUC_MODE_MODULE && is_templ == 1) {163
str_append(out, DUC_SYM_WRITE "(\"");164
str_concat(out, hex);165
str_append(out, "\")\n");166
}167
str_free(&hex);168
idx += hex_len;169
}170
}171
is_code = !is_code;172
set = i + 1;173
}174
}175
str_t meta = STR_NULL;176
str_append(&meta, "#include <dlfcn.h>\n");177
str_append(&meta, "#include <ductape.h>\n");178
str_appendf(&meta, "const char " DUC_SYM_UID "%s[] = \"%s\";\n", *uid, *uid);179
str_appendf(&meta, "const size_t " DUC_SYM_SIG_HASH "%s = %ld;\n", *uid, hash);180
str_concat(&meta, *out);181
str_free(out);182
*out = STR_NULL;183
str_concat(out, meta);184
str_free(&meta);185
if (is_code) {186
return DUC_ERR_UNCLOSED_CODE;187
}188
else {189
const size_t STRING_LIMIT = 4095;190
size_t str_len = len - set;191
size_t rem_len = str_len;192
size_t hex_len = 0;193
size_t idx = 0;194
while (rem_len > 0) {195
if (rem_len > STRING_LIMIT) {196
hex_len = STRING_LIMIT;197
}198
else {199
hex_len = str_len - idx;200
}201
rem_len -= hex_len;202
str_t hex = STR_NULL;203
str_append_len(&hex, content + set + idx, hex_len);204
str_repall(&hex, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);205
str_to_hex(&hex);206
if (mode == DUC_MODE_MODULE && is_templ == 1) {207
str_append(out, DUC_SYM_WRITE "(\"");208
str_concat(out, hex);209
str_append(out, "\")\n");210
}211
str_free(&hex);212
idx += hex_len;213
}214
}215
str_repall(out, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);216
return DUC_ERR_OK;217
}219
// @func duc_file_read220
duc_err_t duc_file_read(char **content, const char *filename) {221
FILE *fp = fopen(filename, "r");222
if (fp == NULL) {223
return DUC_ERR_NO_FILE;224
}225
fseek(fp, 0, SEEK_END);226
size_t len = ftell(fp);227
fseek(fp, 0, SEEK_SET);228
*content = calloc(len + 1, sizeof(char));229
fread(*content, sizeof(char), len, fp);230
(*content)[len] = '\0';231
fclose(fp);232
return DUC_ERR_OK;233
}235
// @func duc_file_read_binary236
duc_err_t duc_file_read_binary(char **binary, size_t *len, const char *filename) {237
FILE *fp = fopen(filename, "rb");238
if (fp == NULL) {239
return DUC_ERR_NO_FILE;240
}241
fseek(fp, 0, SEEK_END);242
*len = ftell(fp);243
fseek(fp, 0, SEEK_SET);244
*binary = calloc(*len, sizeof(char));245
fread(*binary, sizeof(char), *len, fp);246
fclose(fp);247
return DUC_ERR_OK;248
}250
// @func duc_file_write251
duc_err_t duc_file_write(const char *content, size_t len, const char *filename) {252
FILE *fp = fopen(filename, "w");253
if (fp == NULL) {254
return DUC_ERR_NO_FILE;255
}256
if (fwrite(content, sizeof(char), len, fp) != len) {257
return DUC_ERR_NO_WRITE;258
}259
fclose(fp);260
return DUC_ERR_OK;261
}263
// @func duc_compile_file264
duc_err_t duc_compile_file(duc_mode_t mode, char *filepath, char **uid) {265
duc_err_t err;266
char *content = NULL;267
err = duc_file_read(&content, filepath);268
if (err != DUC_ERR_OK) {269
duc_error_str(err);270
return err;271
}272
str_t template = STR_NULL;273
err = duc_render(mode, &template, content, uid);274
free(content);275
if (err != DUC_ERR_OK || *uid == NULL) {276
str_free(&template);277
duc_error_str(err);278
return err;279
}280
switch (mode) {281
case DUC_MODE_HEADER: {282
char *path = getenv(DUC_HEADERPATH);283
if (path == NULL) {284
path = "./";285
}286
str_t filename = STR_NULL;287
str_appendf(&filename, "%s%s.h", path, *uid);288
duc_file_write((char *) str_get(&template), str_len(&template), (char *) str_get(&filename));289
str_free(&filename);290
str_free(&template);291
return DUC_ERR_OK;292
}293
case DUC_MODE_MODULE: {294
err = duc_file_write(str_get(&template), str_len(&template), DUC_TMP_C_PATH);295
str_free(&template);296
char cmd[500];297
snprintf(cmd, sizeof(cmd), DUC_CMD_COMPILE, DUC_TMP_SO_PATH, DUC_TMP_C_PATH);298
int rc = system(cmd);299
remove(DUC_TMP_C_PATH);300
if (rc != 0) {301
err = DUC_ERR_NO_COMPILE;302
duc_error_str(err);303
return err;304
}305
char *binary = NULL;306
size_t len = 0;307
err = duc_file_read_binary(&binary, &len, DUC_TMP_SO_PATH);308
remove(DUC_TMP_SO_PATH);309
if (err != DUC_ERR_OK) {310
duc_error_str(err);311
return err;312
}313
char *path = getenv(DUC_BUILDPATH);314
if (path == NULL) {315
path = "./";316
}317
str_t filename = STR_NULL;318
str_appendf(&filename, "%s%s.so", path, *uid);319
duc_file_write(binary, len, (char *) str_get(&filename));320
str_free(&filename);321
free(binary);322
return DUC_ERR_OK;323
}324
case __DUC_MODE__COUNT:325
break;326
}327
return DUC_ERR_OK;328
}330
// @func main331
int main(int argc, char **argv) {332
if (argc <= 1) {333
return DUC_ERR_NO_ARGS;334
}335
char *headerpath = getenv(DUC_HEADERPATH);336
char *buildpath = getenv(DUC_BUILDPATH);337
fprintf(stdout, "[ductape] Compiling templates...\n ." DUC_HEADERPATH " = '%s'\n ." DUC_BUILDPATH " = '%s'\n", headerpath ? headerpath : "./", buildpath ? buildpath : "./");338
duc_err_t err = DUC_ERR_OK;339
for (size_t i = 1; i < (size_t) argc; i++) {340
char *filepath = argv[i];341
duc_err_t header_err = DUC_ERR_OK;342
char *uid = NULL;343
if ((header_err = duc_compile_file(DUC_MODE_HEADER, filepath, &uid)) != DUC_ERR_OK) {344
err = header_err;345
}346
if (uid) {347
free(uid);348
}349
uid = NULL;350
duc_err_t module_err = DUC_ERR_OK;351
if ((module_err = duc_compile_file(DUC_MODE_MODULE, filepath, &uid)) != DUC_ERR_OK) {352
err = module_err;353
}354
fprintf(stdout, "-- compile '%s' | %s [%s]\n", uid, filepath, header_err == DUC_ERR_OK && module_err == DUC_ERR_OK ? "OK" : "FAILED");355
if (uid) {356
free(uid);357
}358
}359
return err;360
}