Static template generator for C
git clone git://soophie.de/ductape
1
CompileFlags:2
Add:3
- -Wall4
- -Wextra5
- -Werror6
- -pedantic7
- -I/root/git/buf/src
1
CC=cc2
CFLAGS=-Wall -Wextra -Werror -pedantic1
CC := cc2
CFLAGS := -Wall -Wextra -Werror -pedantic3
CFLAGS_RELEASE := -O2 -DNDEBUG4
CFLAGS_DEBUG := -g3 -O05
CCHECK=-I. -x c - -fsyntax-only4
TARGET=ductape6
# NOTE: exposes header impls into dynamically linked modules7
LDFLAGS :=8
LDLIBS :=9
DEPS := \10
-I$(HOME)/git/buf/src11
SOURCE := \12
src/main.c13
TARGET := ductape6
build: src/main.c7
$(CC) -o $(TARGET) $(CFLAGS) $(LIBS) $^15
all: build9
install: build10
cp $(TARGET) /usr/local/bin/11
cp src/ductape.h /usr/local/include/12
rm ./$(TARGET)17
build: $(SOURCE)18
$(CC) -o $(TARGET) $(CFLAGS) $(DEPS) $(LDFLAGS) $^ $(LDLIBS)20
build-release: $(SOURCE)21
$(CC) -o $(TARGET) $(CFLAGS) $(DEPS) $(CFLAGS_RELEASE) $(LDFLAGS) $^ $(LDLIBS)23
build-debug: $(SOURCE)24
$(CC) -o $(TARGET) $(CFLAGS) $(DEPS) $(CFLAGS_DEBUG) $(LDFLAGS) $^ $(LDLIBS)26
clean:27
rm -f ./$(TARGET)29
install: build-release30
cp $(TARGET) /usr/local/bin/31
cp src/ductape.h /usr/local/include/32
rm ./$(TARGET)34
check: src/ductape.h
14
// along with this program. If not, see <https://www.gnu.org/licenses/>.16
// @file ductape.h17
// @desc Static template generator for C19
#pragma once21
#include <stdio.h>20
#include <stdlib.h>21
#include <string.h>23
// @type duc_render_fn_t24
typedef int (*duc_render_fn_t)(void *, void *);26
// @struct duc_templref_t27
typedef struct {28
duc_render_fn_t fn;29
void *data;30
const char *dl_path;31
} duc_templref_t;33
// @macro __DUC_TEMPLDEF_MACRO23
// @macro __DUC_TEMPLDEF_TYPE24
// @internal35
#define __DUC_TEMPLDEF_MACRO($name, $params) \25
// @desc Creates the type definitions for the template.26
// @param name - The template name.27
// @param param_list - The exposed template parameters.28
#define __DUC_TEMPLDEF_TYPE(name, param_list) \29
typedef struct { \37
$params \38
} $name##_t; \30
const char *__duc_unused; \31
param_list \32
} name##_t; \33
\40
typedef void (*$name##_fn_t)(void *, $name##_t *); \41
\42
int $name##_render(void *out, void *ctx) { \34
typedef void (*name##_fn_t)(void *, name##_t *);36
// @macro __DUC_TEMPLDEF_RENDER37
// @internal38
// @desc Creates the render implementation for the template.39
// @param name - The template name.40
#define __DUC_TEMPLDEF_RENDER(name) \41
int name##_render(void *out, void *ctx) { \42
char *path = getenv("DUC_BUILDPATH"); \43
if (path == NULL) { \44
path = "./"; \45
} \47
char dl_path[strlen(path) + strlen(__DUC_UID_##$name) + 4]; \48
snprintf(dl_path, sizeof(dl_path), "%s%s.so", path, __DUC_UID_##$name); \46
char dl_path[strlen(path) + strlen(__DUC_UID_##name) + 4]; \47
snprintf(dl_path, sizeof(dl_path), "%s%s.so", path, __DUC_UID_##name); \48
void *handle = dlopen(dl_path, RTLD_NOW); \49
if (!handle) { \50
fprintf(stderr, "%s\n", dlerror()); \51
return 1; \52
} \54
const size_t *hash = (size_t *) dlsym(handle, "__DUC_SIG_HASH_" #$name); \53
const size_t *hash = (size_t *) dlsym(handle, "__DUC_SIG_HASH_" #name); \54
if (!hash) { \55
fprintf(stderr, "Missing signature hash!\n"); \56
dlclose(handle); \57
return 1; \58
} \60
if (__DUC_SIG_HASH_##$name != *hash) { \61
fprintf(stderr, "Invalid signature hash! (%llx != %llx)\n", __DUC_SIG_HASH_##$name, *hash); \59
if (__DUC_SIG_HASH_##name != *hash) { \60
fprintf(stderr, "Invalid signature hash! (%lx != %lx)\n", __DUC_SIG_HASH_##name, *hash); \61
dlclose(handle); \62
return 1; \63
} \65
$name##_fn_t fn = ($name##_fn_t) dlsym(handle, "duc_" #$name); \64
void *sym = dlsym(handle, "duc_" #name); \65
char *err = dlerror(); \66
if (err) { \67
fprintf(stderr, "%s\n", err); \68
dlclose(handle); \69
return 1; \70
} \72
fn(out, ($name##_t *) ctx); \71
name##_fn_t fn = NULL; \72
memcpy(&fn, &sym, sizeof(fn)); \73
fn(out, (name##_t *) ctx); \74
dlclose(handle); \75
return 0; \76
}78
// @macro __DUC_TEMPLDEF_HEADER79
// @internal79
#define __DUC_TEMPLDEF_HEADER($name, $params) \80
__DUC_TEMPLDEF_MACRO($name, $params) \81
\82
void duc_##$name(void *out, $name##_t *ctx);80
// @desc Creates the header definition for the template. Used in generated .h file.81
// @param name - The template name.82
// @param param_list - The exposed template parameters.83
#define __DUC_TEMPLDEF_HEADER(name, param_list) \84
__DUC_TEMPLDEF_TYPE(name, param_list) \85
__DUC_TEMPLDEF_RENDER(name)87
// @macro DUC_TEMPLDEF85
#define DUC_TEMPLDEF($name, $params) \86
__DUC_TEMPLDEF_MACRO($name, $params) \88
// @desc Marks the beginning of a template definition. Used in generated .so file.89
// @param name - The template name.90
// @param param_list - The exposed template parameters.91
#define DUC_TEMPLDEF(name, param_list) \92
__DUC_TEMPLDEF_TYPE(name, param_list) \93
\88
void duc_##$name(void *out, $name##_t *ctx) {94
void duc_##name(void *out, name##_t *ctx) {96
// @macro DUC_ENDTEMPL97
// @desc Marks the end of a template definition.98
#define DUC_ENDTEMPL }93
// @macro DUC_TEMPLBIND94
#define DUC_TEMPLBIND($name, $data, $dl_path) \95
(duc_templref_t) { \96
.fn = $name##_render, \97
.data = $data, \98
.dl_path = $dl_path, \99
},101
// @macro DUC_INCLUDE102
#define DUC_INCLUDE($name) ctx->$name.fn(duc, ctx->$name.data, ctx->$name.dl_path);
1
// Copyright (C) 2026 Sophie Sitzler2
//3
// This program is free software: you can redistribute it and/or modify4
// it under the terms of the GNU General Public License as published by5
// the Free Software Foundation, either version 3 of the License, or6
// (at your option) any later version.7
//8
// This program is distributed in the hope that it will be useful,9
// but WITHOUT ANY WARRANTY; without even the implied warranty of10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11
// GNU General Public License for more details.12
//13
// You should have received a copy of the GNU General Public License14
// along with this program. If not, see <https://www.gnu.org/licenses/>.16
// @file main.c17
// @desc The ductape compiler.19
#include <stdio.h>20
#include <stdlib.h>21
#include <ctype.h>7
#define SO_STR_IMPL8
// @include <so/str.h>9
#include <so/str.h>23
#define BUF_IMPL24
// @include <buf.h>25
#include <buf.h>27
#define DUC_BUILDPATH "DUC_BUILDPATH"28
#define DUC_HEADERPATH "DUC_HEADERPATH"
102
}104
// @func duc_render89
duc_err_t duc_render(duc_mode_t mode, str_t *out, char *content, char **uid) {105
duc_err_t duc_render(duc_mode_t mode, buf_t *out, char *content, char **uid) {106
size_t len = strlen(content);107
size_t set = 0;108
size_t is_code = 0;109
size_t is_templ = 0;110
size_t is_templ_begin = 0;111
size_t hash = 0;112
for (size_t i = 0; i < len; i++) {113
if (content[i] == *DUC_SYM_SPLIT) {
120
for (size_t j = 0; j < i - set; j++) {121
if (strncmp(content + set + j, DUC_SYM_TEMPLDEF, strlen(DUC_SYM_TEMPLDEF)) == 0) {122
is_templ = 1;123
is_templ_begin = 1;124
hash = duc_sig_hash(content + set, i - set);125
bool is_arg = false;126
bool is_uid = false;
147
}148
}149
if (mode == DUC_MODE_MODULE) {132
str_append_len(out, content + set, i - set);133
str_append(out, "\n");150
buf_write_len(out, content + set, i - set);151
buf_write(out, "\n");152
if (is_templ_begin) {153
is_templ_begin = 0;154
buf_write(out, "(void) ctx;");155
}156
}157
if (mode == DUC_MODE_HEADER && is_templ == 1) {158
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);159
buf_t templdef = BUF_NULL;160
buf_write_len(&templdef, content + set, i - set);161
buf_write(&templdef, "\n");162
buf_repall(&templdef, DUC_SYM_TEMPLDEF, DUC_SYM_TEMPLDEF_HEADER);163
buf_concat(out, templdef);164
buf_free(&templdef);165
}166
}167
else {
178
hex_len = str_len - idx;179
}180
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);181
buf_t hex = BUF_NULL;182
buf_write_len(&hex, content + set + idx, hex_len);183
buf_repall(&hex, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);184
buf_to_hex(&hex);185
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");186
buf_write(out, DUC_SYM_WRITE "(\"");187
buf_concat(out, hex);188
buf_write(out, "\")\n");189
}168
str_free(&hex);190
buf_free(&hex);191
idx += hex_len;192
}193
}
195
set = i + 1;196
}197
}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);198
buf_t meta = BUF_NULL;199
buf_write(&meta, "#include <dlfcn.h>\n");200
buf_write(&meta, "#include <string.h>\n");201
buf_write(&meta, "#include <stdlib.h>\n");202
buf_write(&meta, "#include <ductape.h>\n");203
buf_writef(&meta, "const char " DUC_SYM_UID "%s[] = \"%s\";\n", *uid, *uid);204
buf_writef(&meta, "const size_t " DUC_SYM_SIG_HASH "%s = %ld;\n", *uid, hash);205
buf_concat(&meta, *out);206
buf_free(out);207
*out = BUF_NULL;208
buf_concat(out, meta);209
buf_free(&meta);210
if (is_code) {211
return DUC_ERR_UNCLOSED_CODE;212
}
224
hex_len = str_len - idx;225
}226
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);227
buf_t hex = BUF_NULL;228
buf_write_len(&hex, content + set + idx, hex_len);229
buf_repall(&hex, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);230
buf_to_hex(&hex);231
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");232
buf_write(out, DUC_SYM_WRITE "(\"");233
buf_concat(out, hex);234
buf_write(out, "\")\n");235
}212
str_free(&hex);236
buf_free(&hex);237
idx += hex_len;238
}239
}216
str_repall(out, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);240
buf_repall(out, DUC_SYM_SPLIT_ESCAPE, DUC_SYM_SPLIT);241
return DUC_ERR_OK;242
}
294
duc_error_str(err);295
return err;296
}273
str_t template = STR_NULL;297
buf_t template = BUF_NULL;298
err = duc_render(mode, &template, content, uid);299
free(content);300
if (err != DUC_ERR_OK || *uid == NULL) {277
str_free(&template);301
buf_free(&template);302
duc_error_str(err);303
return err;304
}
308
if (path == NULL) {309
path = "./";310
}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);311
buf_t filename = BUF_NULL;312
buf_writef(&filename, "%s%s.h", path, *uid);313
buf_terminate(&filename);314
duc_file_write((char *) template.bytes, template.len, (char *) filename.bytes);315
buf_free(&filename);316
buf_free(&template);317
return DUC_ERR_OK;318
}319
case DUC_MODE_MODULE: {295
err = duc_file_write(str_get(&template), str_len(&template), DUC_TMP_C_PATH);296
str_free(&template);320
err = duc_file_write((char *) template.bytes, template.len, DUC_TMP_C_PATH);321
buf_free(&template);322
char *lib_path = getenv(DUC_LIBPATH);323
if (lib_path == NULL) {324
lib_path = "";
344
if (path == NULL) {345
path = "./";346
}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);347
buf_t filename = BUF_NULL;348
buf_writef(&filename, "%s%s.so", path, *uid);349
buf_terminate(&filename);350
duc_file_write(binary, len, (char *) filename.bytes);351
buf_free(&filename);352
free(binary);353
return DUC_ERR_OK;354
}