Static template generator for C
git clone git://soophie.de/ductape
1
CC=cc2
CFLAGS=-Wall -Wextra -Werror -pedantic3
LIBS=5
build: src/*.c6
$(CC) -o ctl $(CLFAGS) $(LIBS) $^8
install: build9
cp ctl /usr/local/bin/10
rm ./ctl12
clean:13
rm ./ctl
1
# ctl3
Static template generator for C
1
#include <stdio.h>2
#include <stdlib.h>3
#include <string.h>5
#define LIB_STR_IMPL6
#include <libstr.h>8
typedef enum {9
CTL_ERR_OK,10
CTL_ERR_NO_ARGS,11
CTL_ERR_NO_FILE,12
CTL_ERR_UNCLOSED_CODE,13
} ctl_error_t;15
void ctl_error_str(ctl_error_t err) {16
switch (err) {17
case CTL_ERR_OK:18
break;19
case CTL_ERR_NO_ARGS:20
printf("Usage: ctl <filename>\n");21
break;22
case CTL_ERR_NO_FILE:23
printf("Error: No file found!\n");24
break;25
case CTL_ERR_UNCLOSED_CODE:26
printf("Error: Unclosed code block!\n");27
break;28
}29
}31
char *str_replace_all_len(char *buffer, char *str, char *replacement, int len) {32
char *ret = NULL;33
int str_len = strlen(str);34
for (int i = 0; i < len; i++) {35
char *ptr = &buffer[i];36
if (strncmp(ptr, str, str_len) == 0) {37
str_append(&ret, replacement);38
i += str_len - 1;39
}40
else {41
str_append_len(&ret, &buffer[i], 1);42
}43
}44
return ret;45
}47
char *str_hex_len(char *str, int len) {48
const int HEX = 4;49
char *ret = calloc(HEX * len + 1, sizeof(char));50
for (int i = 0; i < len; i++) {51
unsigned char hex = str[i] & 0xff;52
snprintf(ret + HEX * i, HEX * len, "\\x%02x", hex);53
}54
ret[HEX * len] = '\0';55
return ret;56
}58
ctl_error_t ctl_render(char **template, char *content) {59
int len = strlen(content);60
int set = 0;61
int is_code = 0;62
str_append(template, "{\n");63
for (int i = 0; i < len; i++) {64
if (content[i] == '%') {65
// ignore escaped %66
if (i + 1 < len && content[i + 1] == '%') {67
i += 1;68
continue;69
}70
if (is_code) {71
str_append_len(template, content + set, i - set);72
str_append(template, "\n");73
}74
else {75
const int STRING_LIMIT = 4095;76
int str_len = i - set;77
int rem_len = str_len;78
int hex_len = 0;79
int idx = 0;80
while (rem_len > 0) {81
if (rem_len > STRING_LIMIT) {82
hex_len = STRING_LIMIT;83
}84
else {85
hex_len = str_len - idx;86
}87
rem_len -= hex_len;88
char *str = str_replace_all_len(content + set + idx, "%%", "%", hex_len);89
char *hex = str_hex_len(str, strlen(str));90
str_append(template, "CTL_OUT(ctl_out, \"");91
str_append(template, hex);92
str_append(template, "\");\n");93
free(str);94
free(hex);95
idx += hex_len;96
}97
}98
is_code = !is_code;99
set = i + 1;100
}101
}102
if (is_code) {103
return CTL_ERR_UNCLOSED_CODE;104
}105
else {106
const int STRING_LIMIT = 4095;107
int str_len = len - set;108
int rem_len = str_len;109
int hex_len = 0;110
int idx = 0;111
while (rem_len > 0) {112
if (rem_len > STRING_LIMIT) {113
hex_len = STRING_LIMIT;114
}115
else {116
hex_len = str_len - idx;117
}118
rem_len -= hex_len;119
char *str = str_replace_all_len(content + set + idx, "%%", "%", hex_len);120
char *hex = str_hex_len(str, strlen(str));121
str_append(template, "CTL_OUT(ctl_out, \"");122
str_append(template, hex);123
str_append(template, "\");\n");124
free(str);125
free(hex);126
idx += hex_len;127
}128
}129
str_append(template, "}\n");130
*template = str_replace_all(*template, "%%", "%");131
return CTL_ERR_OK;132
}134
ctl_error_t ctl_file_read(char **content, const char *filename) {135
FILE *fp = fopen(filename, "r");136
if (fp == NULL) {137
return CTL_ERR_NO_FILE;138
}139
fseek(fp, 0, SEEK_END);140
int len = ftell(fp);141
fseek(fp, 0, SEEK_SET);142
*content = calloc(len + 1, sizeof(char));143
fread(*content, sizeof(char), len, fp);144
(*content)[len] = '\0';145
fclose(fp);146
return CTL_ERR_OK;147
}149
int main(int argc, char **argv) {150
ctl_error_t err;151
if (argc < 2) {152
err = CTL_ERR_NO_ARGS;153
ctl_error_str(err);154
return err;155
}156
char *filename = argv[1];157
char *content = NULL;158
err = ctl_file_read(&content, filename);159
if (err != CTL_ERR_OK) {160
ctl_error_str(err);161
return err;162
}163
char *template = NULL;164
err = ctl_render(&template, content);165
if (err != CTL_ERR_OK) {166
ctl_error_str(err);167
free(content);168
if (template != NULL) {169
free(template);170
}171
return err;172
}173
printf("%s\n", template);174
free(template);175
free(content);176
return CTL_ERR_OK;177
}