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